Los tokenizadores constituyen una parte clave del entubamiento de PLN. La traea de un tokenizador es transformar un texto en datos que pueden ser procesados por el modelo.
Los modelos solamente procesan números. Así que un tokenizador transforma un texto en bruto (raw text) en una secuencia de números.
Inicialmente vamos a pensar en tokenización basada en palabras (word-based). La siguiente imagen ilustra le meta inicial de un toeknizador: dividir el texto bruto en palabras (o subpalabras) para encontrar una representación numérica del texto.
Ejemplos de tokenización por palabras
Fuente: HuggingFace Transformers course
La división del texto en palabras puede hacerse directamente con la función split de Python.
tokenized_text = " Daniel es un profesor de inteligencia artificial".split()
print(tokenized_text)
['Daniel', 'es', 'un', 'profesor', 'de', 'inteligencia', 'artificial']
Existen distintas variaciones de tokenizadores por palabras, los cuales incluyen reglas extras para la puntuación. Estos tokenizadores pueden llegar a tener un vocabulario muy grande. El vocabulario
es definido como el total de tokens independientes en el corpus.
A cada palabra (token) se le asigna un ID, el cual empieza en 0 y va hasta el tamaño del vocabulario. El modelo utiliza este ID para identificar cada palabra.
De momento no existe un vocabulario universal único, por lo que los tokenizadores en los modelos preentrenado son específicos en cada uno de ellos. Por supuesto existen tokenizadores para trabajar desde cero (from scratch), pero el resultado está asociado a su corpus.
Un primer pensamiento que podemos tener es justamente cubrir un lenguaje natural con un tokenizador basado en palabras. Sin embargo esto resulta demasiado costoso para el modelo y en realidad innecesario. El costo no es tanto en la codificación, que por ejemplo en el inglés puede supera las 500.000 palabras, sino en lo que eso significa para su incorporación en el modelo. Recuerde que la capa de embedding (incrsutamiento) es una capa densa que debe entrenarse y que den entrada tiene como número de neuronas de entrada el tamaño del vocabulario. Además cada sentencia, entra palabra por palabra, o que rápidamente lleva a cargas excesivas, tanto para la inferencia, como para el entrenamiento.
En el otro extremo se encuentran los tokenizadores basados en caracteres. En general estos generan vocabularios mucho mas cortos. En este caso aparecen problemas diferentes a los tokenizadores por palabras. A diferencia de la tokenización por palabras en donde existen similaridades entre ellas, en la tokenizacipon por caracteres, tal similaridad se pierde. Además algunos problemas aparecen, relacionados con el manejo de la puntuación.
Sinembargo esto difiere entre lso diferente lenguajes naturales. Por ejemplo en chino, cada caracter carga más información que un caracter en un lenguaje latino. La siguiente imagen ilustra una tokenización por caracteres latinos.
Ejemplos de tokenización por caracteres
Fuente: HuggingFace Transformers course
En este tipo de tokenizadores se toma lo mejor de los dos mundos anteriores. La tokenización basada en subpalabras descanasa en el principio de que *palabras frecuentemente usadas no deben subdivisirse más, pero palabras menos frecuentes pueden eventualmente ser subdivididas en subpalabras cons siginficado. Dos ejemplos de tokenización basados en subpalabras son los siguientes.
Observe que 'perros' y 'casas' comparten el hecho de ser palabras plurales.
Asi, sucesivamente.
Cargar y guardar tokenizadores es tan fácil como con los modelos. Tambiés se bsa en los métodos from_trained y save_pretrained. Vamos a cargar el tokenizer BERT.
Para correr el cuaderno original de HuggingFace para Tensorflow en Colab vaya a Huggingface notebook. Para Torch aquí.
from transformers import BertTokenizer
# Instancia el tokenizador parea la configuración bert-base-cased
tokenizer = BertTokenizer.from_pretrained('bert-base-cased')
Ahora podemos usar el tokenizador
tokenizer('Using a transformer network is simple')
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
Guardar un tokenizador es idéntico a salvar un modelo:
El proceso de trasladar texto a números se llama codificación. La codificación tiene dos pasos: tokenización y conversión a ID's.
Este proceso es hechp por el método tokenize del tokenizer.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
sequence = 'Using a Transformer network is easy'
tokens = tokenizer.tokenize(sequence)
print(tokens)
['Using', 'a', 'Trans', '##former', 'network', 'is', 'easy']
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
[7993, 170, 13809, 23763, 2443, 1110, 3123]
import tensorflow as tf
ids_tensor = tf.constant(ids)
print(ids_tensor)
tf.Tensor([ 7993 170 13809 23763 2443 1110 3123], shape=(7,), dtype=int32)
# torch
import torch
ids_tensor = torch.tensor(ids)
print(ids_tensor)
tensor([ 7993, 170, 13809, 23763, 2443, 1110, 3123])
La decodificación es el camino inverso ala codificación. Es decir, se reupera el texto desde los ID's.
decoded_text = tokenizer.decode(ids)
print(decoded_text)
Using a Transformer network is easy