Clasificación de Opiniones con Kili y HuggingFace AutoTrain

'Opinion Classification with Kili and HuggingFace AutoTrain'

Introducción

Comprender las necesidades de tus usuarios es crucial en cualquier negocio relacionado con los usuarios. Pero también requiere mucho trabajo duro y análisis, lo cual es bastante costoso. ¿Por qué no aprovechar el Aprendizaje Automático entonces? Con mucho menos codificación utilizando Auto ML.

En este artículo, aprovecharemos AutoTrain de HuggingFace y Kili para construir un pipeline de aprendizaje activo para la clasificación de texto. Kili es una plataforma que potencia un enfoque centrado en los datos para el Aprendizaje Automático a través de la creación de datos de entrenamiento de calidad. Proporciona herramientas colaborativas de anotación de datos y APIs que permiten iteraciones rápidas entre la construcción confiable de conjuntos de datos y el entrenamiento de modelos. El aprendizaje activo es un proceso en el que se agrega datos etiquetados al conjunto de datos y luego se vuelve a entrenar un modelo de manera iterativa. Por lo tanto, es infinito y requiere que los humanos etiqueten los datos.

Como caso de uso concreto para este artículo, construiremos nuestro pipeline utilizando las reseñas de los usuarios de VoAGI desde la Google Play Store. Después de eso, vamos a categorizar las reseñas con el pipeline que construimos. Finalmente, aplicaremos análisis de sentimiento a las reseñas clasificadas. Así será mucho más fácil analizar los resultados y comprender las necesidades y la satisfacción de los usuarios.

AutoTrain con HuggingFace

El Aprendizaje Automático Automatizado es un término que se refiere a la automatización de un pipeline de Aprendizaje Automático. También incluye la limpieza de datos, la selección de modelos y la optimización de hiperparámetros. Podemos utilizar 🤗 transformers para la búsqueda automatizada de hiperparámetros. La optimización de hiperparámetros es un proceso difícil y que consume mucho tiempo.

Mientras que podemos construir nuestro pipeline nosotros mismos utilizando transformers y otras APIs poderosas, también es posible automatizar completamente esto con AutoTrain. AutoTrain se construye sobre muchas APIs potentes como transformers, datasets e inference-api.

La limpieza de datos, la selección de modelos y los pasos de optimización de hiperparámetros están completamente automatizados en AutoTrain. Uno puede utilizar completamente este framework para construir modelos de transformers listos para producción para una tarea específica. Actualmente, AutoTrain admite clasificación de texto binaria y multi-etiqueta, clasificación de tokens, pregunta y respuesta extractivas, resumen de texto y puntuación de texto. También admite muchos idiomas como inglés, alemán, francés, español, finlandés, sueco, hindi, holandés, ¡y más! Si tu idioma no es compatible con AutoTrain, también es posible utilizar modelos personalizados con tokenizadores personalizados.

Kili

Kili es una plataforma de entrenamiento de IA de extremo a extremo para negocios centrados en los datos. Kili proporciona funciones de etiquetado optimizadas y herramientas de gestión de calidad para administrar tus datos. Puedes anotar rápidamente datos de imagen, video, texto, pdf y voz mientras controlas la calidad del conjunto de datos. También tiene potentes APIs para GraphQL y Python que facilitan mucho la gestión de datos.

Está disponible tanto en línea como en local y permite técnicas modernas de Aprendizaje Automático tanto en visión por computadora como en PLN y OCR. Admite clasificación de texto, reconocimiento de entidades nombradas (NER), extracción de relaciones y más tareas de PLN/OCR. También admite tareas de visión por computadora como detección de objetos, transcripción de imágenes, clasificación de videos, segmentación semántica ¡y muchas más!

Kili es una herramienta comercial, pero también puedes crear una cuenta de desarrollador gratuita para probar las herramientas de Kili. Puedes obtener más información en la página de precios.

Proyecto

Trabajaremos en un ejemplo de clasificación de reseñas, junto con análisis de sentimiento, para obtener información sobre una aplicación móvil.

Hemos extraído alrededor de 40 mil reseñas de VoAGI de la Google Play Store. Anotaremos los textos de las reseñas en este conjunto de datos paso a paso. Y luego construiremos un pipeline para la clasificación de reseñas. En la modelización, el primer modelo se preparará con AutoTrain. Luego también construiremos un modelo sin utilizar AutoTrain.

