Esercitazione: Creare, valutare e assegnare un punteggio a un modello di classificazione del testo

Questa esercitazione presenta un esempio end-to-end di un flusso di lavoro di data science Synapse per un modello di classificazione del testo in Microsoft Fabric. Lo scenario usa word2vec e la regressione logistica, in Spark, per determinare il genere di un libro dal set di dati dei libri di British Library, esclusivamente in base al titolo del libro.

Questa esercitazione illustra questi passaggi:

  • Installare librerie personalizzate
  • Caricare i dati
  • Comprendere ed elaborare i dati con l'analisi esplorativa dei dati
  • Eseguire il training di un modello di Machine Learning con word2vec e regressione logistica e tenere traccia degli esperimenti con MLflow e la funzionalità di assegnazione automatica dell'infrastruttura
  • Caricare il modello di Machine Learning per l'assegnazione di punteggi e stime

Prerequisiti

  • Ottenere una sottoscrizione di Microsoft Fabric. In alternativa, iscriversi per ottenere una versione di valutazione gratuita di Microsoft Fabric.

  • Accedere a Microsoft Fabric.

  • Usare il commutatore esperienza sul lato sinistro della home page per passare all'esperienza di data science di Synapse.

    Screenshot of the experience switcher menu, showing where to select Data Science.

Seguire la procedura in un notebook

È possibile scegliere una di queste opzioni da seguire in un notebook:

  • Aprire ed eseguire il notebook predefinito nell'esperienza di data science di Synapse
  • Caricare il notebook da GitHub nell'esperienza di data science di Synapse

Aprire il notebook predefinito

Il notebook di classificazione del genere titolo di esempio accompagna questa esercitazione.

Per aprire il notebook di esempio predefinito dell'esercitazione nell'esperienza di data science di Synapse:

  1. Passare alla home page di Synapse Data Science.

  2. Selezionare Usa un esempio.

  3. Selezionare l'esempio corrispondente:

    • Dalla scheda predefinita Flussi di lavoro end-to-end (Python), se l'esempio è relativo a un'esercitazione su Python.
    • Dalla scheda Flussi di lavoro end-to-end (R), se l'esempio è relativo a un'esercitazione su R.
    • Dalla scheda Esercitazioni rapide, se l'esempio è per un'esercitazione rapida.
  4. Collegare una lakehouse al notebook prima di iniziare a eseguire il codice.

Importare il notebook da GitHub

AIsample - Title Genre Classification.ipynb è il notebook che accompagna questa esercitazione.

Per aprire il notebook a accompagnamento per questa esercitazione, seguire le istruzioni riportate in Preparare il sistema per le esercitazioni sull'analisi scientifica dei dati per importare il notebook nell'area di lavoro.

Se si preferisce copiare e incollare il codice da questa pagina, è possibile creare un nuovo notebook.

Assicurarsi di collegare un lakehouse al notebook prima di iniziare a eseguire il codice.

Passaggio 1: Installare librerie personalizzate

Per lo sviluppo di modelli di Machine Learning o l'analisi dei dati ad hoc, potrebbe essere necessario installare rapidamente una libreria personalizzata per la sessione di Apache Spark. Sono disponibili due opzioni per installare le librerie.

  • Usare le funzionalità di installazione inline (%pip o %conda) del notebook per installare una libreria solo nel notebook corrente.
  • In alternativa, è possibile creare un ambiente fabric, installare librerie da origini pubbliche o caricarvi librerie personalizzate e quindi l'amministratore dell'area di lavoro può collegare l'ambiente come predefinito per l'area di lavoro. Tutte le librerie nell'ambiente diventano quindi disponibili per l'uso in qualsiasi notebook e definizioni di processo Spark nell'area di lavoro. Per altre informazioni sugli ambienti, vedere Creare, configurare e usare un ambiente in Microsoft Fabric.

Per il modello di classificazione, usare la wordcloud libreria per rappresentare la frequenza delle parole nel testo, in cui la dimensione di una parola rappresenta la frequenza. Per questa esercitazione, usare %pip install per installare wordcloud nel notebook.

Nota

Il kernel PySpark viene riavviato dopo %pip install l'esecuzione. Installare le librerie necessarie prima di eseguire qualsiasi altra cella.

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

