Tokenización con TF Text

Ver en TensorFlow.org Ejecutar en Google Colab Ver en GitHub Descargar libreta Ver modelos TF Hub

Descripción general

La tokenización es el proceso de dividir una cadena en tokens. Comúnmente, estos tokens son palabras, números y/o signos de puntuación. El tensorflow_text paquete proporciona una serie de tokenizers disponibles para pre-procesamiento de texto requerido por los modelos basados en texto. Al realizar la tokenización en el gráfico de TensorFlow, no tendrá que preocuparse por las diferencias entre los flujos de trabajo de entrenamiento e inferencia y la administración de scripts de preprocesamiento.

Esta guía analiza las muchas opciones de tokenización proporcionadas por TensorFlow Text, cuándo es posible que desee usar una opción sobre otra y cómo se llama a estos tokenizadores desde su modelo.

Configuración

pip install -q tensorflow-text
import requests
import tensorflow as tf
import tensorflow_text as tf_text

API de división

Las principales interfaces son Splitter y SplitterWithOffsets que tienen métodos individuales split y split_with_offsets . El SplitterWithOffsets variante (que se extiende Splitter ) incluye una opción para conseguir desplazamientos de bytes. Esto permite que la persona que llama sepa a partir de qué bytes en la cadena original se creó el token creado.

El Tokenizer y TokenizerWithOffsets son versiones especializadas del Splitter que proporcionan los métodos de conveniencia tokenize y tokenize_with_offsets respectivamente.

En general, para cualquier entrada de N-dimensional, las fichas vueltos están en un N + 1-dimensional RaggedTensor con la dimensión interna más de tokens de mapeo para las cuerdas individuales originales.

class Splitter {
  @abstractmethod
  def split(self, input)
}

class SplitterWithOffsets(Splitter) {
  @abstractmethod
  def split_with_offsets(self, input)
}

También hay un Detokenizer interfaz. Cualquier tokenizador que implemente esta interfaz puede aceptar un tensor irregular de N-dimensional de tokens, y normalmente devuelve un tensor N-1-dimensional o un tensor irregular que tiene los tokens dados ensamblados.

class Detokenizer {
  @abstractmethod
  def detokenize(self, input)
}

Tokenizadores

A continuación se muestra el conjunto de tokenizadores proporcionados por TensorFlow Text. Se supone que las entradas de cadena son UTF-8. Por favor revise la guía de Unicode para convertir cadenas en UTF-8.

Tokenizadores de palabras completas

Estos tokenizadores intentan dividir una cadena por palabras y es la forma más intuitiva de dividir texto.

Tokenizador de espacio en blanco

El text.WhitespaceTokenizer es la tokenizer más básico que se divide en cadenas de caracteres de espacio en blanco de la UCI definido (por ejemplo. Espacio, tabulación, nueva línea). Esto suele ser bueno para construir rápidamente modelos prototipo.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py:206: batch_gather (from tensorflow.python.ops.array_ops) is deprecated and will be removed after 2017-10-25.
Instructions for updating:
`tf.batch_gather` is deprecated, please use `tf.gather` with `batch_dims=-1` instead.
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

Puede notar que una deficiencia de este tokenizador es que la puntuación se incluye con la palabra para formar un token. Para dividir las palabras y puntuacion en fichas separadas, la UnicodeScriptTokenizer se debe utilizar.

UnicodeScriptTokenizer

El UnicodeScriptTokenizer divide cadenas basadas en los límites de secuencia de comandos Unicode. Los códigos de script utilizados corresponden a los valores UScriptCode de los componentes internacionales para Unicode (ICU). Ver: http://icu-project.org/apiref/icu4c/uscript_8h.html

En la práctica, esto es similar a la WhitespaceTokenizer con el ser más aparente diferencia de que éste se parte puntuacion (USCRIPT_COMMON) a partir de textos de idioma (por ejemplo. USCRIPT_LATIN, USCRIPT_CYRILLIC, etc), mientras que también la separación de textos en lenguaje de la otra. Tenga en cuenta que esto también dividirá las palabras de contracción en tokens separados.

tokenizer = tf_text.UnicodeScriptTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b'can', b"'", b't', b'explain', b',', b'but', b'you', b'feel', b'it', b'.']]

tokenizadores de subpalabras

