Condividi tramite


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

Questa esercitazione presenta un esempio end-to-end di un flusso di lavoro di data science Synapse per un modello di classificazione testo in Microsoft Fabric. Lo scenario usa sia l'elaborazione del linguaggio naturale word2vec (NLP) che la regressione logistica, in Spark, per determinare il genere di un libro dal set di dati dei libri di British Library. La determinazione si basa esclusivamente sul titolo del libro.

L'esercitazione illustra questi passaggi:

  • Installare librerie personalizzate
  • Caricare i dati
  • Comprendere ed elaborare i dati con l'analisi esplorativa dei dati
  • Addestra un modello di machine learning utilizzando sia Word2vec per l'elaborazione del linguaggio naturale (NLP) che la regressione logistica, e tieni traccia degli esperimenti con MLflow e la funzionalità di autologging di Fabric.
  • Caricare il modello di Machine Learning per l'assegnazione di punteggi e previsioni

Prerequisiti

Seguire la procedura in un notebook

Per seguire la procedura in un notebook, sono disponibili queste opzioni:

  • Aprire ed eseguire il notebook predefinito.
  • Carica il tuo notebook da GitHub.

Aprire il notebook predefinito

Il notebook di esempio per la classificazione dei generi del titolo accompagna questa esercitazione.

  1. Per aprire il notebook di esempio per questa esercitazione, seguire le istruzioni riportate in Preparare il sistema per le esercitazioni sull'analisi scientifica dei dati.

  2. Assicurarsi di 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.

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 una libreria.

  • Per installare una libreria, solo nel notebook corrente, usare le funzionalità di installazione inline (%pip o %conda) del notebook.
  • In alternativa, è possibile creare un ambiente fabric e installare librerie da origini pubbliche o caricarvi librerie personalizzate. L'amministratore dell'area di lavoro può quindi collegare l'ambiente come predefinito per l'area di lavoro. A questo punto, tutte le librerie nell'ambiente diventano disponibili per l'uso in tutti i notebook e in tutte le definizioni di processo Spark in tale area di lavoro. Per altre informazioni sugli ambienti, visitare la pagina relativa alla creazione, alla configurazione e all'uso di un ambiente nella risorsa di Microsoft Fabric .

Per il modello di classificazione, usare la wordcloud libreria per rappresentare la frequenza delle parole nel testo. Nelle wordcloud risorse 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 l'esecuzione di %pip install. 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 dei libri della British Library contiene i metadati relativi ai libri della British Library. Una collaborazione tra la libreria e Microsoft ha digitalizzato le risorse originali che sono diventate il set di dati. I metadati sono informazioni di classificazione che indicano se un libro è fittizio o meno. Il diagramma seguente mostra un esempio di riga del set di dati.

ID di record BL Tipo di risorsa Nome Date associate al nome Tipo di nome Ruolo Tutti i nomi Titolo Titoli variante Titolo serie Numero all'interno della serie Paese di pubblicazione Luogo di pubblicazione Editore Data di pubblicazione Edizione Descrizione fisica Classificazione dewey Segnaposto BL Argomenti Genere Lingue Note ID del record BL per la risorsa fisica ID classificazione ID_utente creato_il subject_ids annotator_date_pub annotatore_data_normalizzata_pub dichiarazione_di_edizione_annotatore annotatore_genere annotatore_TERMINI_genere_FAST Termini soggetto annotatore FAST commenti dell'annotatore lingua_principale_annotatore Sommari delle annotazioni di altre lingue riassunti_lingua_annotatore traduzione_annotatore lingua originale annotatore annotator_publisher annotator_place_pub annotator_country titolo_annotatore Collegamento a un libro digitalizzato Annotato
014602826 Monografia Ann Yearsley 1753-1806 persona More, Hannah, 1745-1833 [persona]; Yearsley, Ann, 1753-1806 [persona] Poesie in diverse occasioni [Con una lettera prefatoria di Hannah More.] Inghilterra Londra 1786 Nota manoscritto quarta edizione Digital Store 11644.d.32 italiano 003996603 Falso
014602830 Monografia A, T. persona Oldham, John, 1653-1683 [persona]; A, T. [persona] Un Satyr contro Vertue. (Una poesia: si suppone di essere pronunciata da un Town-Hector [Di John Oldham. La prefazione firmata: T. A.]) Inghilterra Londra 1679 15 pagine (4°) Digital Store 11602.ee.10. (2) italiano 000001143 Falso