Passaggio 2: Caricare i dati

Il set di dati contiene i metadati relativi ai libri della British Library che una collaborazione tra la libreria e Microsoft ha digitalizzato. I metadati sono informazioni di classificazione per indicare se un libro è fittizio o non informativo. Con questo set di dati, l'obiettivo è eseguire il training di un modello di classificazione che determina il genere di un libro, solo in base al titolo.

BL record ID Tipo di risorsa Nome Date associate al nome Tipo di nome Role Tutti i nomi Title Titoli varianti Titolo serie Numero all'interno della serie Paese di pubblicazione Luogo della pubblicazione Publisher Data di pubblicazione Edition Descrizione fisica Classificazione dewey Segnaposto BL Argomenti Genre Lingue Note ID record BL per la risorsa fisica 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 Collegamento a un libro digitalizzato Annotato
014602826 Monografia Yearsley, Ann 1753-1806 persona Più, Hannah, 1745-1833 [persona]; Yearsley, Ann, 1753-1806 [person] Poesie in diverse occasioni [Con una lettera prefatoria di Hannah More.] England Londra 1786 Quarta edizione MANUSCRIPT nota Digital Store 11644.d.32 Inglese 003996603 Falso
014602830 Monografia A, T. persona Oldham, John, 1653-1683 [persona]; A, T. [person] Un Satyr contro Vertue. (Una poesia: doveva essere pronunciata da un Town-Hector [di John Oldham. La prefazione firmata: T. A.]) England Londra 1679 15 pagine (4°) Digital Store 11602.ee.10. (2.) Inglese 000001143 Falso

Definire i parametri seguenti in modo da poter applicare questo notebook in set di dati diversi:

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

Scaricare il set di dati e caricare nel lakehouse

Questo codice scarica una versione disponibile pubblicamente del set di dati e quindi la archivia in un lakehouse di Fabric.

Importante

Aggiungere una lakehouse al notebook prima di eseguirla. In caso contrario, verrà generato un errore.

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.")

Importare le librerie obbligatorie

Prima di qualsiasi elaborazione, è necessario importare le librerie necessarie, incluse le librerie per Spark e 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

Definire gli iperparametri

Definire alcuni iperparametri per il training del modello.

Importante

Modificare questi iperparametri solo se si comprende ogni parametro.

# 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

Avviare la registrazione del tempo necessario per eseguire questo notebook:

# Record the notebook running time
import time

ts = time.time()

Configurare il rilevamento dell'esperimento MLflow

L'assegnazione automatica estende le funzionalità di registrazione di MLflow. L'assegnazione automatica acquisisce automaticamente i valori dei parametri di input e le metriche di output di un modello di Machine Learning durante il training. Si registrano quindi queste informazioni nell'area di lavoro. Nell'area di lavoro è possibile accedere e visualizzare le informazioni con le API MLflow o l'esperimento corrispondente nell'area di lavoro. Per altre informazioni sull'assegnazione automatica dei tag, vedere l'articolo relativo all'assegnazione automatica in Microsoft Fabric.

# Set up Mlflow for experiment tracking

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

Per disabilitare l'assegnazione automatica di tag di Microsoft Fabric in una sessione del notebook, chiamare mlflow.autolog() e impostare disable=True:

Leggere i dati di data non elaborati dal lakehouse

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

Passaggio 3: Eseguire l'analisi esplorativa dei dati

Esplorare il set di dati con il display comando per visualizzare le statistiche di alto livello per il set di dati e visualizzare le visualizzazioni del grafico:

display(raw_df.limit(20))

Preparare i dati

Rimuovere i duplicati per pulire i dati:

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

display(df.limit(20))

Applicare il bilanciamento della classe per risolvere eventuali distorsioni:

# 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))

Suddividere i paragrafi e le frasi in unità più piccole per tokenizzare il set di dati. In questo modo, diventa più facile assegnare significato. Rimuovere quindi le parole non significative per migliorare le prestazioni. La rimozione delle parole non significative comporta la rimozione di parole che si verificano comunemente in tutti i documenti nel corpus. La rimozione delle parole non significative è uno dei passaggi di pre-elaborazione usati più comunemente nelle applicazioni NLP (Natural Language Processing).