Todo el código y el conjunto de datos se pueden encontrar en el repositorio de GitHub del proyecto.

Conjunto de datos

Comencemos echando un vistazo al conjunto de datos sin procesar,

Hay 10 columnas y 40130 muestras en este conjunto de datos. La única columna que necesitamos es content, que es la reseña del usuario. Antes de comenzar, necesitamos definir algunas categorías.

Hemos definido 4 categorías,

  • Suscripción: Dado que VoAGI tiene una opción de suscripción, cualquier cosa relacionada con las opiniones de los usuarios sobre las características de suscripción debe pertenecer aquí.
  • Contenido: VoAGI es una plataforma de intercambio, hay muchas escrituras desde poesía hasta investigación avanzada de inteligencia artificial. Las opiniones de los usuarios sobre una variedad de temas, la calidad del contenido, deben pertenecer aquí.
  • Interfaz: Pensamientos sobre la interfaz de usuario, búsqueda de artículos, motor de recomendación y cualquier cosa relacionada con la interfaz debe pertenecer aquí. Esto también incluye problemas relacionados con el pago.
  • Experiencia del Usuario: Los pensamientos y opiniones generales del usuario sobre la aplicación. Debería ser generalmente abstracto sin indicar otra categoría.

Para la parte de etiquetado, primero necesitamos crear un proyecto en la plataforma de Kili. Podemos utilizar tanto la interfaz web de la plataforma como las APIs. Veamos ambas opciones.

Desde la interfaz web:

Desde la página de lista de proyectos, creamos un proyecto de clasificación de texto de varias clases.

Después de eso, en la página del proyecto, puedes añadir tus datos haciendo clic en el botón “Añadir activos”. Actualmente, puedes añadir como máximo 25000 ejemplos, pero puedes ampliar este límite si contactas al equipo de ventas de Kili.

Después de crear nuestro proyecto, necesitamos añadir trabajos. Podemos preparar una interfaz de etiquetado desde la página de Configuración.

Aunque hemos definido 4 categorías, es inevitable encontrarnos con opiniones que deberían tener múltiples categorías o categorías completamente extrañas. Añadiré dos etiquetas más (que no se usarán en el modelado) para capturar estos casos también.

En nuestro ejemplo, añadimos dos etiquetas más (Otra, Multi-etiqueta). También añadimos un trabajo de reconocimiento de entidades con nombre (NER) solo para especificar cómo decidimos una etiqueta durante el etiquetado. La interfaz final se muestra a continuación

Como puedes ver en el menú de la izquierda, también es posible incluir un enlace que describe tus etiquetas en la página de Instrucciones. También podemos añadir otros miembros a nuestro proyecto desde Miembros o añadir medidas de calidad desde las páginas de Gestión de calidad. Puedes encontrar más información en la documentación.

Ahora, creemos nuestro proyecto con la API de Python:

En primer lugar, necesitamos importar las bibliotecas necesarias.

( notebooks/kili_project_management.ipynb )

import os
# procesaremos los datos (que son un archivo csv)
import pandas as pd

# cliente de la API
from kili.client import Kili
# ¿Por qué no utilizar barras de progreso bonitas?
from tqdm import tqdm

from dotenv import load_dotenv
load_dotenv()

Para acceder a la plataforma, necesitamos autenticar nuestro cliente.

API_KEY = os.getenv('KILI_API_KEY')
# inicializar y autenticar el cliente de Kili
kili = Kili(api_key = API_KEY)

Ahora podemos empezar a preparar nuestra interfaz. La interfaz es simplemente un diccionario en Python. Definiremos nuestros trabajos y luego completaremos las etiquetas. Dado que todas las etiquetas también pueden tener etiquetas hijas, pasaremos las etiquetas también como diccionarios.

labels = ['Experiencia de usuario', 'Suscripción', 'Contenido', 'Otra', 'Multi-etiqueta']
entity_dict = {
    'Experiencia de usuario': '#cc4125',
    'Suscripción': '#4543e6',
    'Contenido': '#3edeb6',
}
project_name = 'Conjunto de datos de reseñas de usuarios para clasificación de temas'
project_description = "Reseñas de la aplicación de VoAGI obtenidas de la tienda de Google Play para la clasificación de temas"

