Tutoriel : Créer, évaluer et noter un modèle de classification de texte

Ce tutoriel présente un exemple de bout en bout d’un flux de travail de science des données Synapse pour un modèle de classification de texte dans Microsoft Fabric. Le scénario utilise word2vec et la régression logistique sur Spark pour déterminer le genre d’un livre à partir du jeu de données de la British library, uniquement en fonction du titre du livre.

Ce didacticiel couvre ces étapes :

  • Installer des bibliothèques personnalisées
  • Chargement des données
  • Comprendre et traiter les données par le biais d’une analyse exploratoire des données
  • Effectuer l’apprentissage d’un modèle Machine Learning à l’aide de word2vec et de la régression logistique et suivre des expériences avec MLflow et la fonctionnalité de journalisation automatique Fabric
  • Charger le modèle Machine Learning pour le scoring et les prédictions

Prérequis

Suivre dans un notebook

Vous pouvez choisir l’une de ces options pour suivre l’évolution de la situation dans un notebook :

  • Ouvrez et exécutez le notebook intégré dans l’expérience science des données Synapse
  • Chargez votre notebook à partir de GitHub vers l’expérience science des données Synapse

Ouvrir le notebook intégré

L’exemple de notebook Classification du genre du titre accompagne ce tutoriel.

Pour ouvrir l’exemple de notebook intégré du tutoriel dans l’expérience de science des données Synapse :

  1. Accédez à la page d’accueil science des données Synapse.

  2. Sélectionnez Utiliser un échantillon.

  3. Sélectionnez l’échantillon correspondant :

    • À partir de l’onglet par défaut Workflows de bout en bout (Python), si l’exemple concerne un tutoriel Python.
    • À partir de l’onglet Workflows de bout en bout (R), si l’exemple concerne un tutoriel R.
    • À partir de l’onglet Tutoriels rapides, si l’exemple concerne un tutoriel rapide.
  4. Attachez un lakehouse au notebook avant de commencer à exécuter le code.

Importer un notebook à partir de GitHub

AIsample - Title Genre Classification.ipynb est le notebook qui accompagne ce didacticiel.

Pour ouvrir le notebook accompagnant ce tutoriel, suivez les instructions fournies dans Préparer votre système pour le tutoriel sur la science des données afin d’importer les notebooks dans votre espace de travail.

Si vous préférez copier et coller le code de cette page, vous pouvez créer un nouveau notebook.

Assurez-vous d’attacher un lakehouse au notebook avant de commencer à exécuter du code.

Étape 1 : Installer des bibliothèques personnalisées

Pour le développement de modèle Machine Learning ou l’analyse de données ad hoc, vous pourriez avoir besoin d’installer rapidement une bibliothèque personnalisée pour votre session Apache Spark. Vous avez deux options d’installation des bibliothèques.

  • Utilisez les capacités d’installation en ligne (%pip ou %conda) de votre notebook pour installer une bibliothèque, dans votre notebook actuel uniquement
  • Vous pouvez également créer un environnement Fabric, installer des bibliothèques à partir de sources publiques ou y charger des bibliothèques personnalisées. Ensuite, l’administrateur de votre espace de travail peut attacher l’environnement en tant qu’environnement par défaut pour l’espace de travail. Toutes les bibliothèques de l’environnement seront ensuite disponibles pour être utilisées dans les notebooks et les définitions de travail Spark dans l’espace de travail. Pour plus d’informations sur les environnements, consultez Créer, configurer et utiliser un environnement dans Microsoft Fabric.

Pour le modèle de classification, utilisez la bibliothèque wordcloud pour représenter la fréquence des mots dans un texte où la taille d’un mot représente sa fréquence. Pour ce tutoriel, utilisez %pip install pour installer wordcloud dans votre notebook.

Remarque

Le noyau PySpark redémarre après l’exécution de %pip install. Installez les bibliothèques nécessaires avant d’exécuter d’autres cellules.

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

Étape 2 : Chargement des données

