Ver en TensorFlow.org | Ejecutar en Google Colab | Ver fuente en GitHub | Descargar cuaderno |
Este tutorial muestra cómo generar un vocabulario palabra parcial de un conjunto de datos, y lo utilizan para construir un text.BertTokenizer
del vocabulario.
La principal ventaja de un tokenizador de subpalabras es que interpola entre la tokenización basada en palabras y basada en caracteres. Las palabras comunes obtienen un espacio en el vocabulario, pero el tokenizador puede recurrir a partes de palabras y caracteres individuales para palabras desconocidas.
Descripción general
El tensorflow_text
paquete incluye implementaciones de muchos TensorFlow tokenizers comunes. Esto incluye tres tokenizadores de estilo de subpalabras:
-
text.BertTokenizer
- ElBertTokenizer
clase es una interfaz de nivel superior. Incluye algoritmo de división de contadores a BERT y unaWordPieceTokenizer
. Se necesita frases como entrada y devuelve token-IDs. -
text.WordpieceTokenizer
- ElWordPieceTokenizer
clase es una interfaz de nivel inferior. Sólo se implementa el algoritmo de WordPiece . Debe estandarizar y dividir el texto en palabras antes de llamarlo. Se necesita palabras como entrada y devuelve token-IDs. -
text.SentencepieceTokenizer
- ElSentencepieceTokenizer
requiere una configuración más compleja. Su inicializador requiere un modelo de pieza de oración previamente entrenado. Ver el repositorio de Google / sentencepiece para obtener instrucciones sobre cómo construir uno de estos modelos. Se puede aceptar frases como entrada cuando tokenizar.
Este tutorial crea un vocabulario de Wordpiece de arriba hacia abajo, a partir de palabras existentes. Este proceso no funciona para japonés, chino o coreano, ya que estos idiomas no tienen unidades claras de varios caracteres. Para tokenize idiomas conside utilizando text.SentencepieceTokenizer
, text.UnicodeCharTokenizer
o este enfoque .
Configuración
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()
Descarga el conjunto de datos
Buscar el conjunto de datos en portugués / Inglés de los TFD :
examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']
Este conjunto de datos produce pares de oraciones en portugués / inglés:
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 .
Tenga en cuenta algunas cosas sobre las oraciones de ejemplo anteriores:
- Son minúsculas.
- Hay espacios alrededor de la puntuación.
- No está claro si se está utilizando o qué normalización Unicode.
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)
Genera el vocabulario
Esta sección genera un vocabulario de palabras a partir de un conjunto de datos. Si ya tiene un archivo de vocabulario y sólo quiere ver cómo construir un text.BertTokenizer
o text.Wordpiece
tokenizer con él, entonces puede pasar directamente a la construcción del tokenizer sección.
El código de generación vocabulario está incluido en el tensorflow_text
paquete PIP. No se importa de forma predeterminada, debe importarlo manualmente:
from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab
El bert_vocab.bert_vocab_from_dataset
función generará el vocabulario.
Hay muchos argumentos que puede establecer para ajustar su comportamiento. Para este tutorial, utilizará principalmente los valores predeterminados. Si desea obtener más información sobre las opciones, leer primero sobre el algoritmo , y luego echar un vistazo al código .
Esto tarda unos 2 minutos.
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
A continuación se muestran algunos fragmentos del vocabulario resultante.
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'] ['##–', '##—', '##‘', '##’', '##“', '##”', '##⁄', '##€', '##♪', '##♫']
Escribe un archivo de vocabulario:
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)
Use esa función para generar un vocabulario a partir de los datos en inglés:
%%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'] ['##_', '##`', '##ย', '##ร', '##อ', '##–', '##—', '##’', '##♪', '##♫']
Aquí están los dos archivos de vocabulario:
write_vocab_file('en_vocab.txt', en_vocab)
ls *.txt
en_vocab.txt pt_vocab.txt
Construye el tokenizador
El text.BertTokenizer
puede ser inicializado por el que pasa la ruta del archivo de vocabulario como primer argumento (véase la sección sobre tf.lookup para otras opciones):
pt_tokenizer = text.BertTokenizer('pt_vocab.txt', **bert_tokenizer_params)
en_tokenizer = text.BertTokenizer('en_vocab.txt', **bert_tokenizer_params)
Ahora puedes usarlo para codificar texto. Tome un lote de 3 ejemplos de los datos en inglés:
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 ."
Ejecutar a través de la BertTokenizer.tokenize
método. Inicialmente, este devuelve un tf.RaggedTensor
con ejes (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]
Si sustituye los ID de testigo con sus representaciones de texto (usando tf.gather
) se puede ver que en el primer ejemplo de las palabras "searchability"
y "serendipity"
se han descompuesto en "search ##ability"
y "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)>
Para volver a montar palabras de los tokens extraídos, utilice el BertTokenizer.detokenize
método:
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)>
Personalización y exportación
Este tutorial se basa el texto y tokenizer detokenizer utilizado por el transformador tutorial. En esta sección se añade métodos y pasos de procesamiento para simplificar ese tutorial, y las exportaciones de los tokenizers usando tf.saved_model
para que puedan ser importadas por los otros tutoriales.
Tokenización personalizada
Los tutoriales aguas abajo ambos esperan que el texto tokenized para incluir [START]
y [END]
fichas.
El reserved_tokens
espacio de reserva al comienzo del vocabulario, por lo [START]
y [END]
tienen los mismos índices para los dos idiomas:
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)>
Destokenización personalizada
Antes de exportar los tokenizadores, hay un par de cosas que puede limpiar para los tutoriales posteriores:
- Ellos quieren generar la salida de texto limpio, así como dejar caer fichas reservados
[START]
,[END]
y[PAD]
. - Están interesados en las cadenas completas, por lo que se aplica a lo largo de una cadena de unirse a las
words
eje del resultado.
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)
Exportar
El siguiente bloque de código crea un CustomTokenizer
clase para contener los text.BertTokenizer
casos, la lógica personalizada, y los @tf.function
envoltorios requeridos para la exportación.
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)
Construir un CustomTokenizer
para cada idioma:
tokenizers = tf.Module()
tokenizers.pt = CustomTokenizer(reserved_tokens, 'pt_vocab.txt')
tokenizers.en = CustomTokenizer(reserved_tokens, 'en_vocab.txt')
Exportar los tokenizers como un 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.
Actualizar el saved_model
y probar los métodos:
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 !
Archivarlo para los tutoriales de traducción :
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
Opcional: el algoritmo
Vale la pena señalar aquí que hay dos versiones del algoritmo de WordPiece: de abajo hacia arriba y de arriba hacia abajo. En ambos casos, el objetivo es el mismo: "Dado un corpus de entrenamiento y un número de tokens D deseados, el problema de optimización es seleccionar D wordpieces de manera que el corpus resultante sea mínimo en el número de wordpieces cuando se segmenta según el modelo de wordpiece elegido. "
El original algoritmo WordPiece de abajo hacia arriba , se basa en el byte de par de codificación . Al igual que BPE, comienza con el alfabeto y combina iterativamente bigramas comunes para formar piezas de palabras y palabras.
Generador de vocabulario de TensorFlow texto sigue la aplicación de arriba hacia abajo de BERT . Comenzar con palabras y dividirlas en componentes más pequeños hasta que alcancen el umbral de frecuencia o no se puedan dividir más. La siguiente sección describe esto en detalle. Para el japonés, el chino y el coreano, este enfoque de arriba hacia abajo no funciona, ya que no hay unidades de palabras explícitas con las que empezar. Para aquellos que se necesita un enfoque diferente .
Elegir el vocabulario
El algoritmo de generación de WordPiece de arriba hacia abajo realiza en un conjunto de (Word, recuento) pares y un umbral T
, y devuelve un vocabulario V
.
El algoritmo es iterativo. Está dirigido por k
iteraciones, donde normalmente k = 4
, pero sólo los dos primeros son realmente importantes. El tercero y el cuarto (y más allá) son simplemente idénticos al segundo. Tenga en cuenta que cada paso de la búsqueda binaria se ejecuta el algoritmo de cero para k
iteraciones.
Las iteraciones que se describen a continuación:
Primera iteración
- Iterar sobre cada palabra y par recuento en la entrada, indicada como
(w, c)
. - Para cada palabra
w
, generar cada subcadena, que se denota comos
. Por ejemplo, para la palabrahuman
, que generan{h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n}
. - Mantener un mapa hash-subcadena a recuento, e incrementar el recuento de cada uno
s
dec
. Por ejemplo, si tenemos(human, 113)
y(humas, 3)
en nuestra entrada la cuenta des = huma
será113+3=116
. - Una vez que hemos recogido los cargos de cada subcadena, iterar sobre las
(s, c)
pares empezando por el más largos
primero. - Guarde todos los
s
que tiene unc > T
. Por ejemplo, siT = 100
y tenemos(pers, 231); (dogs, 259); (##rint; 76)
, entonces podríamos mantenerpers
ydogs
. - Cuando un
s
se mantiene, restar de su recuento de todos sus prefijos. Esta es la razón para la clasificación de todas lass
por longitud en el paso 4. Esta es una parte crítica del algoritmo, porque de lo contrario palabras serían doble contados. Por ejemplo, digamos que hemos mantenidohuman
y nos dan a(huma, 116)
. Sabemos que113
de los116
procedían dehuman
, y3
venimos dehumas
. Sin embargo, ahora quehuman
está en nuestro vocabulario, sabemos que nunca lo hará segmentohuman
enhuma ##n
. Así que una vezhuman
se ha mantenido, a continuación,huma
sólo se tiene un recuento efectivo de3
.
Este algoritmo genera un conjunto de piezas de palabras s
(muchas de las cuales serán las palabras enteras w
), que podríamos utilizar como nuestro vocabulario WordPiece.
Sin embargo, hay un problema: este algoritmo sobregenerará en exceso las palabras. La razón es que solo restamos los conteos de tokens de prefijo. Por lo tanto, si mantenemos la palabra human
, vamos a restar de la cuenta para h, hu, hu, huma
, pero no para ##u, ##um, ##uma, ##uman
y así sucesivamente. Así que podríamos generar tanto human
y ##uman
como piezas de palabras, a pesar de que ##uman
no se aplicará nunca.
¿Por qué no restar de los recuentos para cada subcadena, no sólo cada prefijo? Porque entonces podríamos terminar restando los conteos varias veces. Digamos que estamos procesamiento s
de longitud 5 y nos mantienen tanto (##denia, 129)
y (##eniab, 137)
, donde 65
de los recuentos de vino de la palabra undeniable
. Si restamos fuera de cada subcadena, nos resta 65
de la subcadena ##enia
dos veces, a pesar de que sólo debe restar una vez. Sin embargo, si solo restamos de los prefijos, solo se restará correctamente una vez.
Segunda (y tercera ...) iteración
Para resolver el problema de sobregeneración mencionado anteriormente, realizamos múltiples iteraciones del algoritmo.
Iteraciones subsiguientes son idénticas a la primera, con una diferencia importante: en el paso 2, en lugar de considerar cada subcadena, aplicamos el algoritmo WordPiece tokenización utilizando el vocabulario de la iteración anterior, y sólo consideramos subseries que comienzan en un punto de división.
Por ejemplo, digamos que estamos realizando el paso 2 del algoritmo y encontrar la palabra undeniable
. En la primera iteración, que podríamos considerar cada subcadena, por ejemplo, {u, un, und, ..., undeniable, ##n, ##nd, ..., ##ndeniable, ...}
.
Ahora, para la segunda iteración, solo consideraremos un subconjunto de estos. Digamos que después de la primera iteración, las piezas de palabras relevantes son:
un, ##deni, ##able, ##ndeni, ##iable
El segmento voluntad algoritmo WordPiece esto en un ##deni ##able
(véase la sección WordPiece aplicación para obtener más información). En este caso, sólo tendremos en cuenta subseries que se inician en un punto de segmentación. Todavía vamos a considerar cada posición final posible. Así que durante la segunda iteración, el conjunto de s
para undeniable
es:
{u, un, und, unden, undeni, undenia, undeniab, undeniabl, undeniable, ##d, ##de, ##den, ##deni, ##denia, ##deniab, ##deniabl , ##deniable, ##a, ##ab, ##abl, ##able}
Por lo demás, el algoritmo es idéntico. En este ejemplo, en la primera iteración, el algoritmo produce la suprious tokens ##ndeni
y ##iable
. Ahora, estos tokens nunca se consideran, por lo que no serán generados por la segunda iteración. Realizamos varias iteraciones solo para asegurarnos de que los resultados converjan (aunque no hay una garantía de convergencia literal).
Aplicar WordPiece
Una vez que se ha generado un vocabulario de WordPiece, necesitamos poder aplicarlo a nuevos datos. El algoritmo es una simple aplicación codiciosa de la coincidencia más larga.
Por ejemplo, considere la segmentación de la palabra undeniable
.
En primer lugar, las operaciones de búsqueda undeniable
en el diccionario WordPiece, y si está presente, hemos terminado. Si no, disminuir el punto final de un carácter, y repetir, por ejemplo, undeniabl
.
Eventualmente, encontraremos un subtoken en nuestro vocabulario o bajaremos a un subtoken de un solo carácter. (En general, se supone que cada personaje está en nuestro vocabulario, aunque esto podría no ser el caso para los caracteres Unicode raras. Si nos encontramos con un personaje raro Unicode que no está en el vocabulario que sólo establecen la palabra completa a <unk>
).
En este caso, nos encontramos con un
en nuestro vocabulario. Así que esa es nuestra primera pieza de palabras. Luego saltamos a finales de un
y repetir el tratamiento, por ejemplo, tratar de encontrar ##deniable
, a continuación, ##deniabl
, etc. Esto se repite hasta que hemos segmentado la palabra completa.
Intuición
Intuitivamente, la tokenización de WordPiece intenta satisfacer dos objetivos diferentes:
Tokenize los datos en el menor número de piezas como sea posible. Es importante tener en cuenta que el algoritmo de WordPiece no "quiere" dividir palabras. De lo contrario, sería simplemente dividir cada palabra en sus personajes, por ejemplo,
human -> {h, ##u, ##m, ##a, #n}
. Esto es una cosa fundamental que los hace diferentes de divisores WordPiece morfológicas, que se dividirán morfemas lingüísticas, incluso para las palabras comunes (por ejemplo,unwanted -> {un, want, ed}
).Cuando una palabra tenga que dividirse en partes, divídala en partes que tengan un recuento máximo en los datos de entrenamiento. Por ejemplo, la razón por la palabra
undeniable
sería dividido en{un, ##deni, ##able}
en lugar de alternativas como{unde, ##niab, ##le}
es que los recuentos deun
y##able
de particular será muy alto, ya que son prefijos y sufijos comunes. A pesar de que el recuento de##le
debe ser superior a##able
, los recuentos bajos deunde
y##niab
se hacen de este un tokenización menos "deseables" para el algoritmo.
Opcional: tf.lookup
Si necesita acceso a, o mayor control sobre el vocabulario Vale la pena señalar que se puede construir la tabla de búsqueda usted mismo y pasar a que BertTokenizer
.
Cuando se pasa una cadena, BertTokenizer
hace lo siguiente:
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)
Ahora tiene acceso directo a la tabla de búsqueda utilizada en el tokenizador.
pt_lookup.lookup(tf.constant(['é', 'um', 'uma', 'para', 'não']))
<tf.Tensor: shape=(5,), dtype=int64, numpy=array([7765, 85, 86, 87, 7765])>
No es necesario utilizar un archivo de vocabulario, tf.lookup
tiene otras opciones de inicializador. Si usted tiene el vocabulario de memoria que puede utilizar 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)