interface = {
    'jobs': {
        'JOB_0': {
            'mlTask': 'CLASIFICACIÓN',
            'instruction': 'Etiquetas',
            'required': 1,
            'content': {
                "categories": {},
                "input": "radio",
            },
        },
        'JOB_1': {
            'mlTask': "RECONOCIMIENTO_DE_ENTIDADES_CON_NOMBRE",
            'instruction': 'Entidades',
            'required': 1,
            'content': {
                'categories': {},
                "input": "radio"
            },
        },
    }
}

# completar el json de la interfaz con los trabajos
for label in labels:
    # convierte las etiquetas a mayúsculas y reemplaza los espacios en blanco por guiones bajos (_)
    # ej. Experiencia de usuario -> EXPERIENCIA_DE_USUARIO
    # esta es la forma preferida de completar la interfaz
    label_upper = label.strip().upper().replace(' ', '_')
    # 
    content_dict_0 = interface['jobs']['JOB_0']['content']
    categories_0 = content_dict_0['categories']
    category = {'name': label, 'children': []}
    categories_0[label_upper] = category

for label, color in entity_dict.items():
    label_upper = label.strip().upper().replace(' ', '_')
    content_dict_1 = interface['jobs']['JOB_1']['content']
    categories_1 = content_dict_1['categories']
    category = {'name': label, 'children': [], 'color': color}
    categories_1[label_upper] = category

# ahora podemos crear nuestro proyecto
# este método devuelve el id del proyecto creado
project_id = kili.create_project(json_interface=interface,
                            input_type='TEXTO',
                            title=project_name,
                            description=project_description)['id']

Estamos listos para cargar nuestros datos en el proyecto. El método append_many_to_dataset se puede utilizar para importar los datos a la plataforma. Utilizando la API de Python, podemos importar los datos en lotes de un máximo de 100. Aquí hay una función simple para cargar los datos:

def import_dataframe(project_id:str, dataset:pd.DataFrame, text_data_column:str, external_id_column:str, subset_size:int=100) -> bool:
    """
    Argumentos:
    Inputs
        - project_id (str): especifica el proyecto al que se cargan los datos, esto también se devuelve al crear nuestro proyecto
        - dataset (pandas DataFrame): Conjunto de datos que tiene columnas adecuadas para las entradas de id y texto
        - text_data_column (str): especifica qué columna tiene los datos de entrada de texto
        - external_id_column (str): especifica qué columna tiene los ids
        - subset_size (int): especifica el número de muestras para importar a la vez. No puede ser mayor que 100
    
    Salidas:
        Ninguna
    
    Retorna: 
        Verdadero o Falso respecto a la sucesión del proceso

    """

    assert subset_size <= 100, "Kili solo permite cargar como máximo 100 activos a la vez en la aplicación"


    L = len(dataset)

    # establecer 25000 como límite de carga, se puede cambiar
    if L>25000:
        print('Kili Projects actualmente admite como máximo 25000 muestras de forma predeterminada. Importando las primeras 25000 muestras...')
        L=25000

    i = 0

    while i+subset_size < L:
        
        subset = dataset.iloc[i:i+subset_size]

        externalIds = subset[external_id_column].astype(str).to_list()
        contents = subset[text_data_column].astype(str).to_list()
        
        kili.append_many_to_dataset(project_id=project_id,
                                    content_array=contents,
                                    external_id_array=externalIds)

        i += subset_size

    return True

Simplemente importa el DataFrame dataset dado a un proyecto especificado por project_id.

Podemos ver los argumentos en el docstring, solo necesitamos pasar nuestro conjunto de datos junto con los nombres de columna correspondientes. Solo usaremos los índices de muestra que obtenemos al cargar los datos. ¡Y listo, la carga de datos está hecha!

dataset_path = '../data/processed/lowercase_cleaned_dataset.csv'
df = pd.read_csv(dataset_path).reset_index() # restablecer el índice para obtener los índices

import_dataframe(project_id, df, 'content', 'index')

