Ayuda a proteger la Gran Barrera de Coral con TensorFlow en Kaggle Únete Challenge

Tokenización con TF Text

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

Visión general

La tokenización es el proceso de dividir una cadena en tokens. Por lo general, estos tokens son palabras, números y / o 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 los scripts de preprocesamiento.

En esta guía, se analizan las muchas opciones de tokenización que ofrece TensorFlow Text, cuándo es posible que desee utilizar una opción sobre otra y cómo se llaman 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 divisor

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 de qué bytes de 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 tokens N-dimensional, 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 que proporciona 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 palabra completa

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

WhitespaceTokenizer

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 crear rápidamente modelos de prototipos.

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 un inconveniente 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 secuencia de comandos utilizados corresponden a los valores de UScriptCode de International Components for 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 las 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.

WordpieceTokenizer

La tokenización de WordPiece es un esquema de tokenización basado en datos que genera un conjunto de sub-tokens. Estos sub tokens pueden corresponder a morfemas lingüísticos, pero este no suele ser el caso.

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.']]]

BertTokenizer

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 la tokenización a 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'.']]]

OraciónTokenizer

SentencepieceTokenizer es un tokenizador de sub-token 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 sub tokens.

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 lenguajes 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 bigrams. Para volver a convertir a 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

Esta es una envoltura de los 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 dividir 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 resultar difícil ver los resultados de las cadenas de bytes codificadas en UTF-8. Decodifica 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))
[['新华社', '北京']]

SplitMergeTokenizer

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 al crear 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))
[['新华社', '北京']]

RegexSplitter

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

Cuando se tokenizan 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, las compensaciones iniciales son inclusivas y las compensaciones finales son exclusivas.

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]]

Destokenizació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 pretokenizada.

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 TF

TF Data es una API poderosa 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!']]