Visualizza su TensorFlow.org | Esegui in Google Colab | Visualizza la fonte su GitHub | Scarica taccuino |
Questo tutorial mostra come generare un vocabolario sottoparola da un set di dati, e utilizzarlo per costruire un text.BertTokenizer
dal vocabolario.
Il vantaggio principale di un tokenizer di sottoparole è che interpola tra tokenizzazione basata su parole e basata su caratteri. Le parole comuni ottengono uno spazio nel vocabolario, ma il tokenizzatore può ricorrere a pezzi di parole e singoli caratteri per parole sconosciute.
Panoramica
La tensorflow_text
pacchetto comprende tensorflow implementazioni di molti tokenizers comuni. Ciò include tre tokenizer in stile sottoparola:
-
text.BertTokenizer
- IlBertTokenizer
classe è un'interfaccia di alto livello. Esso comprende l'algoritmo di token di scissione BERT e unWordPieceTokenizer
. Prende frasi come input e restituisce token-ID. -
text.WordpieceTokenizer
- IlWordPieceTokenizer
classe è un'interfaccia di livello inferiore. Esso implementa solo l' algoritmo di WordPiece . Devi standardizzare e dividere il testo in parole prima di chiamarlo. Prende le parole come input e restituisce token-ID. -
text.SentencepieceTokenizer
- IlSentencepieceTokenizer
richiede una configurazione più complessa. Il suo inizializzatore richiede un modello di frase pre-addestrato. Vedere la repository di Google / sentencepiece per le istruzioni su come costruire uno di questi modelli. Si può accettare frasi come input quando creazione di token.
Questo tutorial costruisce un vocabolario di Wordpiece in modo dall'alto verso il basso, partendo da parole esistenti. Questo processo non funziona per il giapponese, il cinese o il coreano poiché queste lingue non hanno unità chiare a più caratteri. Per tokenize nelle lingue conside utilizzando text.SentencepieceTokenizer
, text.UnicodeCharTokenizer
o questo approccio .
Impostare
pip install -q -U tensorflow-text
pip install -q tensorflow_datasets
import collections
import os
import pathlib
import re
import string
import sys
import tempfile
import time
import numpy as np
import matplotlib.pyplot as plt
import tensorflow_datasets as tfds
import tensorflow_text as text
import tensorflow as tf
tf.get_logger().setLevel('ERROR')
pwd = pathlib.Path.cwd()
Scarica il set di dati
Fetch la traduzione dataset portoghese / inglese da TFDS :
examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']
Questo set di dati produce coppie di frasi portoghese/inglese:
for pt, en in train_examples.take(1):
print("Portuguese: ", pt.numpy().decode('utf-8'))
print("English: ", en.numpy().decode('utf-8'))
Portuguese: e quando melhoramos a procura , tiramos a única vantagem da impressão , que é a serendipidade . English: and when you improve searchability , you actually take away the one advantage of print , which is serendipity .
Nota alcune cose sulle frasi di esempio sopra:
- Sono minuscole.
- Ci sono spazi intorno alla punteggiatura.
- Non è chiaro se o quale normalizzazione unicode venga utilizzata.
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)
Genera il vocabolario
Questa sezione genera un vocabolario di parole da un set di dati. Se si dispone già di un file di vocabolario e desidera solo per vedere come costruire un text.BertTokenizer
o text.Wordpiece
tokenizer con esso poi si può passare direttamente alla Costruire il tokenizzatore sezione.
Il codice vocabolario generazione è incluso nel tensorflow_text
pacchetto pip. Non è importato per impostazione predefinita, è necessario importarlo manualmente:
from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab
La bert_vocab.bert_vocab_from_dataset
funzione genera il vocabolario.
Ci sono molti argomenti che puoi impostare per regolare il suo comportamento. Per questo tutorial, utilizzerai principalmente le impostazioni predefinite. Se volete saperne di più sulle opzioni, prima leggere l'algoritmo , e poi dare un'occhiata al codice .
Questo richiede circa 2 minuti.
bert_tokenizer_params=dict(lower_case=True)
reserved_tokens=["[PAD]", "[UNK]", "[START]", "[END]"]
bert_vocab_args = dict(
# The target vocabulary size
vocab_size = 8000,
# Reserved tokens that must be included in the vocabulary
reserved_tokens=reserved_tokens,
# Arguments for `text.BertTokenizer`
bert_tokenizer_params=bert_tokenizer_params,
# Arguments for `wordpiece_vocab.wordpiece_tokenizer_learner_lib.learn`
learn_params={},
)
%%time
pt_vocab = bert_vocab.bert_vocab_from_dataset(
train_pt.batch(1000).prefetch(2),
**bert_vocab_args
)
CPU times: user 1min 30s, sys: 2.21 s, total: 1min 32s Wall time: 1min 28s
Ecco alcune sezioni del vocabolario risultante.
print(pt_vocab[:10])
print(pt_vocab[100:110])
print(pt_vocab[1000:1010])
print(pt_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"] ['no', 'por', 'mais', 'na', 'eu', 'esta', 'muito', 'isso', 'isto', 'sao'] ['90', 'desse', 'efeito', 'malaria', 'normalmente', 'palestra', 'recentemente', '##nca', 'bons', 'chave'] ['##–', '##—', '##‘', '##’', '##“', '##”', '##⁄', '##€', '##♪', '##♫']
Scrivi un file di vocabolario:
def write_vocab_file(filepath, vocab):
with open(filepath, 'w') as f:
for token in vocab:
print(token, file=f)
write_vocab_file('pt_vocab.txt', pt_vocab)
Usa quella funzione per generare un vocabolario dai dati in inglese:
%%time
en_vocab = bert_vocab.bert_vocab_from_dataset(
train_en.batch(1000).prefetch(2),
**bert_vocab_args
)
CPU times: user 1min 3s, sys: 2.21 s, total: 1min 6s Wall time: 1min 2s
print(en_vocab[:10])
print(en_vocab[100:110])
print(en_vocab[1000:1010])
print(en_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"] ['as', 'all', 'at', 'one', 'people', 're', 'like', 'if', 'our', 'from'] ['choose', 'consider', 'extraordinary', 'focus', 'generation', 'killed', 'patterns', 'putting', 'scientific', 'wait'] ['##_', '##`', '##ย', '##ร', '##อ', '##–', '##—', '##’', '##♪', '##♫']
Ecco i due file di vocabolario:
write_vocab_file('en_vocab.txt', en_vocab)
ls *.txt
en_vocab.txt pt_vocab.txt
Costruisci il tokenizzatore
Il text.BertTokenizer
può essere inizializzato passando il percorso del file di vocabolario come primo argomento (si veda la sezione tf.lookup per altre opzioni):
pt_tokenizer = text.BertTokenizer('pt_vocab.txt', **bert_tokenizer_params)
en_tokenizer = text.BertTokenizer('en_vocab.txt', **bert_tokenizer_params)
Ora puoi usarlo per codificare del testo. Prendi una serie di 3 esempi dai dati inglesi:
for pt_examples, en_examples in train_examples.batch(3).take(1):
for ex in en_examples:
print(ex.numpy())
b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .' b'but what if it were active ?' b"but they did n't test for curiosity ."
Eseguire attraverso il BertTokenizer.tokenize
metodo. Inizialmente, questo restituisce un tf.RaggedTensor
ad assi (batch, word, word-piece)
:
# Tokenize the examples -> (batch, word, word-piece)
token_batch = en_tokenizer.tokenize(en_examples)
# Merge the word and word-piece axes -> (batch, tokens)
token_batch = token_batch.merge_dims(-2,-1)
for ex in token_batch.to_list():
print(ex)
[72, 117, 79, 1259, 1491, 2362, 13, 79, 150, 184, 311, 71, 103, 2308, 74, 2679, 13, 148, 80, 55, 4840, 1434, 2423, 540, 15] [87, 90, 107, 76, 129, 1852, 30] [87, 83, 149, 50, 9, 56, 664, 85, 2512, 15]
Se si sostituiscono gli ID token con le loro rappresentazioni di testo (utilizzando tf.gather
) si può vedere che nel primo esempio le parole "searchability"
e "serendipity"
sono state decomposto in "search ##ability"
e "s ##ere ##nd ##ip ##ity"
:
# Lookup each token id in the vocabulary.
txt_tokens = tf.gather(en_vocab, token_batch)
# Join with spaces.
tf.strings.reduce_join(txt_tokens, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy= array([b'and when you improve search ##ability , you actually take away the one advantage of print , which is s ##ere ##nd ##ip ##ity .', b'but what if it were active ?', b"but they did n ' t test for curiosity ."], dtype=object)>
Per riassemblare parole dai token estratti, utilizzare il BertTokenizer.detokenize
metodo:
words = en_tokenizer.detokenize(token_batch)
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy= array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .', b'but what if it were active ?', b"but they did n ' t test for curiosity ."], dtype=object)>
Personalizzazione ed esportazione
Questa esercitazione si basa il tokenizzatore testo e detokenizer utilizzato dal trasformatore tutorial. Questa sezione aggiunge metodi e fasi di lavorazione per semplificare che un'esercitazione, ed esporta il tokenizers utilizzando tf.saved_model
in modo che possano essere importati dagli altri tutorial.
Tokenizzazione personalizzata
I tutorial valle entrambi si aspettano che il testo in formato token di includere [START]
e [END]
gettoni.
Il reserved_tokens
spazio riserva all'inizio del vocabolario, in modo [START]
e [END]
hanno gli stessi indici per entrambe le lingue:
START = tf.argmax(tf.constant(reserved_tokens) == "[START]")
END = tf.argmax(tf.constant(reserved_tokens) == "[END]")
def add_start_end(ragged):
count = ragged.bounding_shape()[0]
starts = tf.fill([count,1], START)
ends = tf.fill([count,1], END)
return tf.concat([starts, ragged, ends], axis=1)
words = en_tokenizer.detokenize(add_start_end(token_batch))
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy= array([b'[START] and when you improve searchability , you actually take away the one advantage of print , which is serendipity . [END]', b'[START] but what if it were active ? [END]', b"[START] but they did n ' t test for curiosity . [END]"], dtype=object)>
Detokenizzazione personalizzata
Prima di esportare i tokenizer ci sono un paio di cose che puoi pulire per i tutorial downstream:
- Vogliono di generare output di testo pulito, in modo da eliminare i token riservati come
[START]
,[END]
e[PAD]
. - Sono interessati a stringhe complete, in modo da applicare una stringa unirsi a lungo le
words
asse del risultato.
def cleanup_text(reserved_tokens, token_txt):
# Drop the reserved tokens, except for "[UNK]".
bad_tokens = [re.escape(tok) for tok in reserved_tokens if tok != "[UNK]"]
bad_token_re = "|".join(bad_tokens)
bad_cells = tf.strings.regex_full_match(token_txt, bad_token_re)
result = tf.ragged.boolean_mask(token_txt, ~bad_cells)
# Join them into strings.
result = tf.strings.reduce_join(result, separator=' ', axis=-1)
return result
en_examples.numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .', b'but what if it were active ?', b"but they did n't test for curiosity ."], dtype=object)
token_batch = en_tokenizer.tokenize(en_examples).merge_dims(-2,-1)
words = en_tokenizer.detokenize(token_batch)
words
<tf.RaggedTensor [[b'and', b'when', b'you', b'improve', b'searchability', b',', b'you', b'actually', b'take', b'away', b'the', b'one', b'advantage', b'of', b'print', b',', b'which', b'is', b'serendipity', b'.'], [b'but', b'what', b'if', b'it', b'were', b'active', b'?'], [b'but', b'they', b'did', b'n', b"'", b't', b'test', b'for', b'curiosity', b'.']]>
cleanup_text(reserved_tokens, words).numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .', b'but what if it were active ?', b"but they did n ' t test for curiosity ."], dtype=object)
Esportare
Il seguente blocco di codice crea un CustomTokenizer
classe per contenere i text.BertTokenizer
casi, la logica personalizzata, e le @tf.function
involucri richieste per l'esportazione.
class CustomTokenizer(tf.Module):
def __init__(self, reserved_tokens, vocab_path):
self.tokenizer = text.BertTokenizer(vocab_path, lower_case=True)
self._reserved_tokens = reserved_tokens
self._vocab_path = tf.saved_model.Asset(vocab_path)
vocab = pathlib.Path(vocab_path).read_text().splitlines()
self.vocab = tf.Variable(vocab)
## Create the signatures for export:
# Include a tokenize signature for a batch of strings.
self.tokenize.get_concrete_function(
tf.TensorSpec(shape=[None], dtype=tf.string))
# Include `detokenize` and `lookup` signatures for:
# * `Tensors` with shapes [tokens] and [batch, tokens]
# * `RaggedTensors` with shape [batch, tokens]
self.detokenize.get_concrete_function(
tf.TensorSpec(shape=[None, None], dtype=tf.int64))
self.detokenize.get_concrete_function(
tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))
self.lookup.get_concrete_function(
tf.TensorSpec(shape=[None, None], dtype=tf.int64))
self.lookup.get_concrete_function(
tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))
# These `get_*` methods take no arguments
self.get_vocab_size.get_concrete_function()
self.get_vocab_path.get_concrete_function()
self.get_reserved_tokens.get_concrete_function()
@tf.function
def tokenize(self, strings):
enc = self.tokenizer.tokenize(strings)
# Merge the `word` and `word-piece` axes.
enc = enc.merge_dims(-2,-1)
enc = add_start_end(enc)
return enc
@tf.function
def detokenize(self, tokenized):
words = self.tokenizer.detokenize(tokenized)
return cleanup_text(self._reserved_tokens, words)
@tf.function
def lookup(self, token_ids):
return tf.gather(self.vocab, token_ids)
@tf.function
def get_vocab_size(self):
return tf.shape(self.vocab)[0]
@tf.function
def get_vocab_path(self):
return self._vocab_path
@tf.function
def get_reserved_tokens(self):
return tf.constant(self._reserved_tokens)
Costruire un CustomTokenizer
per ogni lingua:
tokenizers = tf.Module()
tokenizers.pt = CustomTokenizer(reserved_tokens, 'pt_vocab.txt')
tokenizers.en = CustomTokenizer(reserved_tokens, 'en_vocab.txt')
Esportare le tokenizers come saved_model
:
model_name = 'ted_hrlr_translate_pt_en_converter'
tf.saved_model.save(tokenizers, model_name)
2021-11-02 15:20:31.762976: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
Ricaricare il saved_model
e testare i metodi:
reloaded_tokenizers = tf.saved_model.load(model_name)
reloaded_tokenizers.en.get_vocab_size().numpy()
7010
tokens = reloaded_tokenizers.en.tokenize(['Hello TensorFlow!'])
tokens.numpy()
array([[ 2, 4006, 2358, 687, 1192, 2365, 4, 3]])
text_tokens = reloaded_tokenizers.en.lookup(tokens)
text_tokens
<tf.RaggedTensor [[b'[START]', b'hello', b'tens', b'##or', b'##f', b'##low', b'!', b'[END]']]>
round_trip = reloaded_tokenizers.en.detokenize(tokens)
print(round_trip.numpy()[0].decode('utf-8'))
hello tensorflow !
Archiviare per le esercitazioni di traduzione :
zip -r {model_name}.zip {model_name}
adding: ted_hrlr_translate_pt_en_converter/ (stored 0%) adding: ted_hrlr_translate_pt_en_converter/saved_model.pb (deflated 91%) adding: ted_hrlr_translate_pt_en_converter/variables/ (stored 0%) adding: ted_hrlr_translate_pt_en_converter/variables/variables.data-00000-of-00001 (deflated 51%) adding: ted_hrlr_translate_pt_en_converter/variables/variables.index (deflated 33%) adding: ted_hrlr_translate_pt_en_converter/assets/ (stored 0%) adding: ted_hrlr_translate_pt_en_converter/assets/pt_vocab.txt (deflated 57%) adding: ted_hrlr_translate_pt_en_converter/assets/en_vocab.txt (deflated 54%)
du -h *.zip
184K ted_hrlr_translate_pt_en_converter.zip
Opzionale: l'algoritmo
Vale la pena notare qui che ci sono due versioni dell'algoritmo WordPiece: Bottom-up e top-down. In entrambi i casi l'obiettivo è lo stesso: "Dato un corpus di addestramento e un numero di token desiderati D, il problema di ottimizzazione è selezionare D wordpiece in modo tale che il corpus risultante sia minimo nel numero di wordpiece quando segmentato secondo il modello di wordpiece scelto. "
L'originale algoritmo WordPiece bottom-up , si riferiscono al codifica byte-pair . Come BPE, inizia con l'alfabeto e combina in modo iterativo bigrammi comuni per formare frammenti di parole e parole.
Generatore di vocabolario di tensorflow testo segue l'implementazione top-down dal BERT . Iniziando con le parole e scomponendole in componenti più piccoli finché non raggiungono la soglia di frequenza, o non possono essere ulteriormente scomposte. La sezione successiva lo descrive in dettaglio. Per il giapponese, il cinese e il coreano questo approccio dall'alto verso il basso non funziona poiché non ci sono unità di parole esplicite con cui iniziare. Per coloro che è necessario un approccio diverso .
La scelta del vocabolario
L'algoritmo di generazione WordPiece top-down prende in un insieme di parole, (valore) coppie e una soglia T
, e ritorna un vocabolario V
.
L'algoritmo è iterativo. È gestito per k
iterazioni, dove tipicamente k = 4
, ma solo i primi due sono molto importanti. Il terzo e il quarto (e oltre) sono semplicemente identici al secondo. Nota che ogni passo della ricerca binaria viene eseguito l'algoritmo da zero per k
iterazioni.
Le iterazioni descritte di seguito:
Prima iterazione
- Iterare su ogni parola e coppia conteggio nel ingresso, indicato come
(w, c)
. - Per ogni parola
w
, generare ogni sottostringa, indicato comes
. Ad esempio, per la parolahuman
, abbiamo generare{h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n}
. - Mantenere una mappa hash stringa-to-count, e incrementare il conteggio di ogni
s
dalc
. Ad esempio, se abbiamo(human, 113)
e(humas, 3)
nel nostro input, il conte dis = huma
sarà113+3=116
. - Una volta che abbiamo raccolto i conti di ogni stringa, iterate nel corso degli
(s, c)
coppie di partire con la più lungas
prima. - Tenere tutti
s
che ha unc > T
. Ad esempio, seT = 100
e abbiamo(pers, 231); (dogs, 259); (##rint; 76)
, allora avremmo tenerepers
edogs
. - Quando un
s
è tenuto, sottrarre fuori il suo numero da tutte le sue prefissi. Questo è il motivo per ordinamento tutti is
per lunghezza nel passo 4. Questa è una parte critica dell'algoritmo, perché altrimenti parole sarebbero doppio contati. Per esempio, diciamo che abbiamo mantenutohuman
e si arriva a(huma, 116)
. Sappiamo che113
di quei116
provenivano dahuman
, e3
venutihumas
. Tuttavia, ora chehuman
è nel nostro vocabolario, sappiamo che non sarà mai segmentohuman
inhuma ##n
. Quindi, una voltahuman
è stato mantenuto, quindihuma
ha solo un conteggio effettivo del3
.
Questo algoritmo genera una serie di pezzi di parole s
(molti dei quali saranno parole intere w
), che potremmo usare come nostro vocabolario WordPiece.
Tuttavia, c'è un problema: questo algoritmo genererà in modo eccessivo i pezzi di parole. Il motivo è che sottraiamo solo i conteggi dei token del prefisso. Pertanto, se osserviamo la parola human
, noi sottrarre fuori il conteggio per h, hu, hu, huma
, ma non per ##u, ##um, ##uma, ##uman
e così via. Così potremmo generare sia human
e ##uman
come pezzi di parola, anche se ##uman
non sarà mai applicata.
Allora perché non sottrarre fuori i conteggi per ogni stringa, non solo ogni prefisso? Perché così potremmo finire per sottrarre dai conteggi più volte. Diciamo che siamo l'elaborazione s
di lunghezza di 5 e ci tengono entrambi (##denia, 129)
e (##eniab, 137)
, dove 65
di questi conteggi è venuto dalla parola undeniable
. Se sottraiamo fuori da ogni stringa, vorremmo sottrarre 65
dalla stringa ##enia
due volte, anche se dovremmo sottrarre solo una volta. Tuttavia, se sottraiamo solo dai prefissi, verrà sottratto correttamente solo una volta.
Seconda (e terza...) iterazione
Per risolvere il problema di sovragenerazione sopra menzionato, eseguiamo più iterazioni dell'algoritmo.
Iterazioni successive sono identiche alla prima, con una distinzione importante: Nel passo 2, invece di considerare ogni stringa, si applica l'algoritmo WordPiece tokenization utilizzando il vocabolario iterazione precedente e consideriamo solo sottostringhe che si aprono su un punto di divisione.
Per esempio, diciamo che stiamo eseguito il punto 2 della algoritmo e incontrare la parola undeniable
. Nella prima iterazione, potremmo considerare ogni stringa, ad esempio, {u, un, und, ..., undeniable, ##n, ##nd, ..., ##ndeniable, ...}
.
Ora, per la seconda iterazione, considereremo solo un sottoinsieme di questi. Diciamo che dopo la prima iterazione, i pezzi di parole rilevanti sono:
un, ##deni, ##able, ##ndeni, ##iable
Il segmento volontà algoritmo WordPiece questo in un ##deni ##able
(vedi la sezione Applicazione WordPiece per maggiori informazioni). In questo caso, prenderemo in considerazione solo stringhe che partono da un punto di segmentazione. Ci sarà ancora in considerazione ogni possibile posizione finale. Così durante la seconda iterazione, l'insieme di s
per undeniable
è:
{u, un, und, unden, undeni, undenia, undeniab, undeniabl, undeniable, ##d, ##de, ##den, ##deni, ##denia, ##deniab, ##deniabl , ##deniable, ##a, ##ab, ##abl, ##able}
L'algoritmo è per il resto identico. In questo esempio, nella prima iterazione, l'algoritmo produce il suprious gettoni ##ndeni
e ##iable
. Ora, questi token non vengono mai considerati, quindi non verranno generati dalla seconda iterazione. Eseguiamo diverse iterazioni solo per assicurarci che i risultati convergano (sebbene non vi sia alcuna garanzia di convergenza letterale).
Applicazione di WordPiece
Una volta che un vocabolario WordPiece è stato generato, dobbiamo essere in grado di applicarlo a nuovi dati. L'algoritmo è una semplice applicazione avida di corrispondenza più lunga.
Si consideri ad esempio la segmentazione della parola undeniable
.
In primo luogo abbiamo lookup undeniable
nel nostro dizionario WordPiece, e se è presente, abbiamo finito. In caso contrario, si decrementa il punto finale di un carattere, e ripetere, ad esempio, undeniabl
.
Alla fine, troveremo un sottotoken nel nostro vocabolario, o scenderemo a un sottotoken di un singolo carattere. (In generale, si assume che ogni personaggio è nel nostro vocabolario, anche se questo potrebbe non essere il caso per i caratteri Unicode rare. Se incontriamo un personaggio rara Unicode che non è nel vocabolario abbiamo semplicemente mappare l'intera parola a <unk>
).
In questo caso, troviamo un
nel nostro vocabolario. Quindi questo è il nostro primo pezzo di parola. Poi saltiamo alla fine di un
e ripetere il trattamento, ad esempio, cercare di trovare ##deniable
, poi ##deniabl
, ecc Questo si ripete finché non avremo segmentato l'intera parola.
Intuizione
Intuitivamente, la tokenizzazione di WordPiece sta cercando di soddisfare due diversi obiettivi:
Tokenize i dati nel minor numero di pezzi possibile. È importante tenere presente che l'algoritmo WordPiece non "vuole" dividere le parole. In caso contrario, sarebbe solo dividere ogni parola nel suo personaggi, ad esempio,
human -> {h, ##u, ##m, ##a, #n}
. Questa è una cosa fondamentale che rende WordPiece diverso da splitter morfologiche, che si dividerà morfemi linguistiche, anche per le parole comuni (ad esempio,unwanted -> {un, want, ed}
).Quando una parola deve essere divisa in pezzi, suddividila in pezzi che hanno un numero massimo di conteggi nei dati di addestramento. Ad esempio, il motivo per cui la parola
undeniable
sarebbe diviso in{un, ##deni, ##able}
piuttosto che alternative come{unde, ##niab, ##le}
è che i conteggi perun
e##able
di particolare sarà molto alto, poiché si tratta di prefissi e suffissi comuni. Anche se il conteggio per##le
deve essere superiore a##able
, le basse conti diunde
e##niab
faranno di questo un meno tokenizzazione "desiderabile" per l'algoritmo.
Opzionale: tf.lookup
Se è necessario accedere a, o un maggiore controllo sul vocabolario vale la pena notare che si può costruire la tabella di ricerca stessi e passare che a BertTokenizer
.
Quando si passa una stringa, BertTokenizer
fa il seguente:
pt_lookup = tf.lookup.StaticVocabularyTable(
num_oov_buckets=1,
initializer=tf.lookup.TextFileInitializer(
filename='pt_vocab.txt',
key_dtype=tf.string,
key_index = tf.lookup.TextFileIndex.WHOLE_LINE,
value_dtype = tf.int64,
value_index=tf.lookup.TextFileIndex.LINE_NUMBER))
pt_tokenizer = text.BertTokenizer(pt_lookup)
Ora hai accesso diretto alla tabella di ricerca utilizzata nel tokenizer.
pt_lookup.lookup(tf.constant(['é', 'um', 'uma', 'para', 'não']))
<tf.Tensor: shape=(5,), dtype=int64, numpy=array([7765, 85, 86, 87, 7765])>
Non è necessario utilizzare un file di vocabolario, tf.lookup
ha altre opzioni di inizializzazione. Se avete il vocabolario in memoria è possibile utilizzare lookup.KeyValueTensorInitializer
:
pt_lookup = tf.lookup.StaticVocabularyTable(
num_oov_buckets=1,
initializer=tf.lookup.KeyValueTensorInitializer(
keys=pt_vocab,
values=tf.range(len(pt_vocab), dtype=tf.int64)))
pt_tokenizer = text.BertTokenizer(pt_lookup)