Tutorial: Creación, evaluación y puntuación de un modelo de clasificación de texto

Este tutorial presenta un ejemplo de extremo a extremo de un flujo de trabajo de ciencia de datos de Synapse para un modelo de clasificación de texto en Microsoft Fabric. El escenario usa word2vec y la regresión logística en Spark para determinar el género de un libro del conjunto de datos de libros de la Biblioteca Británica en función únicamente del título del libro.

En este tutorial se describen estos pasos:

  • Instalación de bibliotecas personalizadas
  • Carga de los datos
  • Comprender y procesar los datos a través de un análisis de datos exploratorio
  • Entrenamiento de un modelo de Machine Learning mediante word2vec y una regresión logística, y seguimiento de experimentos mediante la característica de registro automático de MLflow y Fabric
  • Carga del modelo de Machine Learning para puntuar y realizar predicciones

Requisitos previos

Seguir en un cuaderno

Puede elegir una de estas opciones para seguir en un cuaderno:

  • Abra y ejecute el cuaderno integrado en la experiencia de ciencia de datos de Synapse.
  • Cargue su cuaderno desde GitHub a la experiencia de ciencia de datos de Synapse.

Abrir el cuaderno de notas integrado

El cuaderno de notas de muestra Clasificación de género del título acompaña a este tutorial.

Para abrir el cuaderno de muestra integrado en el tutorial en la experiencia de ciencia de datos de Synapse:

  1. Vaya a la página principal de ciencia de datos de Synapse.

  2. Seleccione Utilizar una muestra.

  3. Seleccione la muestra correspondiente:

    • Desde la pestaña predeterminada Flujos de trabajo de un extremo a otro (Python), si la muestra es para un tutorial de Python.
    • Desde la pestaña Flujos de trabajo de un extremo a otro (R), si la muestra es para un tutorial de R.
    • En la pestaña Tutoriales rápidos, si la muestra es para un tutorial rápido.
  4. Adjunte una instancia de LakeHouse al cuaderno antes de empezar a ejecutar código.

Importación del cuaderno desde GitHub

AIsample- Title Genre Classification.ipynb es el cuaderno que acompaña a este tutorial.

Para abrir el cuaderno complementario para este tutorial, siga las instrucciones en Preparación del sistema para los tutoriales de ciencia de datos para importar el cuaderno en el área de trabajo.

Si prefiere copiar y pegar el código de esta página, puede crear un cuaderno nuevo.

Asegúrese de adjuntar una instancia de LakeHouse al cuaderno antes de empezar a ejecutar código.

Paso 1: Instalación de bibliotecas personalizadas

Para desarrollar modelos de Machine Learning o realizar análisis de datos ad hoc, es posible que tenga que instalar rápidamente una biblioteca personalizada para la sesión de Apache Spark. Tiene dos opciones para instalar bibliotecas.

  • Use las funcionalidades de instalación insertadas (%pip o %conda) del cuaderno para instalar una biblioteca solo en el cuaderno actual.
  • Como alternativa, puede crear un entorno de Fabric, instalar bibliotecas desde orígenes públicos o cargar bibliotecas personalizadas en él y, después, el administrador del área de trabajo puede asociar el entorno como valor predeterminado para el área de trabajo. Todas las bibliotecas del entorno estarán disponibles para su uso en los cuadernos y las definiciones de trabajo de Spark del área de trabajo. Para obtener más información sobre los entornos, consulte Creación, configuración y uso de un entorno en Microsoft Fabric.

Para el modelo de clasificación, use la biblioteca wordcloud para representar la frecuencia de las palabras en un texto, donde el tamaño de una palabra representa su frecuencia. En este tutorial, usará %pip install para instalar wordcloud en el cuaderno.

Nota:

El kernel de PySpark se reiniciará después de %pip install ejecuciones. Si lo necesita, instale bibliotecas antes de ejecutar cualquier otra celda.

# Install wordcloud for text visualization by using pip
%pip install wordcloud

Paso 2: Carga de los datos

El conjunto de datos consta de metadatos sobre libros de la Biblioteca Británica que se han digitalizado a través de la colaboración entre la Biblioteca Británica y Microsoft. Los metadatos son información de clasificación para indicar si un libro es de ficción o no ficción. Con este conjunto de datos, el objetivo es entrenar un modelo de clasificación que determine el género de un libro solo en función de su título.