No fue difícil usar la API de Python, los métodos auxiliares que usamos cubrieron muchas dificultades. También usamos otro script para verificar las nuevas muestras cuando actualizamos el conjunto de datos. A veces, el rendimiento del modelo disminuye después de la actualización del conjunto de datos. Esto se debe a errores simples como etiquetar incorrectamente e introducir sesgos en el conjunto de datos. El script simplemente autentica y luego mueve muestras distintas de dos versiones de conjunto de datos dados a To Review. Podemos cambiar la propiedad de una muestra a través del método update_properties_in_assets:

( scripts/move_diff_to_review.py )

# Configurar el cliente Kili y los argumentos
from kili.client import Kili
from dotenv import load_dotenv
import os
import argparse

import pandas as pd

load_dotenv()

parser = argparse.ArgumentParser()
parser.add_argument('--first',
                    required=True,
                    type=str,
                    help='Ruta al primer dataframe')
parser.add_argument('--second',
                    required=True,
                    type=str,
                    help='Ruta al segundo dataframe')

args = vars(parser.parse_args())

# configurar la conexión de Kili
API_KEY = os.getenv('KILI_API_KEY')
kili = Kili(API_KEY)

# leer los dataframes
df1 = pd.read_csv(args['first'])
df2 = pd.read_csv(args['second'])

# concatenar ambos debería permitirnos tener duplicados de elementos comunes
# luego podemos eliminar los elementos duplicados sin mantener duplicados para obtener los elementos diferentes entre los dos dataframes
diff_df = pd.concat((df1, df2)).drop_duplicates(keep=False)
diff_ids = diff_df['id'].to_list()

# Los cambios deben darse como un array que 
# contiene el cambio para cada muestra individual. 
# Por eso se pasa [‘TO_REVIEW’] * len(diff_df) al argumento status_array
kili.update_properties_in_assets(diff_ids,
                                 status_array=['TO_REVIEW'] * len(diff_ids))

print('¡SE ESTABLECIERON %d ENTRADAS PARA REVISIÓN!' % len(diff_df))

Etiquetado

Ahora que hemos cargado los datos fuente, la plataforma tiene una interfaz de etiquetado incorporada que es bastante fácil de usar. Los atajos de teclado disponibles ayudaron durante la anotación de datos. Utilizamos la interfaz sin esfuerzo, hay atajos de teclado definidos automáticamente y simplifica el etiquetado. Podemos ver los atajos de teclado haciendo clic en el icono del teclado en la parte superior derecha de la interfaz, también se muestran con caracteres subrayados en la interfaz de etiquetado a la derecha.

Algunas muestras eran muy extrañas, así que decidimos omitirlas mientras las etiquetábamos. En general, el proceso fue mucho más fácil gracias a la plataforma integrada de Kili.

Exportando los datos etiquetados

Los datos etiquetados se exportan fácilmente utilizando la API de Python. El siguiente script exporta las muestras etiquetadas y revisadas a un dataframe, luego lo guarda con un nombre dado como archivo CSV.

( scripts/prepare_dataset.py )

import argparse
import os

import pandas as pd
from dotenv import load_dotenv
from kili.client import Kili

load_dotenv()

parser = argparse.ArgumentParser()
parser.add_argument('--output_name',
                    required=True,
                    type=str,
                    default='dataset.csv')
parser.add_argument('--remove', required=False, type=str)
args = vars(parser.parse_args())

API_KEY = os.getenv('KILI_API_KEY')
dataset_path = '../data/processed/lowercase_cleaned_dataset.csv'
output_path = os.path.join('../data/processed', args['output_name'])


def extract_labels(labels_dict):
    response = labels_dict[-1]  # seleccionar la última versión de la muestra
    label_job_dict = response['jsonResponse']['JOB_0']
    categories = label_job_dict['categories']
    # todas las muestras tienen una etiqueta, podemos seleccionarla por su índice
    label = categories[0]['name']
    return label


kili = Kili(API_KEY)
print('¡Autenticado!')
# la consulta devolverá una lista que contiene elementos coincidentes (proyectos en este caso)
# como solo tenemos un proyecto con este nombre, podemos seleccionar el primer índice
project = kili.projects(
    search_query='Conjunto de datos de opiniones de usuarios para clasificación de temas')[0]
