Modelos de clasificación de texto basados en mecanismos de atención
Creando modelos de clasificación de texto usando FastAI y BLURR.
En este notebook se muestra cómo crear un modelo de clasificación de texto usando modelos basados en mecanismos de atención mediante la librería HuggingFace, y en concreto mediante la librería blurr que integra HuggingFace con FastAI.
Para esta práctica es necesario el uso de GPU, así que recuerda activar esta opción en Colab.
!pip install fastai -Uqq
!pip install datasets -Uqq
!pip install transformers[sentencepiece] -Uqq
!pip install git+https://github.com/ohmeow/blurr.git@dev-2.0.0 -Uqq
Cargamos a continuación las librerías que necesitaremos en esta práctica.
from fastai.data.all import *
from fastai.learner import *
from fastai.losses import CrossEntropyLossFlat
from fastai.optimizer import Adam, OptimWrapper, params
from fastai.metrics import accuracy, F1Score
from fastai.torch_core import *
from fastai.torch_imports import *
from transformers import AutoModelForSequenceClassification
from blurr.data.core import *
from blurr.modeling.core import *
from blurr.utils import BLURR
from datasets import load_dataset,concatenate_datasets
Dataset
Para este ejemplo vamos a usar el dataset Gutenberg Poem Dataset, un dataset para detectar sentimientos en poemas (negativos, positivos, sin impacto, mezcla de positivo y negativo).
Descarga el dataset usando el siguiente comando.
poem_sentiment_dataset = load_dataset("poem_sentiment")
Vamos a añadir a nuestro dataset una columna que nos indique si estamos trabajando con el conjunto de entrenamiento o el de validación. Para lo cuál debemos definir la siguiente función.
def add_is_valid_batch_friendly(examples, is_valid=False):
return {"is_valid": [is_valid for txt in examples["verse_text"]]}
Y ahora añadimos esa información.
poem_sentiment_dataset["train"] = poem_sentiment_dataset["train"].map(partial(add_is_valid_batch_friendly,is_valid=False),batched=True)
poem_sentiment_dataset["validation"] = poem_sentiment_dataset["validation"].map(partial(add_is_valid_batch_friendly,is_valid=True),batched=True)
print(poem_sentiment_dataset)
print(poem_sentiment_dataset["train"][0])
Antes de empezar a entrenar nuestro modelo combinamos los conjuntos de entrenamiento y test.
proc_ds = concatenate_datasets([poem_sentiment_dataset["train"],poem_sentiment_dataset["validation"]])
proc_ds
Entrenando un modelo de clasificación
El proceso a seguir para hacer fine-tuning sobre el modelo de lenguaje de FastAI es análogo al visto en prácticas anteriores. Comenzamos creando un DataBlock
a partir de nuestro dataframe. Sin embargo, para llevar a cabo esta tarea tenemos que definirlo a partir de los constructores de la librería Blurr.
En primer lugar definimos la tarea con la que vamos a trabajar que es la clasificación de secuencias de texto.
model_cls = AutoModelForSequenceClassification
A continuación debemos indicar el modelo que vamos a utilizar, ya que la creación de los datablocks
es dependiente de esta elección. En nuestro caso usaremo el modelo Bert.
pretrained_model_name = "bert-base-uncased"
Seguidamente debemos crear la configuración de nuesto datablock
, para ello vamos a usar la configuración por defecto del modelo que hemos seleccionado y cambiaremos el número de posibles clases de nuestro problema (en este caso 4).
labels = proc_ds.features["label"].names
labels
n_labels = len(labels)
Por último generamos varias componentes que son necesarias para entrenar nuestro modelo y que son dependientes del modelo que elijamos:
- La arquitectura del modelo.
- La configuración del modelo.
- El tokenizer.
- El modelo de huggingface.
hf_arch, hf_config, hf_tokenizer,hf_model = BLURR.get_hf_objects(
pretrained_model_name,model_cls= model_cls, config_kwargs={"num_labels": n_labels}
)
hf_arch, type(hf_config), type(hf_tokenizer),type(hf_model)
Ahora ya podemos construir nuestro datablock
para lo cual primero debemos preprocesarlo con el modelo que vamos a usar. Notar que debemos indicar el atributo donde se encuentran las frases de nuestro dataset (en este caso verse_text
).
preprocessor = ClassificationPreprocessor(hf_tokenizer,label_mapping=labels,text_attr='verse_text')
ds = preprocessor.process_hf_dataset(proc_ds)
ds
También debemos identificar los índices de nuestro dataset que pertenecen al conjunto de validación.
val_idxs = [idx for idx,el in enumerate(ds) if el["is_valid"]==True]
min(val_idxs),max(val_idxs)
Y por fin construimos nuestro datablock
.
blocks = (
TextBlock(
hf_arch,
hf_config,
hf_tokenizer,
hf_model,
is_pretokenized=True,
before_batch_kwargs={"labels":labels},
tok_kwargs={"add_special_tokens":False}
),
CategoryBlock
)
dblock = DataBlock(
blocks=blocks,
get_x=ItemGetter('verse_text'),
get_y=ItemGetter('label'),
splitter=IndexSplitter(val_idxs)
)
Creamos ahora nuestro dataloader
.
dls = dblock.dataloaders(ds,bs=16)
Podemos ahora mostrar un batch de este dataloader
.
dls.show_batch(dataloaders=dls, max_n=2)
Creamos ahora nuestro Learner
.
model = BaseModelWrapper(hf_model)
learn = Learner(dls,
model,
metrics=[accuracy],
cbs=[BaseModelCallback],
splitter=blurr_splitter
)
Y por último entrenamos el modelo.
learn.fine_tune(10,1e-3)
Una vez entrenado el modelo podemos guardarlo para el futuro.
export_fname = 'seq_class_learn_export'
learn.export(fname=f'{export_fname}.pkl')
Para realizar predicciones debemos usar el método blurr_predict
.
learn.blurr_predict('with pale blue berries. in these peaceful shades--.')
Por último, validamos en nuestro conjunto de test.
poem_sentiment_dataset["test"] = poem_sentiment_dataset["test"].map(partial(add_is_valid_batch_friendly,is_valid=True),batched=True)
proc_ds_test = concatenate_datasets([poem_sentiment_dataset["train"],poem_sentiment_dataset["test"]])
ds_test = preprocessor.process_hf_dataset(proc_ds_test)
test_idxs = [idx for idx,el in enumerate(ds_test) if el["is_valid"]==True]
dblock_test = DataBlock(
blocks=blocks,
get_x=ItemGetter('verse_text'),
get_y=ItemGetter('label'),
splitter=IndexSplitter(test_idxs)
)
dls_test = dblock_test.dataloaders(ds_test,bs=16)
learn.dls = dls_test
learn.validate()
Con esto hemos logrado un modelo con una accuracy del 82% (muy superior al 69% logrado en la práctica anterior).