Id. de registro de BL Tipo de recurso Nombre Fechas asociadas con el nombre Tipo de nombre Role Todos los nombres Título Títulos variantes Título de serie Número dentro de la serie País de publicación Lugar de publicación Publicador Fecha de publicación Edición Descripción física Clasificación Dewey Signatura topográfica de BL Temas Género Idiomas Notas Id. de registro de BL para el recurso físico classification_id user_id created_at subject_ids annotator_date_pub annotator_normalised_date_pub annotator_edition_statement annotator_genre annotator_FAST_genre_terms annotator_FAST_subject_terms annotator_comments annotator_main_language annotator_other_languages_summaries annotator_summaries_language annotator_translation annotator_original_language annotator_publisher annotator_place_pub annotator_country annotator_title Vínculo al libro digitalizado anotado
014602826 Monografía Yearsley, Ann 1753-1806 persona More, Hannah, 1745-1833 [persona]; Yearsley, Ann, 1753-1806 [persona] Poems on several occasions [With a prefatory letter by Hannah More.] Inglaterra London 1786 Cuarta edición de la nota de MANUSCRITO Digital Store 11644.d.32 English 003996603 False
014602830 Monografía A, T. persona Oldham, John, 1653-1683 [persona]; A, T. [persona] A Satyr against Vertue. (A poem: supposed to be spoken by a Town-Hector [By John Oldham. The preface signed: T. A.]) Inglaterra London 1679 15 páginas (4°) Digital Store 11602.ee.10. (2.) English 000001143 False

Defina los parámetros siguientes para aplicar este cuaderno a diferentes conjuntos de datos:

IS_CUSTOM_DATA = False  # If True, the user must manually upload the dataset
DATA_FOLDER = "Files/title-genre-classification"
DATA_FILE = "blbooksgenre.csv"

# Data schema
TEXT_COL = "Title"
LABEL_COL = "annotator_genre"
LABELS = ["Fiction", "Non-fiction"]

EXPERIMENT_NAME = "sample-aisample-textclassification"  # MLflow experiment name

Descarga del conjunto de datos y carga en un almacén de lago de datos

El código siguiente descarga una versión disponible públicamente del conjunto de datos y, a continuación, la almacena en un almacén de Fabric lakehouse.

Importante

Agregue un almacén de lago al cuaderno antes de ejecutarlo. De lo contrario, se producirá un error.

if not IS_CUSTOM_DATA:
    # Download demo data files into the lakehouse, if they don't exist
    import os, requests

    remote_url = "https://synapseaisolutionsa.blob.core.windows.net/public/Title_Genre_Classification"
    fname = "blbooksgenre.csv"
    download_path = f"/lakehouse/default/{DATA_FOLDER}/raw"

    if not os.path.exists("/lakehouse/default"):
        # Add a lakehouse, if no default lakehouse was added to the notebook
        # A new notebook won't link to any lakehouse by default
        raise FileNotFoundError(
            "Default lakehouse not found, please add a lakehouse and restart the session."
        )
    os.makedirs(download_path, exist_ok=True)
    if not os.path.exists(f"{download_path}/{fname}"):
        r = requests.get(f"{remote_url}/{fname}", timeout=30)
        with open(f"{download_path}/{fname}", "wb") as f:
            f.write(r.content)
    print("Downloaded demo data files into lakehouse.")

Importación de bibliotecas necesarias

Antes de cualquier procesamiento, debe importar las bibliotecas necesarias, incluidas las de Spark y SynapseML:

import numpy as np
from itertools import chain

from wordcloud import WordCloud
import matplotlib.pyplot as plt
import seaborn as sns

import pyspark.sql.functions as F

from pyspark.ml import Pipeline
from pyspark.ml.feature import *
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import (
    BinaryClassificationEvaluator,
    MulticlassClassificationEvaluator,
)

from synapse.ml.stages import ClassBalancer
from synapse.ml.train import ComputeModelStatistics

import mlflow

Definición de hiperparámetros

Defina algunos hiperparámetros para el entrenamiento de un modelo.

