Modelos de clasificación de texto usando self-supervised learning
Creando modelos de clasificación de texto usando FastAI y los datasets de HuggingFace.
En este notebook se muestra cómo crear un modelo de clasificación de texto usando la aproximación ULMFit.
En primer lugar es necesario disponer de un modelo de lenguaje entrenado con un dataset de tamaño considerable. En nuestro caso la Wikipedia. Este modelo sirve para conocer los fundamentos del lenguaje con el cual se está trabajando. Sin embargo, a la hora de construir un modelo de clasificación es conveniente que el modelo comprenda el estilo que se usa para escribir esos textos. Dicho estilo puede ser más informal o más técnico que el contenido de la wikipedia. En el caso del dataset IMDb, este va a contener una gran cantidad de nombres de directores, actores, y además el estilo de redacción es más informal que los textos que aparecen en la Wikipedia. Por ello, a partir del modelo de lenguaje de Wikipedia entrenaremos un modelo de lenguaje para IMDb, y a partir de ese modelo de lenguaje construiremos nuestro clasificador.
Afortunadamente la librería FastAI ya proporciona un modelo de lenguaje para la Wikipedia (construir este tipo de modelos puede llevar días), por lo que nos podemos centrar en los otros dos pasos.
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
Cargamos a continuación las librerías que necesitaremos en esta práctica que son la parte de procesado de lenguaje natural de la librería fastAI, la librería pandas, y la funcionalidad para cargar datasets de HuggingFace.
import pandas as pd
from fastai.text.all import *
from datasets import load_dataset
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")
train_df = poem_sentiment_dataset["train"].to_pandas()
valid_df = poem_sentiment_dataset["validation"].to_pandas()
test_df = poem_sentiment_dataset["test"].to_pandas()
A continuación procesamos el dataset como vimos en la práctica anterior para tenerlo en el formato adecuado.
train_df['set']=False
valid_df['set']=True
train_df = train_df.drop(columns=['id'],axis=1)
valid_df = valid_df.drop(columns=['id'],axis=1)
train_valid_df = pd.concat([train_df,valid_df])
train_valid_df = train_valid_df.rename(columns={"verse_text": "text"})
db_lm = DataBlock(
blocks=TextBlock.from_df('text', is_lm=True,max_vocab=100000), # Indicamos que vamos a trabajar con un modelo de lenguaje
get_items=ColReader('text'), # Indicamos donde estará el texto dentro del dataframe
splitter=RandomSplitter(0.1) # Partimos el dataset en entrenamiento y validación
)
Creamos ahora nuestro dataloader
(esto puede llevar varios segundos).
dls_lm = db_lm.dataloaders(train_valid_df, bs=128, seq_len=80)
Podemos ahora mostrar un batch de este dataloader
. Como podemos apreciar, la entrada del modelo es una frase, y la salida es dicha frase desplazada una posición a la derecha.
dls_lm.show_batch(max_n=2)
Creamos ahora nuestro Learner
.
learn = language_model_learner(
dls_lm, # El dataloader que usamos
AWD_LSTM, # La arquitectura que es la misma usada en la práctica anterior
drop_mult=0.3, # Aplicamos dropout para evitar el sobreajuste
metrics=[accuracy, Perplexity()] # Como métricas usamos la accuracy y la perplexity.
).to_fp16()
Y por último entrenamos el modelo.
learn.fine_tune(10,base_lr=2e-2)
Una vez entrenado el modelo guardamos el encoder
que usaremos luego para nuestro modelo de clasificación (esto es análogo a lo que vimos para los modelos de clasificación de imágenes).
learn.save_encoder('finetuned')
Entrenando un modelo de clasificación
Pasamos ahora a crear nuestro modelo de clasificación de texto. El proceso será el mismo que el que vimos en la práctica anterior con la diferencia de que antes de empezar a entrenar el modelo cargaremos el encoder
guardado en el paso anterior.
Comenzamos definiendo un DataBlock
que se creará a partir de nuestro dataframe df
.
sentiment_clas = DataBlock(
blocks=(TextBlock.from_df('text', vocab=dls_lm.vocab), # La entrada del modelo es texto usando el mismo
# vocabulario que en el modelo de lenguaje
CategoryBlock), #, y la salida una clase
get_x=ColReader('text'), # Indicamos donde estará el texto dentro del dataframe
get_y=ColReader('label'), # Indicamos cómo extraer la clase del dataframe
splitter=ColSplitter('set') # Partimos el dataset en entrenamiento y validación
)
Ahora definimos nuestro dataloader a partir del DataBlock que acabamos de crear.
dls = sentiment_clas.dataloaders(train_valid_df, bs=64)
Podemos mostrar un batch de nuestro dataloader.
dls.show_batch(max_n=2)
Pasamos ahora a crear nuestro learner
usando el método text_classifier_learner
al que pasamos como arquitectura de red la arquitectura AWD_LSTM, además aplicamos dropout a nuestro modelo.
callbacks = [ShowGraphCallback(),
SaveModelCallback()]
learnClass = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5, metrics=accuracy,cbs=callbacks).to_fp16()
Cargamos a continuación el encoder
del modelo de lenguaje.
learnClass = learnClass.load_encoder('finetuned')
Ahora podemos utilizar toda la funcionalidad que ya vimos para clasificación de imágenes. Por ejemplo, podemos buscar un learning rate adecuado para entrenar nuestro modelo.
learnClass.lr_find()
Y a continuación aplicar fine tuning.
learnClass.fine_tune(10, 6e-2)
Ahora podemos usar nuestro modelo para predecir la clase de una nueva frase.
learnClass.predict('with pale blue berries. in these peaceful shades--.')
Por último, podemos validar nuestro modelo en el conjunto de test, para lo cuál hay que combinar los dataframes y construir un nuevo dataloader.
test_df['set']=True
test_df = test_df.drop(columns=['id'],axis=1)
train_test_df = pd.concat([train_df,test_df])
train_test_df = train_test_df.rename(columns={"verse_text": "text"})
dls_test = sentiment_clas.dataloaders(train_test_df, bs=64)
Modificamos ahora el dataloader de nuestro learner, y procedemos a validar.
learnClass.dls = dls_test
learnClass.validate()
Hemos obtenido un modelo con una accuracy del 69% (un 3% mejor que sin aplicar la técnica de self-supervised learning).