次の方法で共有


チュートリアル: テキスト分類モデルの作成、評価、スコア付け

このチュートリアルでは、Microsoft Fabric でのテキスト分類モデルのエンドツーエンドの Synapse Data Science ワークフローについて説明します。 シナリオは、Spark で word2vec とロジスティック回帰を使用して、書籍のタイトルのみに基づいた大英図書館の書籍データセットから書籍のジャンルを決定します。

このチュートリアルでは次の手順を説明します。

  • カスタム ライブラリをインストールする
  • データの読み込み
  • 探索的データ分析によってデータを理解して処理する
  • word2vec とロジスティクス回帰を使用して機械学習モデルをトレーニングし、MLflow と Fabric の自動ログ機能を使用して実験を追跡する
  • スコアリングと予測のために機械学習モデルを読み込む

前提条件

ノートブックで作業を進める

ノートブックでこれらの後続のオプションのうちのいずれかを選択できます

  • Synapse Data Science 環境のビルトイン ノートブックを開いて実行します
  • GitHub から Synapse Data Science 環境にノートブックをアップロードします

組み込みのノートブックを開く

このチュートリアルには、サンプルの Title Genre Classification ノートブックが附属しています。

チュートリアルの組み込みのサンプル ノートブックを Synapse Data Science エクスペリエンスで開くには、次のようにします。

  1. Synapse Data Science のホーム ページに移動します。

  2. [サンプルの使用] を選択してください。

  3. 対応するサンプルを選択してください。

    • サンプルが Python チュートリアル用の場合は、既定の [エンド ツー エンド ワークフロー (Python)] タブから。
    • サンプルが R チュートリアル用の場合は、[エンド ツー エンド ワークフロー (R)] タブから。
    • サンプルがクイック チュートリアル用の場合は、[クイック チュートリアル] タブから。
  4. コードの実行を開始する前に、[レイクハウスをノートブックにアタッチします]

GitHub からノートブックをインポートする

AIsample - Title Genre Classification.ipynb は、このチュートリアルに付属するノートブックです。

このチュートリアルに付随するノートブックを開くには、「データ サイエンス用にシステムを準備する」チュートリアル の手順に従い、ノートブックをお使いのワークスペースにインポートします。

このページからコードをコピーして貼り付ける場合は、[新しいノートブックを作成する] ことができます。

コードの実行を開始する前に、必ずレイクハウスをノートブックにアタッチしてください。

手順 1: カスタム ライブラリをインストールする

機械学習モデルの開発またはアドホックなデータ分析には、Apache Spark セッション用のカスタム ライブラリをすばやくインストールすることが必要な場合があります。 ライブラリをインストールするには、2 つのオプションがあります。

  • ノートブックのインライン インストール機能 (%pip または %conda) を使って、現在のノートブックにのみライブラリをインストールします
  • または、Fabric 環境を作成し、パブリック ソースからライブラリをインストールするか、あるいはカスタム ライブラリをそこにアップロードすると、ワークスペース管理者はその環境をワークスペースの既定としてアタッチできます。 その後、環境内のすべてのライブラリが、ワークスペース内のすべてのノートブックと Spark ジョブ定義で使用できるようになります。 環境の詳細については、「Microsoft Fabric で環境を作成、構成、および使用する」を参照してください。

分類モデルでは、wordcloud ライブラリを使用してテキスト内の単語の頻度を表します。単語のサイズはその頻度を表します。 このチュートリアルでは、%pip install を使用して、ノートブックに wordcloud をインストールします。

Note

PySpark カーネルは、%pip install の実行後に再起動します。 他のセルを実行する前に必要なライブラリをインストールします。

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

手順 2: データを読み込む

データセットには、大英図書館と Microsoft のコラボレーションによるデジタル化された大英図書館の書籍に関するメタデータが含まれています。 メタデータは、書籍がフィクションかノンフィクションかを示す分類情報です。 このデータセットでは、タイトルのみに基づいて書籍のジャンルを決定する分類モデルをトレーニングすることが目標です。

BL レコード ID リソースの種類 名前 名前に関連付けられている日付 名前の種類 ロール すべての名前 敬称 異形タイトル シリーズ タイトル シリーズ内の番号 出版国 出版地 発行者 出版日 エディション 物理的な特徴 デューイ分類 BL の書架記号 Topics Genre 言語 メモ 物理リソースの BL レコード ID 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 デジタル化された書籍へのリンク 注釈付き
014602826 モノグラフ Yearsley, Ann 1753-1806 person More, Hannah, 1745-1833 [person]; Yearsley, Ann, 1753-1806 [person] Poems on several occasions [With a prefatory letter by Hannah More.] England ロンドン 1786 第 4 版 デジタル ストア 11644.d.32 English 003996603 誤り
014602830 モノグラフ A, T. person Oldham, John, 1653-1683 [person]; A, T. [person] A Satyr against Vertue. (A poem: supposed to be spoken by a Town-Hector [By John Oldham. The preface signed: T. A.]) England ロンドン 1679 15 ページ (4°) デジタル ストア 11602.ee.10. (2.) English 000001143 False

次のパラメーターを定義して、このノートブックをさまざまなデータセットに簡単に適用できます。

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

データセットをダウンロードしてレイクハウスにアップロードする

このコードは、公開されているバージョンのデータセットをダウンロードし、Fabric Lakehouse に格納します。

重要

実行する前に、ノートブックに [レイクハウスを追加する]。 間違ってインストールすると、エラーが発生します。

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

必要なライブラリをインポートする