Importante

Modifique estos hiperparámetros solo si comprende cada parámetro.

# Hyperparameters 
word2vec_size = 128  # The length of the vector for each word
min_word_count = 3  # The minimum number of times that a word must appear to be considered
max_iter = 10  # The maximum number of training iterations
k_folds = 3  # The number of folds for cross-validation

Inicie la grabación del tiempo necesario para ejecutar este cuaderno:

# Record the notebook running time
import time

ts = time.time()

Configuración del seguimiento del experimento de MLflow

El registro automático amplía las funcionalidades de registro de MLflow. El registro automático captura automáticamente los valores de los parámetros de entrada y las métricas de salida de un modelo de Machine Learning a medida que se entrene. A continuación, registre esta información en el área de trabajo. En el área de trabajo, puede acceder a la información y visualizarla con las API de MLflow o el experimento correspondiente en el área de trabajo. Para obtener más información sobre el registro automático, consulte Registro automático en Microsoft Fabric.

# Set up Mlflow for experiment tracking

mlflow.set_experiment(EXPERIMENT_NAME)
mlflow.autolog(disable=True)  # Disable Mlflow autologging

Para deshabilitar el registro automático de Microsoft Fabric en una sesión de cuaderno, llame a mlflow.autolog() y establezca disable=True.

Lectura de datos sin procesar desde el almacén de lago

raw_df = spark.read.csv(f"{DATA_FOLDER}/raw/{DATA_FILE}", header=True, inferSchema=True)

Paso 3: Realiza un análisis exploratorio de los datos

Explore el conjunto de datos mediante el comando display para ver las estadísticas de alto nivel del conjunto de datos y mostrar las vistas del gráfico:

display(raw_df.limit(20))

Preparación de los datos

Elimine los duplicados para limpiar los datos:

df = (
    raw_df.select([TEXT_COL, LABEL_COL])
    .where(F.col(LABEL_COL).isin(LABELS))
    .dropDuplicates([TEXT_COL])
    .cache()
)

display(df.limit(20))

Aplique el equilibrio de clases para abordar cualquier sesgo:

# Create a ClassBalancer instance, and set the input column to LABEL_COL
cb = ClassBalancer().setInputCol(LABEL_COL)

# Fit the ClassBalancer instance to the input DataFrame, and transform the DataFrame
df = cb.fit(df).transform(df)

# Display the first 20 rows of the transformed DataFrame
display(df.limit(20))

Divida los párrafos y oraciones en unidades más pequeñas para tokenizar el conjunto de datos. De este modo, resulta más fácil asignar significado. A continuación, quite las palabras irrelevantes para mejorar el rendimiento. La eliminación de palabras irrelevantes implica la eliminación de palabras que suelen aparecer en todos los documentos del corpus. La eliminación de palabras irrelevantes es uno de los pasos de preprocesamiento más usados en las aplicaciones de procesamiento del lenguaje natural (NLP).

# Text transformer
tokenizer = Tokenizer(inputCol=TEXT_COL, outputCol="tokens")
stopwords_remover = StopWordsRemover(inputCol="tokens", outputCol="filtered_tokens")

# Build the pipeline
pipeline = Pipeline(stages=[tokenizer, stopwords_remover])

token_df = pipeline.fit(df).transform(df)

display(token_df.limit(20))

Muestre la biblioteca wordcloud para cada clase. Una biblioteca wordcloud es una presentación visualmente destacada de palabras clave que aparecen con frecuencia en datos de texto. La biblioteca wordcloud es eficaz porque la representación de palabras clave forma una imagen de color similar a una nube para capturar mejor los datos de texto principales a simple vista. Más información sobre wordlcloud.

# WordCloud
for label in LABELS:
    tokens = (
        token_df.where(F.col(LABEL_COL) == label)
        .select(F.explode("filtered_tokens").alias("token"))
        .where(F.col("token").rlike(r"^\w+$"))
    )

    top50_tokens = (
        tokens.groupBy("token").count().orderBy(F.desc("count")).limit(50).collect()
    )

    # Generate a wordcloud image
    wordcloud = WordCloud(
        scale=10,
        background_color="white",
        random_state=42,  # Make sure the output is always the same for the same input
    ).generate_from_frequencies(dict(top50_tokens))

    # Display the generated image by using matplotlib
    plt.figure(figsize=(10, 10))
    plt.title(label, fontsize=20)
    plt.axis("off")
    plt.imshow(wordcloud, interpolation="bilinear")