Le jeu de données contient des métadonnées sur les livres de la British Library qui ont été numérisés dans le cadre d’une collaboration entre la bibliothèque et Microsoft. Les métadonnées sont des informations de classification pour indiquer si un livre est de fiction ou non. Avec ce jeu de données, l’objectif est d’entraîner un modèle de classification qui détermine le genre d’un livre, uniquement en fonction de son titre.

ID d’enregistrement BL Type de ressource Nom Dates associées au nom Type de nom Rôle tous les noms Titre Titres variables Titre de la série Nombre dans la série Pays de publication Lieu de publication Serveur de publication Date de publication Édition Description physique Classification Dewey BL shelfmark Rubriques Genre Langages Notes ID d’enregistrement BL pour la ressource physique 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 Lien vers un livre numérisé Annotée
014602826 Monographie Yearsley, Ann 1753-1806 personne Plus, Hannah, 1745-1833 [personne]; Yearsley, Ann, 1753-1806 [personne] Poèmes à plusieurs reprises [Avec une lettre préliminaire d’Hannah More.] England London 1786 Note MANUSCRITE de la quatrième édition Magasin numérique 11644.d.32 Anglais 003996603 False
014602830 Monographie A, T. personne Oldham, John, 1653-1683 [personne]; A, T. [personne] A Satyr against Vertue. (Un poème : censé être prononcé par un Town-Hector [Par John Oldham. Préface signée : T. A.]) England London 1679 15 pages (4°) Magasin numérique 11602.ee.10. (2.) Anglais 000001143 False

Définissez les paramètres suivants pour appliquer ce notebook à différents jeux de données :

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

Télécharger le jeu de données et le charger dans le lakehouse

Ce code télécharge une version disponible publiquement du jeu de données, puis la stocke dans un lakehouse Fabric.

Important

Ajoutez un lakehouse au notebook avant de l’exécuter. Dans le cas contraire, vous obtiendrez une erreur.

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

Importer les bibliothèques nécessaires

Avant tout traitement, vous devez importer les bibliothèques requises, y compris celles pour Spark et 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

Définir des hyperparamètres

Définissez certains hyperparamètres pour l’entraînement du modèle.

Important

Modifiez ces hyperparamètres uniquement si vous comprenez chaque paramètre.

# 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

Commencer à enregistrer le temps nécessaire pour exécuter ce notebook :

# Record the notebook running time
import time

ts = time.time()

Configurer le suivi des expériences MLflow

La journalisation automatique étend les fonctionnalités de journalisation MLflow. La journalisation automatique capture automatiquement les valeurs des paramètres d’entrée et les métriques de sortie d’un modèle Machine Learning au fur et à mesure que vous l’entraînez. Vous consignez ensuite ces informations dans l’espace de travail. Dans l’espace de travail, vous pouvez accéder aux informations et les visualiser avec les API MLflow, ou l’expérience correspondante. Pour en savoir plus sur l’autologging, consultez autologging dans Microsoft Fabric.

# Set up Mlflow for experiment tracking

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

Pour désactiver la journalisation automatique de Microsoft Fabric dans une session de notebook, appelez mlflow.autolog() et définissez disable=True :

Lire les données de date brutes de la lakehouse

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

Étape 3 : Effectuer une analyse exploratoire des données

Explorez le jeu de données à l’aide de la commande display pour afficher les statistiques générales du jeu de données et afficher les vues de graphique :

display(raw_df.limit(20))

Préparer les données

Supprimez les doublons pour nettoyer les données :

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

display(df.limit(20))

Appliquez l’équilibrage de classe pour résoudre tout biais :

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

Fractionnez les paragraphes et les phrases en unités plus petites pour tokeniser le jeu de données. De cette façon, il devient plus facile d’attribuer une signification. Ensuite, supprimez les mots vides pour améliorer les performances. La suppression de mots vides implique la suppression de mots qui se produisent généralement dans tous les documents du corpus. La suppression de mots vides est l’une des étapes de prétraitement les plus couramment utilisées dans les applications de traitement du langage naturel (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))