# 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))

Visualizzare la libreria wordcloud per ogni classe. Una libreria wordcloud è una presentazione visivamente prominente delle parole chiave che vengono visualizzate di frequente nei dati di testo. La libreria wordcloud è efficace perché il rendering delle parole chiave costituisce un'immagine a colori cloudlike, per acquisire meglio i dati di testo principali a colpo d'occhio. Altre informazioni su wordcloud.

# 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")

Usare infine word2vec per vettorizzare il testo. La tecnica word2vec crea una rappresentazione vettoriale di ogni parola nel testo. Le parole usate in contesti simili, o che hanno relazioni semantiche, vengono acquisite in modo efficace attraverso la loro vicinanza nello spazio vettoriale. Questa vicinanza indica che parole simili hanno vettori di parole simili.

# 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))

Passaggio 4: Eseguire il training e valutare il modello

Con i dati sul posto, definire il modello. In questa sezione viene eseguito il training di un modello di regressione logistica per classificare il testo vettorializzato.

Preparare set di dati di training e test

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

Tenere traccia degli esperimenti di Machine Learning

Un esperimento di Machine Learning è l'unità principale dell'organizzazione e il controllo per tutte le esecuzioni di Machine Learning correlate. Un'esecuzione corrisponde a una singola esecuzione del codice del modello.

Il rilevamento degli esperimenti di Machine Learning gestisce tutti gli esperimenti e i relativi componenti, ad esempio parametri, metriche, modelli e altri artefatti. Il rilevamento consente l'organizzazione di tutti i componenti necessari di un esperimento di Machine Learning specifico. Consente inoltre di riprodurre facilmente i risultati precedenti con esperimenti salvati. Altre informazioni sugli esperimenti di Machine Learning in Microsoft Fabric.

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

Ottimizzare gli iperparametri

Creare una griglia di parametri per eseguire la ricerca sugli iperparametri. Compilare quindi uno strumento di stima incrociato per produrre un CrossValidator modello:

# 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,
)

Valutare il modello

È possibile valutare i modelli nel set di dati di test per confrontarli. Un modello con training corretto deve illustrare prestazioni elevate, sulle metriche pertinenti, quando vengono eseguite sui set di dati di convalida e test.

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

Tenere traccia degli esperimenti usando MLflow

Avviare il processo di training e valutazione. Usare MLflow per tenere traccia di tutti gli esperimenti e registrare parametri, metriche e modelli. Tutte queste informazioni vengono registrate sotto il nome dell'esperimento nell'area di lavoro.

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,
        }
    )

Per visualizzare gli esperimenti:

  1. Selezionare l'area di lavoro nel riquadro di spostamento a sinistra
  2. Trovare e selezionare il nome dell'esperimento, in questo caso sample_aisample-textclassification

Screenshot of an experiment.

Passaggio 5: Assegnare un punteggio e salvare i risultati della stima

Microsoft Fabric consente agli utenti di rendere operativi i modelli di Machine Learning con la PREDICT funzione scalabile. Questa funzione supporta l'assegnazione dei punteggi batch (o l'inferenza batch) in qualsiasi motore di calcolo. È possibile creare stime batch direttamente da un notebook o dalla pagina degli elementi per un determinato modello. Per altre informazioni su PREDICT e su come usarlo in Infrastruttura, vedere Assegnazione dei punteggi dei modelli di Machine Learning con PREDICT in Microsoft Fabric.

Dai risultati della valutazione precedente, il modello 1 ha le metriche più grandi per entrambe le metriche area sotto la curva AUPRC (Precision-Recall Curve) e per Area Under the Curve Receiver Operating Characteristic (AUC-ROC). Pertanto, è consigliabile usare il modello 1 per la stima.

La misura AUC-ROC viene ampiamente usata per misurare le prestazioni dei classificatori binari. Tuttavia, a volte diventa più appropriato valutare il classificatore in base alle misurazioni AUPRC. Il grafico AUC-ROC visualizza il compromesso tra il tasso positivo reale (TPR) e il tasso falso positivo (FPR). La curva AUPRC combina la precisione (valore predittivo positivo o PPV) e il richiamo (tasso positivo reale o TPR) in una singola visualizzazione.

# 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.")