Los tokenizadores de subpalabras se pueden usar con un vocabulario más pequeño y permiten que el modelo tenga información sobre palabras nuevas de las subpalabras que lo crean.

Se discuten brevemente las opciones de palabra parcial tokenización abajo, pero el tutorial palabra parcial Tokenization va más en profundidad y también explica cómo generar los archivos de vocabulario.

Tokenizer de Wordpiece

La tokenización de WordPiece es un esquema de tokenización basado en datos que genera un conjunto de subtokens. Estos tokens secundarios pueden corresponder a morfemas lingüísticos, pero a menudo no es así.

WordpieceTokenizer espera que la entrada ya esté dividida en tokens. Debido a este requisito, que a menudo se desee dividir utilizando el WhitespaceTokenizer o UnicodeScriptTokenizer de antemano.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

Después de que la cadena se divide en fichas, el WordpieceTokenizer se puede utilizar para dividir en subtokens.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_wp_en_vocab.txt?raw=true"
r = requests.get(url)
filepath = "vocab.txt"
open(filepath, 'wb').write(r.content)
52382
subtokenizer = tf_text.UnicodeScriptTokenizer(filepath)
subtokens = tokenizer.tokenize(tokens)
print(subtokens.to_list())
[[[b'What'], [b'you'], [b'know'], [b'you'], [b"can't"], [b'explain,'], [b'but'], [b'you'], [b'feel'], [b'it.']]]

BertTokenizador

El BertTokenizer refleja la implementación original de tokenización del documento BERT. Esto está respaldado por WordpieceTokenizer, pero también realiza tareas adicionales como la normalización y tokenización de palabras primero.

tokenizer = tf_text.BertTokenizer(filepath, token_out_type=tf.string, lower_case=True)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[[b'what'], [b'you'], [b'know'], [b'you'], [b'can'], [b"'"], [b't'], [b'explain'], [b','], [b'but'], [b'you'], [b'feel'], [b'it'], [b'.']]]

SentencepieceTokenizer

El SentencepieceTokenizer es un tokenizador de tokens secundarios que es altamente configurable. Esto está respaldado por la biblioteca Sentencepiece. Al igual que BertTokenizer, puede incluir normalización y división de tokens antes de dividirse en subtokens.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_oss_model.model?raw=true"
sp_model = requests.get(url).content
tokenizer = tf_text.SentencepieceTokenizer(sp_model, out_type=tf.string)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'\xe2\x96\x81What', b'\xe2\x96\x81you', b'\xe2\x96\x81know', b'\xe2\x96\x81you', b'\xe2\x96\x81can', b"'", b't', b'\xe2\x96\x81explain', b',', b'\xe2\x96\x81but', b'\xe2\x96\x81you', b'\xe2\x96\x81feel', b'\xe2\x96\x81it', b'.']]

Otros divisores

UnicodeCharTokenizer

Esto divide una cadena en caracteres UTF-8. Es útil para los idiomas CJK que no tienen espacios entre palabras.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]

La salida son puntos de código Unicode. Esto también puede ser útil para crear ngramas de caracteres, como bigramas. Para volver a convertir en caracteres UTF-8.

characters = tf.strings.unicode_encode(tf.expand_dims(tokens, -1), "UTF-8")
bigrams = tf_text.ngrams(characters, 2, reduction_type=tf_text.Reduction.STRING_JOIN, string_separator='')
print(bigrams.to_list())
[[b'Wh', b'ha', b'at', b't ', b' y', b'yo', b'ou', b'u ', b' k', b'kn', b'no', b'ow', b'w ', b' y', b'yo', b'ou', b'u ', b' c', b'ca', b'an', b"n'", b"'t", b't ', b' e', b'ex', b'xp', b'pl', b'la', b'ai', b'in', b'n,', b', ', b' b', b'bu', b'ut', b't ', b' y', b'yo', b'ou', b'u ', b' f', b'fe', b'ee', b'el', b'l ', b' i', b'it', b't.']]

HubModuleTokenizer

Este es un contenedor de modelos implementados en TF Hub para facilitar las llamadas, ya que TF Hub actualmente no admite tensores irregulares. Tener un modelo que realice la tokenización es particularmente útil para los lenguajes CJK cuando desea dividirlos en palabras, pero no tiene espacios para proporcionar una guía heurística. En este momento, tenemos un modelo de segmentación único para chino.

