Abrir en Google Colab
|
Descargar notebook
|
Zero-shot learning en un problema de clasificación
Los grandes modelos de lenguaje exhiben grandes habilidades en zero-shot learning. Sin embargo, los resultados dependen mucho de la capacidad del modelo, y de la ténica que utilicemos para resolver el problema.
En este ejemplo, utizaremos un modelo de lenguaje para resolver el problema de clasificación de tweets sin entrenar ningún modelo (zero-shot).
Introducción
Los grandes modelos de lenguaje son capaces de resolver problemas de clasificación al utilizar determinadas estructuras del idioma.
Para ejecutar este notebook
Para ejecutar este notebook, instale las siguientes librerias:
[ ]:
!wget https://raw.githubusercontent.com/santiagxf/M72109/master/NLP/Datasets/mascorpus/tweets_marketing.csv \
--quiet --no-clobber --directory-prefix ./Datasets/mascorpus/
!wget https://raw.githubusercontent.com/santiagxf/M72109/master/docs/nlp/neural/zero_shot_classification.txt \
--quiet --no-clobber
!pip install -r zero_shot_classification.txt --quiet
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2/7.2 MB 30.2 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 268.8/268.8 kB 24.0 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 46.9 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 45.9/45.9 kB 6.2 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 486.2/486.2 kB 24.6 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.8/7.8 MB 72.2 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 41.5 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 244.2/244.2 kB 24.1 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 86.0/86.0 kB 8.7 MB/s eta 0:00:00
Preparing metadata (setup.py) ... done
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 81.4/81.4 kB 9.5 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 110.5/110.5 kB 16.1 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 212.5/212.5 kB 22.5 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 134.3/134.3 kB 17.5 MB/s eta 0:00:00
Building wheel for sentence-transformers (setup.py) ... done
[ ]:
import warnings
warnings.filterwarnings('ignore')
Cargamos el set de datos
[ ]:
import pandas as pd
tweets = pd.read_csv('Datasets/mascorpus/tweets_marketing.csv')
[ ]:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(tweets['TEXTO'], tweets['SECTOR'],
test_size=0.33,
stratify=tweets['SECTOR'])
Verificando el hardware disponible
[ ]:
import torch
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
print("Este notebook se está ejecutando en", device)
Este notebook se está ejecutando en cuda
Creando un modelo de clasificación utilizando zero-shot learning
Trataremos de resolver entonces el mismo problema de clasificación con el que veniamos trabajando: clasificar los tweets dependiendo del sector al que pertenecen. Recordemos que tenemos 7 categorias distintas:
[ ]:
labels = tweets['SECTOR'].unique().tolist()
labels
['RETAIL',
'TELCO',
'ALIMENTACION',
'AUTOMOCION',
'BANCA',
'BEBIDAS',
'DEPORTES']
El modelo base que utizaremos es BART el cual es multi-lenguaje y puede manejar texto en multiples idiomas:
[ ]:
model_name = "facebook/bart-large-mnli"
En la libraría transformers, podemos utilizar un pipeline del tipo zero-shot-classification, el cual nos permite resolver una tarea de clasificación al modelarla como un problema de text completion.
Este pipeline construye tantos prompts como diferentes clases querramos predecir. Luego, aplica una plantilla que combina el texto de entrada y la etiqueta y busca qué etiqueta genera un texto al cual el modelo de lenguaje le asigna la mayor probabilidad:
[ ]:
from transformers import pipeline
classifier = pipeline(task="zero-shot-classification", model=model_name, device=0)
Tomemos un tweet del conjunto de datos como ejemplo:
[ ]:
example = tweets.iloc[2131]
print(example["TEXTO"], "\n", example["SECTOR"])
El BBVA debería hacer nuevos comerciales con Claudio Bravo.
Aprovechando que ahora es el rey de la banca.
@alebattocchio
BANCA
El papeline necesita 2 argumentos:
sequence: El texto que queremos clasificar.candidate_labels: Las diferentes posibilidades.
[ ]:
sequence = example["TEXTO"]
candidate_labels = labels
Ejecutamos el pipeline:
[ ]:
classifier(sequence, candidate_labels)
{'sequence': 'El BBVA debería hacer nuevos comerciales con Claudio Bravo.\nAprovechando que ahora es el rey de la banca.\n@alebattocchio',
'labels': ['BANCA',
'BEBIDAS',
'ALIMENTACION',
'AUTOMOCION',
'DEPORTES',
'RETAIL',
'TELCO'],
'scores': [0.48729321360588074,
0.34352853894233704,
0.05284114554524422,
0.04914722219109535,
0.03771361708641052,
0.01828593574464321,
0.011190303601324558]}
Vemos que en este caso es capaz de predecir la etiqueta correcta. ¿Sucede con todos los casos? ¿Puede verificar como se comporta este model en otras situaciones?
Mejorando la plantilla para nuestro conjunto de datos
El pipeline, por defecto, aplica una plantilla genérica en inglés para clasificar el texto. Esto puede traer problemas al nuestros tweets estár en español. Para correjir este problema, utilicemos una plantilla propia:
[ ]:
hypothesis_template = "Este tweet se refiere a {}."
Apliquemos la nueva plantilla y verifiquemos los resultado:
[ ]:
classifier(sequence, candidate_labels, hypothesis_template=hypothesis_template)
{'sequence': 'El BBVA debería hacer nuevos comerciales con Claudio Bravo.\nAprovechando que ahora es el rey de la banca.\n@alebattocchio',
'labels': ['BANCA',
'BEBIDAS',
'ALIMENTACION',
'AUTOMOCION',
'RETAIL',
'DEPORTES',
'TELCO'],
'scores': [0.5197097659111023,
0.17679853737354279,
0.0929044634103775,
0.06869390606880188,
0.0685601457953453,
0.04455644264817238,
0.028776705265045166]}
Vemos que cambiar el prompt que utilizamos aumentó la probabilidad de la etiqueta correcta. Sin embargo, ¿afecta este cambio la performance del clasificador en general?
Verifiquemos la performance de este clasificador en el conjunto de evaluación.
[ ]:
predictions = classifier(X_test.tolist(), candidate_labels, hypothesis_template=hypothesis_template, batch_size=100)
Computemos las etiquetas correspondientes a cada predicción:
[ ]:
predictions_label = [pred["labels"][0] for pred in predictions]
Veamos la performance del modelo:
[ ]:
from sklearn.metrics import classification_report
print(classification_report(y_test, predictions_label))
precision recall f1-score support
ALIMENTACION 0.08 0.20 0.11 110
AUTOMOCION 0.39 0.78 0.52 148
BANCA 0.69 0.26 0.38 198
BEBIDAS 0.21 0.23 0.22 223
DEPORTES 0.10 0.01 0.02 216
RETAIL 0.36 0.40 0.38 268
TELCO 0.29 0.06 0.10 79
accuracy 0.29 1242
macro avg 0.30 0.28 0.25 1242
weighted avg 0.31 0.29 0.26 1242
Vemos que la performance mejoró, pero no sustancialmente. Veamos otras alternativas.
Mejorando las etiquetas
Es válido pensar que la etiqueta «ALIMENTACIÓN» es un tanto extraña dentro de una frase. El modelo de lenguaje que estamos utilizando es un Masked Language Model. Esto significa que el mismo predice la probabilidad de la palabra que representa la etiqueta dentro del texto. Esto tiene dos supociones importantes:
Que la etiqueta es parte del vocabulario del modelo.
Que la etiqueta puede utilizarce exitosamente en la posición en donde estamos ubicando la palabra a predecir.
Veamos como se comporta el modelo si cambiamos la etiquetas por otras palabras un poco más representativas:
[ ]:
label_mapping = {
"alimentos": "ALIMENTACION",
"automobiles": "AUTOMOCION",
"bancos": "BANCA",
"bebidas": "BEBIDAS",
"deportes": "DEPORTES",
"supermercados": "RETAIL",
"telefonía": "TELCO"
}
Las nuevas etiquetas entonces luces como sigue:
[ ]:
candidate_labels = list(label_mapping.keys())
candidate_labels
['alimentos',
'automobiles',
'bancos',
'bebidas',
'deportes',
'supermercados',
'telefonía']
Ejecutemos nuestro pipeline con las nuevas etiquetas, aunque manteniendo el prompt que diseñamos en el ejemplo anterior:
[ ]:
predictions = classifier(X_test.tolist(), candidate_labels, hypothesis_template=hypothesis_template, batch_size=100)
Compotemos los labels y veriquemos la performance:
[ ]:
predictions_label = [label_mapping[pred["labels"][0]] for pred in predictions]
[ ]:
print(classification_report(y_test, predictions_label))
precision recall f1-score support
ALIMENTACION 0.23 0.62 0.34 110
AUTOMOCION 0.51 0.93 0.66 148
BANCA 0.91 0.90 0.91 198
BEBDIDAS 0.00 0.00 0.00 0
BEBIDAS 0.00 0.00 0.00 223
DEPORTES 0.45 0.22 0.29 216
RETAIL 0.89 0.12 0.21 268
TELCO 0.39 0.65 0.49 79
accuracy 0.41 1242
macro avg 0.42 0.43 0.36 1242
weighted avg 0.52 0.41 0.38 1242
Vemos que la performance mejoró sustancialmente. Sin embargo, nuestro modelo no exhibe performance similar al ejemplo que diseñamos con BERT.
Recuerde que este problema lo estamos implementando sin disponer de un conjunto de datos de entrenamiento!
Cambiando el modelo base
Verifiquemos la performance del modelo utilizando otro modelo base. El modelo mDeBERTa-v3-base-mnli-xnli fue entrenado previamente por Microsoft en el conjunto de datos multilingüe CC100. Luego se ajustó en el conjunto de datos XNLI, que contiene pares de hipótesis y premisas de 15 idiomas, así como en el conjunto de datos MNLI en inglés.
[ ]:
model_name = "MoritzLaurer/mDeBERTa-v3-base-mnli-xnli"
[ ]:
classifier = pipeline(task="zero-shot-classification", model=model_name, device=0)
Computemos las predicciones utilizando las mismas condiciones que en el ejemplo anterior:
[ ]:
predictions = classifier(X_test.tolist(), candidate_labels, hypothesis_template=hypothesis_template, batch_size=100)
Compotemos los labels y veriquemos la performance:
[ ]:
predictions_label = [label_mapping[pred["labels"][0]] for pred in predictions]
[ ]:
print(classification_report(y_test, predictions_label))
precision recall f1-score support
ALIMENTACION 0.22 0.25 0.24 110
AUTOMOCION 0.84 0.89 0.86 148
BANCA 0.94 0.98 0.96 198
BEBDIDAS 0.00 0.00 0.00 0
BEBIDAS 0.00 0.00 0.00 223
DEPORTES 0.84 0.92 0.88 216
RETAIL 0.99 0.30 0.46 268
TELCO 0.71 0.89 0.79 79
accuracy 0.57 1242
macro avg 0.57 0.53 0.52 1242
weighted avg 0.67 0.57 0.58 1242
Vemos que la utilización de otro modelo base mejoró sustancialmente la performance en el problema puntual.
Abrir en Google Colab
Descargar notebook