Por último, use word2vec para vectorizar el texto. La técnica word2vec crea una representación vectorial de cada palabra del texto. Las palabras usadas en contextos similares o que tienen relaciones semánticas se capturan de forma eficaz a través de su proximidad en el espacio vectorial. Esta proximidad indica que las palabras similares tienen vectores de palabra similares.

# Label transformer
label_indexer = StringIndexer(inputCol=LABEL_COL, outputCol="labelIdx")
vectorizer = Word2Vec(
    vectorSize=word2vec_size,
    minCount=min_word_count,
    inputCol="filtered_tokens",
    outputCol="features",
)

# Build the pipeline
pipeline = Pipeline(stages=[label_indexer, vectorizer])
vec_df = (
    pipeline.fit(token_df)
    .transform(token_df)
    .select([TEXT_COL, LABEL_COL, "features", "labelIdx", "weight"])
)

display(vec_df.limit(20))

Paso 4: Entrenamiento y evaluación del modelo

Con los datos implementados, defina el modelo. En esta sección, entrene un modelo de regresión logística para clasificar el texto vectorizado.

Preparación de conjuntos de datos de entrenamiento y prueba

# Split the dataset into training and testing
(train_df, test_df) = vec_df.randomSplit((0.8, 0.2), seed=42)

Seguimiento de experimentos de Machine Learning

Un experimento de aprendizaje automático es la unidad principal de organización y control para todas las ejecuciones de aprendizaje automático relacionadas. Una ejecución corresponde a una única ejecución del código del modelo.

El seguimiento de experimentos de aprendizaje automático administra todos los experimentos y sus componentes, como parámetros, métricas, modelos y otros artefactos. El seguimiento permite organizar todos los componentes necesarios de un experimento de aprendizaje automático específico. También permite la reproducción sencilla de los resultados pasados con experimentos guardados. Obtenga más información sobre los experimentos de aprendizaje automático en Microsoft Fabric.

# Build the logistic regression classifier
lr = (
    LogisticRegression()
    .setMaxIter(max_iter)
    .setFeaturesCol("features")
    .setLabelCol("labelIdx")
    .setWeightCol("weight")
)

Ajuste de hiperparámetros

Cree una cuadrícula de parámetros para buscar en los hiperparámetros. A continuación, cree un estimador de evaluador cruzado para generar un modelo CrossValidator:

# Build a grid search to select the best values for the training parameters
param_grid = (
    ParamGridBuilder()
    .addGrid(lr.regParam, [0.03, 0.1])
    .addGrid(lr.elasticNetParam, [0.0, 0.1])
    .build()
)

if len(LABELS) > 2:
    evaluator_cls = MulticlassClassificationEvaluator
    evaluator_metrics = ["f1", "accuracy"]
else:
    evaluator_cls = BinaryClassificationEvaluator
    evaluator_metrics = ["areaUnderROC", "areaUnderPR"]
evaluator = evaluator_cls(labelCol="labelIdx", weightCol="weight")

# Build a cross-evaluator estimator
crossval = CrossValidator(
    estimator=lr,
    estimatorParamMaps=param_grid,
    evaluator=evaluator,
    numFolds=k_folds,
    collectSubModels=True,
)

Evaluación del modelo

Podemos evaluar los modelos en el conjunto de datos de prueba para compararlos. Si un modelo se ha entrenado bien, debe demostrar un alto rendimiento en las métricas pertinentes al ejecutarlo con respecto a los conjuntos de datos de validación y prueba.

def evaluate(model, df):
    log_metric = {}
    prediction = model.transform(df)
    for metric in evaluator_metrics:
        value = evaluator.evaluate(prediction, {evaluator.metricName: metric})
        log_metric[metric] = value
        print(f"{metric}: {value:.4f}")
    return prediction, log_metric

Uso de MLflow para realizar un seguimiento de los experimentos