project_id = project['id']

# podemos personalizar los campos devueltos
# los campos a continuación son suficientes, 
# labels.jsonResponse contiene los datos de etiquetado
returned_fields = [
    'id', 'externalId', 'labels.jsonResponse', 'skipped', 'status'
]
# También leí el conjunto de datos original para emparejar las muestras con externalId
dataset = pd.read_csv(dataset_path)

# podemos obtener los datos como un dataframe
df = kili.assets(project_id=project_id,
                 status_in=['LABELED', 'REVIEWED'],
                 fields=returned_fields,
                 format='pandas')

print('¡Obtenidas las muestras!')

# pasaremos por alto las muestras omitidas
df_ns = df[~df['skipped']].copy()

# extraemos las muestras etiquetadas
df_ns.loc[:, 'label'] = df_ns['labels'].apply(extract_labels)
# La columna externalId se devuelve como una cadena, vamos a convertirla a entero
# para usarla como índices
df_ns.loc[:, 'content'] = dataset.loc[df_ns.externalId.astype(int), 'content']

# podemos eliminar la columna `labels` ahora
df_ns = df_ns.drop(columns=['labels'])

# eliminaremos también las muestras con la etiqueta especificada en el argumento 'remove' si se proporciona
if args['remove']:
    df_ns = df_ns.drop(index=df_ns[df_ns['label'] == args['remove']].index)

print('RECUPERACIÓN DE DATOS FINALIZADA')
print('EL CONJUNTO DE DATOS TIENE %d MUESTRAS' % (len(df_ns)))
print('GUARDANDO EL CONJUNTO DE DATOS PROCESADO EN: %s' % os.path.abspath(output_path))

df_ns.to_csv(output_path, index=False)

print('¡HECHO!')

¡Genial! Ahora tenemos los datos etiquetados como un archivo CSV. ¡Creemos un repositorio de conjuntos de datos en HuggingFace y subamos los datos allí!

Es realmente simple, solo haz clic en tu foto de perfil y selecciona la opción Nuevo Conjunto de Datos.

Luego ingresa el nombre del repositorio, elige una licencia si quieres y ¡listo!

Ahora podemos subir el conjunto de datos desde Añadir archivo en la pestaña Archivos y versiones.

El visualizador de conjuntos de datos está disponible automáticamente después de cargar los datos, ¡podemos verificar fácilmente las muestras!

También es posible cargar el conjunto de datos en el hub de conjuntos de datos de Hugging Face utilizando el paquete datasets.

Modelado

Utilicemos el aprendizaje activo. Etiquetamos y ajustamos el modelo de forma iterativa. En cada iteración, etiquetamos 50 muestras en el conjunto de datos. El número de muestras se muestra a continuación:

Probemos primero AutoTrain:

Primero, abra el AutoTrain

  1. Crea un proyecto

  1. Podemos seleccionar el repositorio de conjuntos de datos que creamos antes o cargar el conjunto de datos nuevamente. Luego, debemos elegir el tipo de división, lo dejaré como Auto.

  1. Entrena los modelos

AutoTrain probará diferentes modelos y seleccionará los mejores modelos. Luego realiza la optimización automática de hiperparámetros. El conjunto de datos también se procesa automáticamente.

El precio depende totalmente de su caso de uso. Puede ser tan bajo como $10 o puede ser más caro que el valor actual.

El entrenamiento se realiza después de aproximadamente 20 minutos, ¡los resultados son bastante buenos!

La precisión del mejor modelo es casi del 89%.

Ahora podemos usar este modelo para realizar el análisis, solo tomó unos 30 minutos configurar todo.

Modelado sin AutoTrain

Utilizaremos Ray Tune y la API del entrenador de Hugging Face para buscar hiperparámetros y ajustar un modelo de aprendizaje profundo pre-entrenado. Hemos seleccionado el modelo de clasificación de sentimientos base roBERTa que se entrena en tweets para el ajuste fino. Hemos ajustado el modelo en colaboratorio de Google y se puede encontrar en la carpeta notebooks en el repositorio de GitHub.

