Abrir en Google Colab
|
Descargar notebook
|
BERT en un problema de clasificación
Introducción
Los modelos basados en transformers nos pueden ayudar a resolver varios tipos de problemas. Desde problemas de clasificación y regresión hasta tareas más complejas como resumen de textos o generación de leguaje condicionado. Veamos como resolver el problema de clasificación de tweets sobre el que hemos estado trabajando anteriormente pero ahora utilizando el modelo BERT.
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/BERT.txt \
--quiet --no-clobber
!pip install -r BERT.txt --quiet
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2/7.2 MB 50.4 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 486.2/486.2 kB 42.6 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 268.8/268.8 kB 24.0 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.8/7.8 MB 70.7 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 31.7 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 244.2/244.2 kB 21.1 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 110.5/110.5 kB 11.1 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 212.5/212.5 kB 22.3 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 134.3/134.3 kB 9.8 MB/s eta 0:00:00
[ ]:
import warnings
warnings.filterwarnings('ignore')
Cargamos el set de datos
[ ]:
import pandas as pd
tweets = pd.read_csv('Datasets/mascorpus/tweets_marketing.csv')
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']
Para simplificar nuestra tarea con la libraria transformers, nos asguraremos de disponer de 2 columnas, una llamada text y otra labels, la cual inidica lo que queremos predecir.
[ ]:
tweets = tweets[['TEXTO', 'SECTOR']].rename(columns = {'TEXTO':'text', 'SECTOR':'labels'})
Utilizaremos la libraría datasets de ahora en adelante para trabajar con el conjunto de datos. La misma ofrece una integración mas sencilla con la librarías de HuggingFace.
[ ]:
from datasets import Dataset, ClassLabel, Features, Value
dataset = Dataset.from_pandas(
df=tweets,
features=Features({
'text': Value("string"),
'labels': ClassLabel(names=labels)
})
)
Dividimos nuestro conjunto de datos en entrenamiento y testing.
[ ]:
train_test = dataset.train_test_split(test_size=0.33, stratify_by_column='labels')
Vea como se representan en esta libraría:
[ ]:
train_test
DatasetDict({
train: Dataset({
features: ['text', 'labels'],
num_rows: 2521
})
test: Dataset({
features: ['text', 'labels'],
num_rows: 1242
})
})
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
Transferencia de Aprendizaje y Fine-tuning
En general, existen 2 estrategias para utilizar modelos de lenguaje pre-entrenados en una tarea especifica:
Feature-based
Fine-tunning
Las técnicas que se conocen como Feature-based utiliza arquitecturas especificas para resolver cada una de las tareas de NLP, en donde los pesos de las representaciones vectoriales están «congeladas» y no son parámetros que el modelo deba optimizar. En consecuencia, estos modelos son más rápidos de entrenar y permiten aplicar arquitecturas especificas que sean diferenciales en cada una de las tareas.
Por el otro lado, las técnicas que emplean Fine-tunning tiene la flexibilidad de poder adaptar sus representaciones al permitir que todos los parametros sean optimizados en la tarea en particular. Además, estas arquitecturas permiten resolver multiples problemas de NLP utilizando una mínima cantidad de parametros específicos para la tarea.
BETO: BERT en español
Al igual que con word2vec, entrenar un modelo de lenguaje requiere de una gran cantidad de datos sumado a un poder de computo interesante (cuando BERT fué publicado en 2018, tomó 4 días entrenar el modelo usando 16 TPUs. Si se hubiera entrenado en 8 GPUs hubiera tomado entre 40–70 días). Por este motivo, utilizaremos un modelo pre-entrenado para un cuerpo de texto en español. Este modelo, BETO, fué entrenado sobre un gran corpora de textos. Pueden encontrar más información sobre en el sitio
web del autor.
Tokenizers
BERT utiliza su propio tokenizer que está basado en WordPiece. Este tokenizer tiene un vocabulario de 30.000 tokens donde cada secuencia comienza con un token especial [CLS]. Recuerden que los tokenizers dependen del modelo con el que estamos trabajando:
[ ]:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('dccuchile/bert-base-spanish-wwm-uncased',
do_lower_case=True)
Noten que el tokenizer depende del modelo que estamos utilizando
[ ]:
def tokenization(example):
return tokenizer(example["text"], padding='max_length', truncation=True, return_tensors="pt")
train_test = train_test.map(tokenization, batched=True, batch_size=16)
Crando un modelo de clasificación basado en BERT
Necesitaremos contar con el numero de categorias para nuestro clasificador:
[ ]:
num_labels=len(labels)
Antes de hacer fine-tunning de nuestro modelo, tenemos que instanciar el modelo sobre el cual queremos aplicar esta técnica. Para ello instanciaremos el modelo base el cual no está entrenado en ninguna tarea en particular. De hecho, si habilitan las alertas en este notebook, verán que cuando se carga el modelo, la libreria HuggingFace les advierte sobre esto:
[ ]:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained('dccuchile/bert-base-spanish-wwm-uncased',
num_labels=num_labels)
Some weights of the model checkpoint at dccuchile/bert-base-spanish-wwm-uncased were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.dense.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.bias', 'cls.predictions.decoder.weight', 'cls.predictions.decoder.bias']
- This IS expected if you are initializing BertForSequenceClassification 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 BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at dccuchile/bert-base-spanish-wwm-uncased and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Construiremos nuestro dataset sobre el que queremos entrenar el modelo. Recuerden que ya habíamos separado el set de datos en porciones para entrenar y para testear el modelo.
Como entrenar modelos con Transformers
La librería transformers puede entrenar modelos tanto utilizando TensorFlow como PyTorch como backend. En nuestro caso utilizaremos PyTorch simplemente porque generaremos código un poco más compacto, pero pueden utilizar el backend con el que más cómodos se sientan:
Trainer
Especificamos los parametros con los que entrenaremos nuestro modelo:
[ ]:
from transformers import Trainer, TrainingArguments, DataCollatorForLanguageModeling
[ ]:
training_args = TrainingArguments(
output_dir='./results', # Directorio de trabajo del Trainer
num_train_epochs=3, # Numero total de epochs sobre el que entrenaremos
warmup_steps=500, # Numero de pasos que se usaran para determinar la politica de Learning Rate
weight_decay=0.01, # Weight decay
logging_dir='./logs', # Directorio de logs
)
Instanciamos el Trainer
[ ]:
trainer = Trainer(
model=model, # modelo sobre el que haremos fine tunning
args=training_args, # parametros del entrenamiento
train_dataset=train_test['train'], # set de datos de entrenamiento
eval_dataset=train_test['test'], # set de datos de evaluación
)
[ ]:
history = trainer.train()
| Step | Training Loss |
|---|---|
| 500 | 0.584900 |
Verifiquemos la performance de nuestro modelo
[ ]:
predictions = trainer.predict(test_dataset=train_test['test']).predictions
Para evaluar el modelo, primero deberemos obtener cual es la categoria que obtuvo la mayor probabilidad en la clasificación:
[ ]:
import numpy as np
predictions = np.argmax(predictions, axis=1)
Convertimos los IDs de las categorias a los labels correctos
[ ]:
from sklearn.metrics import classification_report
print(classification_report(train_test['test']['labels'], predictions, target_names=labels))
precision recall f1-score support
RETAIL 0.99 0.98 0.99 268
TELCO 0.96 0.99 0.97 79
ALIMENTACION 0.99 0.97 0.98 110
AUTOMOCION 0.99 0.99 0.99 148
BANCA 1.00 0.99 1.00 198
BEBIDAS 1.00 0.99 0.99 223
DEPORTES 0.97 1.00 0.98 216
accuracy 0.99 1242
macro avg 0.99 0.99 0.99 1242
weighted avg 0.99 0.99 0.99 1242
Persistiendo el modelo
Podemos persistir el modelo para utilizar posteriormente de la siguiente forma. Note que deberemos persistir tanto el modelo como el tokenizer ya que los dos funcionan de la mano.
[ ]:
model_name = "tweet_classifier_bert"
trainer.save_model(model_name)
tokenizer.save_pretrained(model_name)
('tweet_classifier_bert/tokenizer_config.json',
'tweet_classifier_bert/special_tokens_map.json',
'tweet_classifier_bert/vocab.txt',
'tweet_classifier_bert/added_tokens.json',
'tweet_classifier_bert/tokenizer.json')
De las lineas anteriores, vemos que tenemos dos elementos para persistir, el modelo propiamente dicho y su correspondiente tokenizer. De hecho, si exploramos un poco más a detalle los elementos que se guardaron, veremos varios archivos que se generan, incluyendo, por ejemplo, el vocabulario que el tokenizer utilizará.
Vea el contenido del directorio:
[ ]:
!ls $model_name
config.json special_tokens_map.json tokenizer.json vocab.txt
pytorch_model.bin tokenizer_config.json training_args.bin
Una vez que su modelo esta guardado, puede publicarlo en HuggingFace simplemente volverlo a cargar utilizando la instrucción from_pretrained.
[ ]:
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)
Inspeccionando el modelo utilizando TensorBoard
PRECAUCIÓN 😱: El tema presentado en esta sección está clasificado como avanzado. El entendimiento de este contenido es totalmente opcional.
Podemos inspeccionar nuestro modelo utilizando la herramienta TensorBoard
[ ]:
!tensorboard --logdir ./logs --bind_all
En Google Colab, podemos ver TensorBoard usando el siguiente Magic
[ ]:
%load_ext tensorboard
%tensorboard --logdir ./logs
Abrir en Google Colab
Descargar notebook