Con questo set di dati, l'obiettivo è eseguire il training di un modello di classificazione che determina il genere di un libro, in base solo al titolo del libro.

Definire i parametri seguenti per applicare questo notebook a 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 caricarlo nel lakehouse

Il frammento di codice seguente 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 eseguirlo. In caso contrario, si verifica 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.z13.web.core.windows.net/data/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

Il frammento di codice seguente definisce gli iperparametri necessari 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 di MLflow

La registrazione automatica estende le funzionalità di registrazione di MLflow. La registrazione 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 ulteriori informazioni sulla registrazione automatica, visitare la risorsa Autologging in Microsoft Fabric.

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

# Set up Mlflow for experiment tracking

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

Leggere i dati non elaborati di data 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 comando display 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

Per pulire i dati, rimuovere i duplicati:

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

Per tokenizzare il set di dati, suddividere i paragrafi e le frasi in unità più piccole. 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 compaiono comunemente in tutti i documenti del corpus. La rimozione delle parole non significative è uno dei passaggi di pre-elaborazione usati più comunemente nelle applicazioni di elaborazione del linguaggio naturale (NLP). Il frammento di codice seguente illustra questi passaggi:

# 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 presenta parole chiave che vengono visualizzate di frequente nei dati di testo, è una presentazione visivamente prominente. La libreria di nuvole di parole è efficace perché la renderizzazione delle parole chiave costituisce un'immagine a colori simile a una nuvola, per catturare meglio i dati di testo principali a prima vista. Visitare questa risorsa per altre informazioni su wordcloud.

Il frammento di codice seguente illustra questi passaggi:

# 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 NLP per vettorizzare il testo. La tecnica NLP 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. Il frammento di codice seguente illustra questi passaggi:

# 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 disponibili, definire il modello. In questa sezione viene eseguito il training di un modello di regressione logistica per classificare il testo vettorializzato.

Preparare i set di dati di training e di test

Il frammento di codice seguente suddivide il set di dati:

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

Rilevamento degli esperimenti di Machine Learning

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 abilita l'organizzazione e la gestione di tutti i componenti richiesti da un esperimento di Machine Learning specifico. Consente inoltre di riprodurre facilmente i risultati precedenti con esperimenti salvati. Per altre informazioni, vedere Esperimenti di Machine Learning in Microsoft Fabric .

Un esperimento di Machine Learning è l'unità primaria di organizzazione e controllo per tutte le esecuzioni di Machine Learning correlate. Un'esecuzione corrisponde a una singola esecuzione del codice dei modelli. Il frammento di codice seguente illustra questi passaggi:

# 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. Costruire quindi uno stimatore di valutazione incrociata per produrre un CrossValidator modello, come illustrato nel frammento di codice seguente.

# 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 correttamente addestrato dovrebbe dimostrare elevate prestazioni, sulle metriche pertinenti, quando viene eseguito contro i dataset di convalida e di test. Il frammento di codice seguente illustra questi passaggi:

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

Rilevamento degli esperimenti tramite MLflow

Avviare il processo di training e valutazione. Usare MLflow per tenere traccia di tutti gli esperimenti e registrare i parametri, le metriche e i modelli. Nell'area di lavoro tutte queste informazioni vengono registrate con il nome dell'esperimento. Il frammento di codice seguente illustra questi passaggi:

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 un'area di lavoro nel riquadro di spostamento a sinistra
  2. Trovare e selezionare il nome dell'esperimento, in questo caso sample_aisample-textclassification

Screenshot di un esperimento.

Passaggio 5: assegnare un punteggio e salvare i risultati della previsione

Microsoft Fabric consente agli utenti di rendere operativi i modelli di Machine Learning con la funzione scalabile PREDICT . 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 sulla PREDICT funzione e su come usarla in Infrastruttura, vedere Assegnazione dei punteggi dei modelli di Machine Learning con PREDICT in Microsoft Fabric.

Dai risultati della valutazione, il modello 1 ha le metriche più grandi per entrambe le aree nella curva di Precision-Recall (AUPRC) e per Area sotto la caratteristica operativa del ricevitore della curva (AUC-ROC). Pertanto, è consigliabile usare il modello 1 per la previsione.

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. I frammenti di codice seguenti illustrano questi passaggi:

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