Ray Tune es una biblioteca popular para la optimización de hiperparámetros que viene con muchos algoritmos SOTA predefinidos. También es posible usar Optuna y SigOpt. También utilizamos el algoritmo de programación sucesiva asíncrona (ASHA) como el programador y HyperOpt como el algoritmo de búsqueda. Lo cual es prácticamente un punto de partida. Puede utilizar diferentes programadores y algoritmos de búsqueda.

¿Qué haremos?

  • Importar las bibliotecas necesarias (una docena de ellas) y preparar una clase de conjunto de datos
  • Definir las funciones y métodos necesarios para procesar los datos
  • Cargar el modelo pre-entrenado y el tokenizador
  • Ejecutar la búsqueda de hiperparámetros
  • Utilizar los mejores resultados para la evaluación

¡Comencemos importando las bibliotecas necesarias! (todo el código está en notebooks/modeling.ipynb y el cuaderno de colaboratorio de Google)

# importaciones generales de ciencia de datos/utilización/visualización
import json
import os
import random

# barra de progreso
from tqdm import tqdm

# manipulación de datos / lectura
import numpy as np
import pandas as pd

# visualización
import plotly.express as px
import matplotlib.pyplot as plt

# métricas de evaluación predefinidas
from sklearn.metrics import (accuracy_score, f1_score,
                             precision_score, recall_score)

from sklearn.model_selection import train_test_split
# importaciones de torch
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset, random_split
# importaciones de Hugging Face
import transformers
from datasets import load_metric
from transformers import (AutoModelForSequenceClassification, AutoTokenizer, 
                          Trainer, TrainingArguments)

# importaciones de Ray Tune para la optimización de hiperparámetros
from ray.tune.schedulers import ASHAScheduler, PopulationBasedTraining
from ray.tune.suggest.hyperopt import HyperOptSearch

Estableceremos una semilla para las bibliotecas que utilizamos para la reproducibilidad

def seed_all(seed):
    torch.manual_seed(seed)
    random.seed(seed)
    np.random.seed(seed)

SEED=42
seed_all(SEED)

¡Ahora definamos nuestra clase de conjunto de datos!

class TextClassificationDataset(Dataset):
    def __init__(self, dataframe):
        self.labels = dataframe.label.to_list()
        self.inputs = dataframe.content.to_list()
        self.labels_to_idx = {k:v for k,v in labels_dict.items()} # copiar el diccionario labels_dict

    def __len__(self):
        return len(self.inputs)

    def __getitem__(self, idx):
        if type(idx)==torch.Tensor:
            idx = list(idx)

        input_data = self.inputs[idx]
        target = self.labels[idx]
        target = self.labels_to_idx[target]

        return {'text': input_data, 'label':target}

Podemos descargar el modelo fácilmente especificando el repositorio de HuggingFace hub. También es necesario importar el tokenizador para el modelo especificado. Tenemos que proporcionar una función para inicializar el modelo durante la optimización de hiperparámetros. El modelo se definirá allí mismo.

La métrica a optimizar es la precisión, queremos que este valor sea lo más alto posible. Por eso, necesitamos cargar la métrica, luego definir una función para obtener las predicciones y calcular la métrica preferida.

model_name = 'cardiffnlp/twitter-roberta-base-sentiment'
# realizaremos la búsqueda para optimizar la precisión del modelo,
# necesitamos especificar y cargar la métrica de precisión como primer paso
metric = load_metric("accuracy")


# dado que ya hemos ingresado un nombre de modelo, podemos cargar el tokenizador
# también podemos cargar el modelo, pero lo describiré en la función model_init.
tokenizer = AutoTokenizer.from_pretrained(model_name)


def model_init():
    """
    La optimización de hiperparámetros se realiza mediante modelos recién inicializados,
    por lo tanto, necesitaremos inicializar el modelo nuevamente para cada ejecución de búsqueda individual. 
    Esta función inicializa y devuelve el modelo pre-entrenado seleccionado con `model_name`
    """
    return AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=4, return_dict=True, ignore_mismatched_sizes=True)

# la función para calcular la precisión
def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1) # simplemente selecciona los índices que tienen los valores máximos
    return metric.compute(predictions=predictions, references=labels)

Después de definir el cálculo de la métrica y la función de inicialización del modelo, podemos cargar los datos:

file_name = "dataset-11.csv"

dataset_path = os.path.join('data/processed', file_name)
dataset = pd.read_csv(dataset_path)

También he definido dos diccionarios para mapear etiquetas a índices e índices a etiquetas.

idx_to_label = dict(enumerate(dataset.label.unique()))
labels_dict = {v:k for k,v in idx_to_label.items()}

Ahora podemos definir el algoritmo de búsqueda y el programador para la búsqueda de hiperparámetros.

scheduler = ASHAScheduler(metric='objective', mode='max')
search_algorithm = HyperOptSearch(metric='objective', mode='max', random_state_seed=SEED)
# número de ejecuciones para la búsqueda de parámetros
n_trials = 40

También necesitamos tokenizar los datos de texto antes de pasarlos al modelo, podemos hacer esto fácilmente utilizando el tokenizador cargado. Ray Tune funciona en un entorno de caja negra, por lo que utilicé el tokenizador como un argumento predeterminado para una solución alternativa. De lo contrario, se produciría un error sobre la definición del tokenizador.

def tokenize(muestra, tokenizer=tokenizer):
    muestra_tokenizada = tokenizer(muestra['text'], padding=True, truncation=True)
    muestra_tokenizada['label'] = muestra['label']
    return muestra_tokenizada

Otra función de utilidad que devuelve divisiones de conjunto de datos de Torch estratificadas y tokenizadas:

def prepare_datasets(dataset_df, test_size=.2, val_size=.2):
    train_set, test_set = train_test_split(dataset_df, test_size=test_size,
                                        stratify=dataset_df.label, random_state=SEED)

    train_set, val_set = train_test_split(train_set, test_size=val_size,
                                        stratify=train_set.label, random_state=SEED)

    # barajar los dataframes de antemano 
    train_set = train_set.sample(frac=1, random_state=SEED)
    val_set = val_set.sample(frac=1, random_state=SEED)
    test_set = test_set.sample(frac=1, random_state=SEED)

    # convertir los dataframes a conjuntos de datos Torch
    train_dataset = TextClassificationDataset(train_set)
    val_dataset = TextClassificationDataset(val_set)
    test_dataset = TextClassificationDataset(test_set)

    # tokenizar los conjuntos de datos
    train_set_tokenizado = train_dataset.map(tokenize)
    val_set_tokenizado = val_dataset.map(tokenize)
    test_set_tokenizado = test_dataset.map(tokenize)

    # finalmente, devolver los conjuntos procesados
    return train_set_tokenizado, val_set_tokenizado, test_set_tokenizado

¡Ahora podemos realizar la búsqueda! Comencemos procesando los datos:

train_set_tokenizado, val_set_tokenizado, test_set_tokenizado = prepare_datasets(dataset)

training_args = TrainingArguments(
    'trial_results',
    evaluation_strategy="steps",
    disable_tqdm=True,
    skip_memory_metrics=True,
)

trainer = Trainer(
    args=training_args,
    tokenizer=tokenizer,
    train_dataset=train_set_tokenizado,
    eval_dataset=val_set_tokenizado,
    model_init=model_init,
    compute_metrics=compute_metrics
    )

best_run = trainer.hyperparameter_search(
    direction="maximize", 
    n_trials=n_trials,
    backend="ray",
    search_alg=search_algorithm,
    scheduler=scheduler
    )

Realizamos la búsqueda con 20 y 40 pruebas respectivamente, los resultados se muestran a continuación. El promedio ponderado de las puntuaciones de F1, Recall y Precision para 20 ejecuciones.

El promedio ponderado de las puntuaciones de F1, Recall y Precision para 40 ejecuciones.

El rendimiento se disparó en la tercera versión del conjunto de datos. En algún momento de la etiquetación de datos, introduje demasiado sesgo en el conjunto de datos por error. Como podemos ver, su rendimiento se vuelve más razonable a medida que la varianza de la muestra aumenta más adelante. El modelo final se guarda en Google Drive y se puede descargar desde aquí, también es posible descargarlo a través del script download_models.py.

Análisis Final

Ahora podemos utilizar el modelo ajustado para realizar el análisis final. Todo lo que tenemos que hacer es cargar los datos, procesarlos y obtener los resultados de predicción del modelo. Luego podemos usar un modelo pre-entrenado para el análisis de sentimientos y esperamos obtener conocimientos.