Affichez la bibliothèque wordcloud pour chaque classe. Une bibliothèque wordcloud est une présentation visuellement importante des mots clés qui apparaissent fréquemment dans les données de texte. La bibliothèque wordcloud est efficace, car le rendu des mots clés forme une image de couleur de type cloud pour mieux capturer les données de texte principales en un clin d’œil. En savoir plus sur 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")

Enfin, utilisez word2vec pour vectoriser le texte. La technique word2vec crée une représentation vectorielle de chaque mot dans le texte. Les mots utilisés dans des contextes similaires ou ayant des relations sémantiques sont capturés efficacement par leur proximité dans l’espace vectoriel. Cette proximité indique que les mots similaires ont des vecteurs de mots similaires.

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

Étape 4 : Effectuer l’apprentissage et évaluer le modèle

Avec les données en place, définissez le modèle. Dans cette section, vous entraînez un modèle de régression logistique pour classifier le texte vectorisé.

Préparation de jeux de données d’apprentissage et de test

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

Suivre les expériences d’apprentissage automatique

Une expérience de Machine Learning est l’unité principale d’organisation et de contrôle pour tous les cycles d’apprentissage automatique connexes. Une exécution correspond à une seule exécution de code de modèle.

Le suivi des expériences d’apprentissage automatique gère toutes les expériences et leurs composants, tels que les paramètres, les métriques, les modèles et d’autres artefacts. Le suivi permet d’organiser tous les composants requis d’une expérience d’apprentissage automatique spécifique. Il permet également la reproduction facile des résultats passés avec des expériences enregistrées. En savoir plus sur les expériences d’apprentissage automatique dans Microsoft Fabric.

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

Optimiser les hyperparamètres

Générez une grille de paramètres pour effectuer une recherche sur les hyperparamètres. Créez ensuite un estimateur d’évaluateur croisé pour produire un modèle 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,
)

Évaluer le modèle

Nous pouvons évaluer les modèles sur le jeu de données de test pour les comparer. Un modèle bien entraîné doit démontrer des performances élevées sur les indicateurs de performance pertinents lorsqu’il est exécuté sur les jeux de données de validation et de 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

Suivre les expériences à l’aide de MLflow

Démarrez le processus d’entraînement et d’évaluation. Utilisez MLflow pour suivre toutes les expériences et les paramètres de journalisation, les métriques et les modèles. Toutes ces informations sont consignées sous le nom de l’expérience dans l’espace de travail.

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

Pour afficher vos expériences :

  1. Sur la gauche, sélectionnez votre espace de travail.
  2. Recherchez et sélectionnez le nom de l’expérience, dans ce cas sample_aisample-textclassification

Screenshot of an experiment.

Étape 5 : Noter et enregistrer les résultats de prédiction

Microsoft Fabric permet aux utilisateurs d’opérationnaliser des modèles Machine Learning à l’aide de la fonction évolutive PREDICT. Cette fonction prend en charge le scoring par lots (ou l’inférence par lot) dans n’importe quel moteur de calcul. Vous pouvez créer des prédictions par lots directement à partir d’un bloc-notes ou de la page d’élément d’un modèle particulier. Pour en savoir plus sur PREDICT et comment l’utiliser dans Fabric, consultez Scoring de modèle Machine Learning avec PREDICT dans Microsoft Fabric.

À partir des résultats d’évaluation ci-dessus, le modèle 1 a les plus grandes métrique pour AUPRC (Area Under the Precision-Recall Curve) et AUC-ROC (Area Under the Curve Receiver Operating Characteristic). Vous devez donc utiliser le modèle 1 pour la prédiction.

La mesure AUC-ROC est largement utilisée pour mesurer les performances des classifieurs binaires. Toutefois, il est parfois plus approprié d’évaluer le classifieur en fonction de la mesure AUPRC. Le graphique AUC-ROC visualise le compromis entre le taux de vrais positifs (TPR) et le taux de faux positifs (FPR). La courbe AUPRC combine la précision (valeur prédictive positive ou PPV) et le rappel (taux de vrais positifs ou TPR) dans une visualisation unique.

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