<jupyter_start><jupyter_text>Modelos de clasificación de texto> Creando modelos de clasificación de texto usando FastAI y los datasets de HuggingFace.- toc: true - badges: true- comments: true- categories: [practica]- image: images/chart-preview.png 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](https://www.fast.ai/).Para esta práctica es necesario el uso de GPU, así que recuerda activar esta opción en Colab.   LibreríasComenzamos actualizando la librería FastAI y descargando la librería [datasets de HuggingFace](https://huggingface.co/docs/datasets/index). Al finalizar la instalación deberás reiniciar el kernel (menú Entorno de ejecución -> Reiniciar Entorno de ejecución).<jupyter_code>!pip install fastai -Uqq
!pip install datasets -Uqq<jupyter_output><empty_output><jupyter_text>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.<jupyter_code>import pandas as pd
from fastai.text.all import *
from datasets import load_dataset<jupyter_output><empty_output><jupyter_text>DatasetPara este ejemplo vamos a usar el dataset [Gutenberg Poem Dataset](https://huggingface.co/datasets/poem_sentiment), un dataset para detectar sentimientos en poemas (negativos, positivos, sin impacto, mezcla de positivo y negativo).Descarga el dataset usando el siguiente comando.<jupyter_code>poem_sentiment_dataset = load_dataset("poem_sentiment")<jupyter_output>Using custom data configuration default
Reusing dataset poem_sentiment (/root/.cache/huggingface/datasets/poem_sentiment/default/1.0.0/4e44428256d42cdde0be6b3db1baa587195e91847adabf976e4f9454f6a82099)<jupyter_text>A continuación mostramos el dataset que hemos descargado.<jupyter_code>poem_sentiment_dataset<jupyter_output><empty_output><jupyter_text>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.<jupyter_code>poem_sentiment_dataset["train"][0]<jupyter_output><empty_output><jupyter_text>Podemos ver también una descripción del dataset usando el atributo ``info``.<jupyter_code>print(poem_sentiment_dataset["train"].info)<jupyter_output>DatasetInfo(description='Poem Sentiment is a sentiment dataset of poem verses from Project Gutenberg. This dataset can be used for tasks such as sentiment classification or style transfer for poems.\n', citation='@misc{sheng2020investigating,\n      title={Investigating Societal Biases in a Poetry Composition System},\n      author={<NAME> and <NAME>},\n      year={2020},\n      eprint={2011.02686},\n      archivePrefix={arXiv},\n      primaryClass={cs.CL}\n}\n', homepage='https://github.com/google-research-datasets/poem-sentiment', license='', features={'id': Value(dtype='int32', id=None), 'verse_text': Value(dtype='string', id=None), 'label': ClassLabel(num_classes=4, names=['negative', 'positive', 'no_impact', 'mixed'], id=None)}, post_processed=None, supervised_keys=None, task_templates=None, builder_name='poem_sentiment', config_name='default', version=1.0.0, splits={'train': SplitInfo(name='train', num_bytes=48667, num_examples=892, dataset_name='poem_sentiment'), 'vali[...]<jupyter_text>Por último, podemos ver las características de nuestro dataset utilizando el atributo ``features``.<jupyter_code>poem_sentiment_dataset["train"].features<jupyter_output><empty_output><jupyter_text>Con el comando anterior podemos ver las cuatro clases de nuestro dataset, y los índices que les corresponden.  Carga de datosCargamos a continuación el dataset en distintos dataframes de pandas (el formato que puede leer la librería de FastAI).<jupyter_code>train_df = poem_sentiment_dataset["train"].to_pandas()
valid_df = poem_sentiment_dataset["validation"].to_pandas()
test_df = poem_sentiment_dataset["test"].to_pandas()<jupyter_output><empty_output><jupyter_text>Podemos ver el contenido del dataset usando el siguiente comando.<jupyter_code>train_df<jupyter_output><empty_output><jupyter_text>Del dataset nos interesan dos campos: `verse_text` (que contiene el poema) y `label` (que contiene el sentimiento).<jupyter_code>train_df['verse_text']
train_df['label']<jupyter_output><empty_output><jupyter_text>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.<jupyter_code>train_df['set']=False
valid_df['set']=True<jupyter_output><empty_output><jupyter_text>También eliminamos la columna ``id`` que no va a ser necesaria.<jupyter_code>train_df = train_df.drop(columns=['id'],axis=1)
valid_df = valid_df.drop(columns=['id'],axis=1)<jupyter_output><empty_output><jupyter_text>Ahora juntamos ambos datasets.<jupyter_code>train_valid_df = pd.concat([train_df,valid_df])<jupyter_output><empty_output><jupyter_text>Por último, hay que renombrar la columna ``verse_text`` por ``text`` (esto es una restricción de FastAI).<jupyter_code>train_valid_df = train_valid_df.rename(columns={"verse_text": "text"})
train_valid_df<jupyter_output><empty_output><jupyter_text>Entrenando un modeloEl 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.<jupyter_code>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
)<jupyter_output><empty_output><jupyter_text>Ahora definimos nuestro dataloader a partir del DataBlock que acabamos de crear (esto le puede costar unos segundos).<jupyter_code>dls = sentiment_clas.dataloaders(train_valid_df, bs=64)<jupyter_output><empty_output><jupyter_text>Podemos mostrar un batch de nuestro dataloader.<jupyter_code>dls.show_batch(max_n=2)<jupyter_output><empty_output><jupyter_text>Pasamos ahora a crear nuestro `learner` usando el método `text_classifier_learner` al que pasamos como arquitectura de red la arquitectura [AWD_LSTM](https://arxiv.org/abs/1708.02182), además aplicamos dropout a nuestro modelo.<jupyter_code>callbacks = [ShowGraphCallback(),
             SaveModelCallback()]

learn = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5, metrics=accuracy,cbs=callbacks).to_fp16()<jupyter_output><empty_output><jupyter_text>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.<jupyter_code>learn.lr_find()<jupyter_output><empty_output><jupyter_text>Y a continuación aplicar fine tuning.<jupyter_code>learn.fine_tune(10, 2e-2)<jupyter_output><empty_output><jupyter_text>Ahora podemos usar nuestro modelo para predecir la clase de una nueva frase.<jupyter_code>learn.predict('with pale blue berries. in these peaceful shades--.')<jupyter_output><empty_output><jupyter_text>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.<jupyter_code>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)<jupyter_output><empty_output><jupyter_text>Modificamos ahora el dataloader de nuestro learner, y procedemos a validar.<jupyter_code>learn.dls = dls_test
learn.validate()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09015142622373518, 'fraction_numerical': 0.011738466956215518, 'mean_word_length': 7.597376387487387, 'pattern_counts': {'":': 2, '<': 93, '<?xml version=': 0, '>': 95, 'https://': 5, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '577028', 'license': 'Apache-2.0', 'n_tokens_mistral': 2905, 'n_tokens_neox': 2813, 'n_words': 765, 'path': '_notebooks/2022-03-22-practica6-clasificacion-texto-basica.ipynb', 'repo_name': 'AP2122/aprendizajeprofundo2122', 'size': '8529'}