MODEL_HANDLE = "https://tfhub.dev/google/zh_segmentation/1"
segmenter = tf_text.HubModuleTokenizer(MODEL_HANDLE)
tokens = segmenter.tokenize(["新华社北京"])
print(tokens.to_list())
[[b'\xe6\x96\xb0\xe5\x8d\x8e\xe7\xa4\xbe', b'\xe5\x8c\x97\xe4\xba\xac']]

Puede ser difícil ver los resultados de las cadenas de bytes codificadas en UTF-8. Decodifique los valores de la lista para facilitar la visualización.

def decode_list(x):
  if type(x) is list:
    return list(map(decode_list, x))
  return x.decode("UTF-8")

def decode_utf8_tensor(x):
  return list(map(decode_list, x.to_list()))

print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

SplitMergeTokenizador

El SplitMergeTokenizer y SplitMergeFromLogitsTokenizer tienen un propósito específico de la división una cadena basada en los valores previstos que indican que la cadena debe ser dividida. Esto es útil cuando crea sus propios modelos de segmentación como el ejemplo de segmentación anterior.

Para el SplitMergeTokenizer , un valor de 0 se usa para indicar el inicio de una nueva cadena, y el valor de 1 indica que el carácter es parte de la cadena actual.

strings = ["新华社北京"]
labels = [[0, 1, 1, 0, 1]]
tokenizer = tf_text.SplitMergeTokenizer()
tokens = tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

El SplitMergeFromLogitsTokenizer es similar, pero en cambio acepta pares de valores logit de una red neuronal que predecir si cada personaje debe ser dividida en una nueva cadena o se fusionaron en la actual.

strings = [["新华社北京"]]
labels = [[[5.0, -3.2], [0.2, 12.0], [0.0, 11.0], [2.2, -1.0], [-3.0, 3.0]]]
tokenizer = tf_text.SplitMergeFromLogitsTokenizer()
tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

Regex Splitter

El RegexSplitter es capaz de cadenas de segmentos en puntos de interrupción arbitrarios definidos por una expresión regular proporcionada.

splitter = tf_text.RegexSplitter("\s")
tokens = splitter.split(["What you know you can't explain, but you feel it."], )
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

Compensaciones

Al tokenizar cadenas, a menudo se desea saber en qué parte de la cadena original se originó el token. Por esta razón, cada tokenizer que implementa TokenizerWithOffsets tiene un método tokenize_with_offsets que devolverá los desplazamientos de bytes, junto con las fichas. start_offsets enumera los bytes en la cadena original en la que comienza cada token, y end_offsets enumera los bytes inmediatamente después del punto donde termina cada token. Para reformular, los desplazamientos iniciales son inclusivos y los desplazamientos finales son exclusivos.

tokenizer = tf_text.UnicodeScriptTokenizer()
(tokens, start_offsets, end_offsets) = tokenizer.tokenize_with_offsets(['Everything not saved will be lost.'])
print(tokens.to_list())
print(start_offsets.to_list())
print(end_offsets.to_list())
[[b'Everything', b'not', b'saved', b'will', b'be', b'lost', b'.']]
[[0, 11, 15, 21, 26, 29, 33]]
[[10, 14, 20, 25, 28, 33, 34]]

detokenización

Tokenizers que implementan la Detokenizer proporcionan una detokenize método que trata de combinar las cuerdas. Esto tiene la posibilidad de tener pérdidas, por lo que es posible que la cadena destokenizada no siempre coincida exactamente con la cadena original pre-tokenizada.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
strings = tokenizer.detokenize(tokens)
print(strings.numpy())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]
[b"What you know you can't explain, but you feel it."]

Datos de TF

TF Data es una potente API para crear una canalización de entrada para modelos de entrenamiento. Los tokenizadores funcionan como se esperaba con la API.

docs = tf.data.Dataset.from_tensor_slices([['Never tell me the odds.'], ["It's a trap!"]])
tokenizer = tf_text.WhitespaceTokenizer()
tokenized_docs = docs.map(lambda x: tokenizer.tokenize(x))
iterator = iter(tokenized_docs)
print(next(iterator).to_list())
print(next(iterator).to_list())
[[b'Never', b'tell', b'me', b'the', b'odds.']]
[[b"It's", b'a', b'trap!']]