Las tareas del procesamiento del lenguaje natural moderno se dividen escencialmente en:
masked text
.ner
: named entity recognition. ciudad, nombre de persona, localización, organización.En esta lección se introducen los procesos que pipeline
de HuggingFace corre internamente. Básicamente pipeline agrupa tres pasos
Etapas del procesamiento del lenguaje natural
Fuente: HuggingFace Transformers course
Para correr el cuaderno original en Tensorflow de HuggingFace en Colab vaya a HuggingFace notebook. Versión Pytorch. En esta lección usamos la versión de Tensorflow.
from transformers import pipeline
classifier = pipeline("sentiment-analysis")
classifier("I've been waiting for a HuggingFace course my hole life")
[{'label': 'NEGATIVE', 'score': 0.9950999617576599}]
classifier([
"I've been waiting for a HuggingFace course my whole life.",
"I hate this so much!"
])
[{'label': 'POSITIVE', 'score': 0.9598047137260437}, {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
Como cualquier red neuronal, los transformers no pueden procesar el texto crudo directamente. El primer paso es entonces convertir los textos en números que tengan sentido para la red. Este paso es hecho por un tokenizador. El tokenizador hace lo siguiente:
Para usar un modelo preentrenado, es necesario hacer este proceso exáctamente de la misma manera como se hizo cuando se pre-entrenó al modelo, por lo que es necesario bajar esta información del Hub del modelo.
Esto se hace con la clase AutoTokenizer y su método from_pretrained. Se usa el checkpoint
del modelo, que es el conjunto de pesos del último modelo entrenado.
En HugginFace el checkpoint por defecto para el pipeline sentiment-analysis es distilbert-base-uncased-finetuned-sst-2-english. La tarjeta de presentación del modelo (card
) se puede consultar aquí. Veámos:
from transformers import AutoTokenizer
checkpoint = 'distilbert-base-uncased-finetuned-sst-2-english'
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
Ya disponemos del tokenizador adecuado y podemos pasarle nuestras frases para conseguir de vuelta un diccionario que está listo para alimentar al modelo. Lo único que queda por hacer es convertir la lista de ID's a tensores. Los tensores pueden ser Pytorch, Tensorflow y más recientemente Flax. En todo caso es importante tener en cuenta que los transformers en Hugginface solamente aceptan tensores.
raw_inputs = [
'This course is fantastic.',
'I hate this so much!'
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors='tf') # return ='pt' para tensores pytorch
print(inputs)
{'input_ids': <tf.Tensor: shape=(2, 8), dtype=int32, numpy= array([[ 101, 2023, 2607, 2003, 10392, 1012, 102, 0], [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(2, 8), dtype=int32, numpy= array([[1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1]], dtype=int32)>}
Podemos bajar el modelo preentrenado mediante el uso de la clase TFAutoModel que también tiene el método from-trained.
from transformers import TFAutoModel # AutoModel en Pythorch
checkpoint = 'distilbert-base-uncased-finetuned-sst-2-english'
model = TFAutoModel.from_pretrained(checkpoint)
Some layers from the model checkpoint at distilbert-base-uncased-finetuned-sst-2-english were not used when initializing TFDistilBertModel: ['pre_classifier', 'classifier', 'dropout_19'] - This IS expected if you are initializing TFDistilBertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model). - This IS NOT expected if you are initializing TFDistilBertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model). All the layers of TFDistilBertModel were initialized from the model checkpoint at distilbert-base-uncased-finetuned-sst-2-english. If your task is similar to the task the model of the checkpoint was trained on, you can already use TFDistilBertModel for predictions without further training.
model.summary()
Model: "tf_distil_bert_model_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= distilbert (TFDistilBertMain multiple 66362880 ================================================================= Total params: 66,362,880 Trainable params: 66,362,880 Non-trainable params: 0 _________________________________________________________________
Es usualmente un tensor de tamaño grande y con tres dimensiones:
outputs = model(inputs)
print(outputs.last_hidden_state.shape)
(2, 8, 768)
outputs
TFBaseModelOutput(last_hidden_state=<tf.Tensor: shape=(2, 8, 768), dtype=float32, numpy= array([[[ 0.71443266, 0.16590782, 0.22164305, ..., 0.4517184 , 0.8582398 , -0.4670435 ], [ 0.833101 , 0.1852059 , 0.14624734, ..., 0.42707193, 0.8372486 , -0.39773327], [ 0.9286826 , 0.1862842 , 0.2808665 , ..., 0.38525626, 0.7030744 , -0.4284996 ], ..., [ 0.8273146 , 0.05676208, 0.45793867, ..., 0.43878704, 0.83444375, -0.67980033], [ 1.0110863 , 0.14425977, 0.80684364, ..., 0.4608204 , 0.61149776, -0.76684564], [ 0.6806943 , 0.1321691 , 0.23668604, ..., 0.40009487, 0.85364753, -0.45703 ]], [[-0.29370627, 0.7282562 , -0.14972652, ..., -0.11868126, -1.0226722 , -0.04215673], [-0.22063597, 0.9383844 , -0.09512459, ..., -0.36431718, -0.66052145, 0.24069718], [-0.15360779, 0.89875007, -0.07276374, ..., -0.21891779, -0.85275996, 0.07099426], ..., [-0.24425443, 0.7034866 , -0.11993726, ..., -0.3340568 , -0.9157687 , 0.17108764], [-0.02888484, 0.90061086, -0.21417457, ..., -0.21345952, -0.92661613, -0.13903481], [ 0.04716622, 0.36029524, -0.17284958, ..., -0.31908724, -0.92990047, -0.10469045]]], dtype=float32)>, hidden_states=None, attentions=None)
El vector de salida outputs contiene la salida de la red transformer. Es decir la salida de la capa codificadora en este caso de clasificación de sentencias en positivas o negativas.
En los modelos de HuggingFace la cabeza corresponde al conjunto final de capas lineales que dan sentido a la tarea a realizar. La imagen ilustra el concepto. Todo el mecanismo de auto-atención está incorporado en la red transformer. En otras palabras, la cabeza es la parte que se agrega al modelo pre-entrenado, luego de quitarle la capa final, para luergo hacer el ajuste fino (fine tunning).
Modelo transformer en huggingface
Fuente: HuggingFace Transformers course
Hay diferentes arquitecturas disponibles en los transformers de HuggingFace, cada diseñada para atacar una tarea específica. Algunas de estas tareas son:
Por ejemplo para la tarea de clasificación no usaremos Model (TFAutoModel) que mostramos arriba, sino que usaremos TFAutoModelForSequenceClassification.
from transformers import TFAutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(inputs)
print(outputs.logits.shape)
Some layers from the model checkpoint at distilbert-base-uncased-finetuned-sst-2-english were not used when initializing TFDistilBertForSequenceClassification: ['dropout_19'] - This IS expected if you are initializing TFDistilBertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model). - This IS NOT expected if you are initializing TFDistilBertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model). Some layers of TFDistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert-base-uncased-finetuned-sst-2-english and are newly initialized: ['dropout_57'] You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
(2, 2)
La salida en nuestro ejmplo son logits:
print(outputs.logits)
tf.Tensor( [[-4.358263 4.692012 ] [ 4.169232 -3.3464477]], shape=(2, 2), dtype=float32)
El modelo predice [-4.358263, 4.692012 ] para la primera sentencia y [ 4.169232, -3.3464477]. Estos valores no son probabilidades sino logits. Para convertirlas a probabilidades se usa la función softmax.
import tensorflow as tf
predictions = tf.math.softmax(outputs.logits)
print(predictions)
tf.Tensor( [[1.17345015e-04 9.99882698e-01] [9.99455869e-01 5.44183713e-04]], shape=(2, 2), dtype=float32)
Para obtener las etiquetas (labels) asociadas a cada posición se puede utilizar model.config.id2label.
model.config.id2label
{0: 'NEGATIVE', 1: 'POSITIVE'}
Por lo que la primera sentencia es calificada como positiva y la segunda como negativa.