Modelos de clasificación de texto
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 una variante de una red LSTM implementada en FastAI.
Para esta práctica es necesario el uso de GPU, así que recuerda activar esta opción en Colab.
Librerías
Comenzamos actualizando la librería FastAI y descargando la librería datasets de HuggingFace. Al finalizar la instalación deberás reiniciar el kernel (menú Entorno de ejecución -> Reiniciar Entorno de ejecución).
!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")
A continuación mostramos el dataset que hemos descargado.
poem_sentiment_dataset
Podemos ver que el dataset es una estructura DatasetDict que puede verse como un diccionario. El diccionario tiene tres claves que son train, validation y test que indican respectivamente los conjuntos de entrenamiento, validación y test (estas claves pueden variar dependiendo del dataset). Cada uno de estos subconjuntos es un Dataset que puede verse como una lista. Podemos ver por ejemplo la primera frase del conjunto de entrenamiento del siguiente modo.
poem_sentiment_dataset["train"][0]
Podemos ver también una descripción del dataset usando el atributo info
.
print(poem_sentiment_dataset["train"].info)
Por último, podemos ver las características de nuestro dataset utilizando el atributo features
.
poem_sentiment_dataset["train"].features
Con el comando anterior podemos ver las cuatro clases de nuestro dataset, y los índices que les corresponden.
train_df = poem_sentiment_dataset["train"].to_pandas()
valid_df = poem_sentiment_dataset["validation"].to_pandas()
test_df = poem_sentiment_dataset["test"].to_pandas()
Podemos ver el contenido del dataset usando el siguiente comando.
train_df
Del dataset nos interesan dos campos: verse_text
(que contiene el poema) y label
(que contiene el sentimiento).
train_df['verse_text']
train_df['label']
Juntamos ahora nuestro conjunto de entrenamiento y de validación. Para poder diferenciarlos vamos a añadir una columna a cada uno de ellos para indicar al conjunto al que pertenecen. La columna set
va a tener dos valores: True si pertenece al conjunto de validación y False si pertenece al conjunto de test.
train_df['set']=False
valid_df['set']=True
También eliminamos la columna id
que no va a ser necesaria.
train_df = train_df.drop(columns=['id'],axis=1)
valid_df = valid_df.drop(columns=['id'],axis=1)
Ahora juntamos ambos datasets.
train_valid_df = pd.concat([train_df,valid_df])
Por último, hay que renombrar la columna verse_text
por text
(esto es una restricción de FastAI).
train_valid_df = train_valid_df.rename(columns={"verse_text": "text"})
train_valid_df
Entrenando un modelo
El proceso para entrenar un modelo es el mismo que hemos visto para los modelos de clasificación de imágenes y podemos usar toda la funcionalidad vista hasta ahora. Comenzamos definiendo un DataBlock
que se creará a partir de nuestro dataframe df
. Tendremos que definir también una función que nos indique cuándo un poema es del conjunto de entrenamiento o del de validación.
sentiment_clas = DataBlock(
blocks=(TextBlock.from_df('text'), CategoryBlock), # La entrada del modelo es texto, 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 (esto le puede costar unos segundos).
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()]
learn = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5, metrics=accuracy,cbs=callbacks).to_fp16()
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.
learn.lr_find()
Y a continuación aplicar fine tuning.
learn.fine_tune(10, 2e-2)
Ahora podemos usar nuestro modelo para predecir la clase de una nueva frase.
learn.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.
learn.dls = dls_test
learn.validate()
Como podemos ver nuestro modelo tiene una accuracy del 66%. En las próximas prácticas veremos cómo mejorarlo.