Utilizamos Google Colab para la inferencia (aquí) y luego exportamos los resultados a result.csv. Se puede encontrar en results en el repositorio de GitHub. Luego analizamos los resultados en otro cuaderno colaborativo de Google para una experiencia interactiva. Por lo tanto, también puedes usarlo fácilmente e interactuar con él.

¡Veamos los resultados ahora!

Podemos ver que las puntuaciones dadas son muy positivas. En general, a los usuarios les gusta la aplicación.

Esto también coincide con el análisis de sentimientos, la mayoría de las opiniones son positivas y la menor cantidad de opiniones se clasifican como negativas.

Como podemos ver en lo anterior, el rendimiento del modelo es comprensible. Las puntuaciones positivas son dominantemente más altas que las demás, tal como muestra el gráfico de análisis de sentimientos.

En cuanto a las categorías definidas anteriormente, parece que el modelo predice que la mayoría de las opiniones se refieren a las experiencias de los usuarios (excluyendo las experiencias relacionadas con otras categorías):

También podemos ver las predicciones de sentimientos sobre las categorías definidas a continuación:

No haremos un análisis detallado de las opiniones, sería suficiente tener una comprensión básica de los posibles problemas. Por lo tanto, es suficiente con obtener resultados simples de los datos finales:

  • Es comprensible que la mayoría de las opiniones sobre la suscripción sean negativas. En general, el contenido de pago no es bien recibido en las aplicaciones móviles.
  • Hay muchas opiniones negativas sobre la interfaz. Esto puede ser una pista para un análisis posterior. Tal vez haya una idea equivocada sobre las características, o una característica no funciona como los usuarios pensaron.
  • En general, a las personas les han gustado los artículos y la mayoría de ellos tuvieron buenas experiencias.

Nota importante sobre el gráfico: no hemos filtrado las opiniones por versión de la aplicación. Cuando observamos los resultados de la última versión actual (4.5), parece que la interfaz de la aplicación confunde a los usuarios o tiene errores molestos.

Conclusión

Ahora podemos utilizar el modelo pre-entrenado para tratar de comprender las posibles deficiencias de la aplicación móvil. Luego sería más fácil analizar una característica específica.

Utilizamos las poderosas APIs de HuggingFace y AutoTrain junto con la interfaz fácil de usar de Kili en este ejemplo. El modelado con AutoTrain solo llevó 30 minutos, eligió los modelos y los entrenó para nuestro uso. AutoTrain es definitivamente mucho más eficiente ya que pasé más tiempo desarrollando el modelo por mi cuenta.

Todo el código, los conjuntos de datos y los scripts se pueden encontrar en GitHub. También puedes probar el modelo AutoTrain.

Aunque podemos considerar esto como un punto de partida válido, deberíamos recopilar más datos e intentar construir mejores tuberías. Mejores tuberías darían como resultado mejoras más eficientes.

We will continue to update Zepes; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Inteligencia Artificial

¿Cómo funciona realmente la Difusión Estable? Una explicación intuitiva

Este breve artículo explica cómo funciona la Difusión Estable de manera intuitiva para principiantes. Es un vistazo b...

Inteligencia Artificial

Comprendiendo el concepto de GPT-4V(ision) La nueva tendencia de la inteligencia artificial

OpenAI ha estado a la vanguardia de los últimos avances en IA, con modelos altamente competentes como GPT y DALLE. Cu...

Inteligencia Artificial

Por qué los científicos se adentran en el mundo virtual

Un número creciente de investigadores científicos están utilizando la tecnología de realidad virtual (VR) en el labor...

Inteligencia Artificial

AI Equipaje para Personas con Discapacidad Visual Recibe Excelentes Críticas

La maleta de IA ofrece características que superan a las de los sistemas de guía tradicionales de teléfonos inteligen...

Inteligencia Artificial

Microsoft Research lanza el 'Cuarteto de Heavy Metal' de los compiladores de IA Rammer, Roller, Welder y Grinder

La evolución de los modelos de inteligencia artificial (IA) y los aceleradores de hardware ha traído desafíos únicos ...