Inicie el proceso de entrenamiento y evaluación. Use MLflow para hacer un seguimiento de todos los experimentos y parámetros de registro, métricas y modelos. Toda esta información se registra bajo el nombre del experimento en el área de trabajo.

with mlflow.start_run(run_name="lr"):
    models = crossval.fit(train_df)
    best_metrics = {k: 0 for k in evaluator_metrics}
    best_index = 0
    for idx, model in enumerate(models.subModels[0]):
        with mlflow.start_run(nested=True, run_name=f"lr_{idx}") as run:
            print("\nEvaluating on test data:")
            print(f"subModel No. {idx + 1}")
            prediction, log_metric = evaluate(model, test_df)

            if log_metric[evaluator_metrics[0]] > best_metrics[evaluator_metrics[0]]:
                best_metrics = log_metric
                best_index = idx

            print("log model")
            mlflow.spark.log_model(
                model,
                f"{EXPERIMENT_NAME}-lrmodel",
                registered_model_name=f"{EXPERIMENT_NAME}-lrmodel",
                dfs_tmpdir="Files/spark",
            )

            print("log metrics")
            mlflow.log_metrics(log_metric)

            print("log parameters")
            mlflow.log_params(
                {
                    "word2vec_size": word2vec_size,
                    "min_word_count": min_word_count,
                    "max_iter": max_iter,
                    "k_folds": k_folds,
                    "DATA_FILE": DATA_FILE,
                }
            )

    # Log the best model and its relevant metrics and parameters to the parent run
    mlflow.spark.log_model(
        models.subModels[0][best_index],
        f"{EXPERIMENT_NAME}-lrmodel",
        registered_model_name=f"{EXPERIMENT_NAME}-lrmodel",
        dfs_tmpdir="Files/spark",
    )
    mlflow.log_metrics(best_metrics)
    mlflow.log_params(
        {
            "word2vec_size": word2vec_size,
            "min_word_count": min_word_count,
            "max_iter": max_iter,
            "k_folds": k_folds,
            "DATA_FILE": DATA_FILE,
        }
    )

Para ver los experimentos:

  1. En el navegador izquierdo, seleccione su área de trabajo.
  2. Busque y seleccione el nombre del experimento, en este caso sample_aisample-textclassification

Screenshot of an experiment.

Paso 5: puntuación y guardado de los resultados de predicción

Microsoft Fabric permite a los usuarios poner en marcha modelos de aprendizaje automático mediante una función escalable PREDICT. Esta función admite la puntuación por lotes (o la inferencia por lotes) en cualquier motor de proceso. Puede crear predicciones por lotes directamente desde un cuaderno o la página de elementos de un modelo determinado. Para más información acerca de PREDICT y su uso en Fabric, consulte Puntuación de modelos de Machine Learning con PREDICT en Microsoft Fabric.

A partir de los resultados de la evaluación anterior, el modelo 1 tiene las métricas más grandes de área bajo la curva de precisión-coincidencia (AUPRC) y área bajo la curva de característica operativa del receptor (AUC-ROC). Por lo tanto, debe usar el modelo 1 para la predicción.

La medida AUC-ROC se usa ampliamente para medir el rendimiento de los clasificadores binarios. Sin embargo, a veces es más adecuado evaluar el clasificador en función de las mediciones de AUPRC. El gráfico AUC-ROC muestra un gráfico que muestra el equilibrio entre la tasa de verdaderos positivos (TPR) y la tasa de falsos positivos (FPR). La curva AUPRC combina precisión (valor predictivo positivo o PPV) y coincidencia (tasa de verdaderos positivos o TPR) en una sola visualización.

# Load the best model
model_uri = f"models:/{EXPERIMENT_NAME}-lrmodel/1"
loaded_model = mlflow.spark.load_model(model_uri, dfs_tmpdir="Files/spark")

# Verify the loaded model
batch_predictions = loaded_model.transform(test_df)
batch_predictions.show(5)
# Code to save userRecs in the lakehouse
batch_predictions.write.format("delta").mode("overwrite").save(
    f"{DATA_FOLDER}/predictions/batch_predictions"
)
# Determine the entire runtime
print(f"Full run cost {int(time.time() - ts)} seconds.")