処理の前に、Spark および 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

ハイパーパラメーターを定義する

モデルのトレーニングのためにハイパーパラメーターを定義する。

重要

各パラメーターを理解している場合にのみ、これらのハイパーパラメーターを変更してください。

# 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

次のノートブックの実行に必要な時間の記録を開始します。

# Record the notebook running time
import time

ts = time.time()

MLflow 実験追跡を設定する

自動ログは、MLflow ログ機能を拡張します。 自動ログ記録は、機械学習モデルをトレーニングするときに、そのモデルの入力パラメータ値と出力メトリックを自動的にキャプチャします。 その後、この情報をワークスペースに記録します。 ワークスペースでは、MLflow API または対応する実験を使用して、ワークスペース内の情報にアクセスして視覚化できます。 自動ログ機能の詳細については、「Microsoft Fabric での自動ログ記録」を参照してください。

# Set up Mlflow for experiment tracking

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

ノートブック セッションで Microsoft Fabric の自動ログ記録を無効にするには、mlflow.autolog() を呼び出して disable=True を設定します。

レイクハウスから日付の生データを読み取る

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

手順 3: 探索的データ分析を実行する

display コマンドを使用してデータセットを探索し、データセットの高度な統計情報を表示し、グラフ ビューを表示します。

display(raw_df.limit(20))

データを準備する

重複を削除してデータをクリーンにします。

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

display(df.limit(20))

バイアスに対処するためにクラス均衡を適用します。

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

データセットをトークン化するために、段落と文をより小さな単位に分割します。 このようにすると、意味を割り当てるのが簡単になります。 次に、ストップワードを削除してパフォーマンスを向上させます。 ストップワードの削除には、コーパス内のすべてのドキュメントで一般的に発生する単語の削除が含まれます。 ストップワードの削除は、自然言語処理 (NLP) アプリケーションで最もよく使用される前処理手順の 1 つです。

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

各クラスの wordcloud ライブラリを表示します。 wordcloud ライブラリは、テキスト データに頻出するキーワードを視覚的に目立つように提示したものです。 wordcloud ライブラリでは、キーワードのレンダリングが雲のようなカラー画像で表示されるため、メインテキストデータを一目で把握できるために効果的です。 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")

最後に、テキストをベクター化するために word2vec を使用します。 word2vec 手法は、テキスト内の各単語のベクター表現を作成します。 同様のコンテキストで使用される単語やセマンティック リレーションシップを持つ単語は、ベクター空間内での近接性によって効果的にキャプチャされます。 この近接性は、類似した単語に類似の単語ベクトルがあることを示します。

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

手順 4: モデルをトレーニングして評価する

データを配置したら、モデルを定義します。 このセクションでは、ベクトル化されたテキストを分類するロジスティック回帰モデルをトレーニングします。

トレーニング用およびテスト用のデータセットを準備する

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

機械学習の実験を追跡する

機械学習の "実験" は、関連するすべての機械学習の実行を編成および制御するための主要な単位です。 "実行" はモデル コードの 1 回の実行に対応します。

機械学習の "実験追跡" では、パラメータ、メトリック、モデル、その他の成果物など、すべての実験とそのコンポーネントを管理します。 追跡により、特定の機械学習実験に必要なすべてのコンポーネントを整理できます。 また、保存された実験を使用して過去の結果を簡単に再現することもできます。 Microsoft Fabric での機械学習の実験の詳細について説明します

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

ハイパーパラメーターを調整する

ハイパーパラメーターを検索するパラメーターのグリッドを構築します。 次に、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,
)

モデルを評価する

テスト データセットのモデルを評価して比較できます。 モデルが十分にトレーニングされていると、検証データセットとテスト データセットに対して実行したときに、関連メトリックでハイ パフォーマンスを示すはずです。

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

MLflow を使用した実験の追跡

トレーニングと評価のプロセスを開始します。 MLflow を使用して、すべての実験とログ パラメータ、メトリック、モデルを追跡します。 この情報はすべて、ワークスペースの実験名の下に記録されます。

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

実験を表示するには:

  1. 左側のナビゲーションで、ワークスペースを選択します
  2. 実験名 (この場合は、sample_aisample-textclassification) を見つけて選択します

Screenshot of an experiment.

手順 5: 予測結果をスコア付けして保存する

Microsoft Fabric では、ユーザーが PREDICT スケーラブルな関数を使って機械学習モデルを運用化することができます。 この関数は、任意のコンピューティング エンジンでのバッチ スコアリング (またはバッチ推論) をサポートしています。 バッチ予測は、特定のモデルのノートブックまたは項目ページから直接作成できます。 PREDICT とそれを Fabric で使用する方法の詳細については、Microsoft Fabric での PREDICT を使用した機械学習モデルのスコアリングに関するページを参照してください。

上記の評価結果から、モデル 1 は、PR 曲線 (AUPRC) と RPC 曲線 (AUC-ROC)の両方について、最大のメトリックを持ちます。 したがって、予測にはモデル 1 を使用するべきです。

AUC-ROC 測定は、バイナリ分類子のパフォーマンスを測定するために広く使用されています。 ただし、AUPRC の測定に基づいて分類子を評価する方が適切な場合もあります。 AUC-ROC グラフは、真陽性率 (TPR) と偽陽性率 (FPR) のトレードオフを視覚化します。 AUPRC 曲線は、精度 (正の予測値または PPV) と再現率 (真陽性率または TPR) を組み合わせて視覚化した 1 本の曲線です。

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