前編から引き続き、Microsoftが提供しているSaaS型のサービスであるMicrosoft Fabricを使用して、機械学習を用いた予測レポートを作成してみたいと思います。
本記事はMicrosoft Learnの以下の記事を参考に作業していきます。
データ サイエンス チュートリアル - 概要 - Microsoft Fabric | Microsoft Learn
こちらの記事は中編となり、前編の記事は以下となります。
作業の概要
用意されたノートブックを使用して、以下の内容のことを実施していきます。
事前準備前編で実施データの取得前編で実施データの加工(クレンジング)と保存前編で実施- 機械学習モデルのトレーニングと登録
- 予測データの作成
- 予測データの視覚化(レポート作成)
公開されているテストデータ(銀行の顧客データ)をもとに、機械学習モデルを使用して顧客の解約状況を予測を立てるというのが目的となっています。
この中編記事では、機械学習モデルのトレーニングと登録を行います。
機械学習モデルのトレーニングと登録
作業内容
マイワークスペースから3-train-evaluate.ipynbを選択します。 このノートブックでは以下のようなことが行われます。
- ライブラリのインストール
- 機械学習モデルとトレーニングのための前準備
- 各種データセットの準備
- マイノリティクラスの新しいサンプルの合成
- モデルのトレーニング
- トレーニング済みモデルのパフォーマンス評価
- トレーニング済みモデルの精度の評価
「すべて実行」を選択します。
最後のコードまでエラーが発生せず完了することを確認します。
※ 一部のコードでWarningのメッセージが発生しますが、無視して大丈夫です。
ライブラリのインストール
imbalanced-learnは、不均衡なデータセットを処理するときに使用される SMOTE (Synthetic Minority Oversampling Technique) のライブラリです。 マイノリティクラスの新しいサンプルの合成で使用します。
pipコマンドを使用してインストールします。
# SMOTEのためにimbalanced-learnのインストール
%pip install imblearn
なお、ノートブックにてライブラリをインストールする場合、そのライブラリはノートブックのセッション中でのみ使用できます。 そのノートブックを再起動した場合などは、再度インストールする必要があります。
よく使用するライブラリがある場合は、そのライブラリをインストールしたFabric環境を作成した方がよいでしょう。
機械学習モデルとトレーニングのための前準備
前準備
前準備として3つの事を行っています。
1) データの読み込み 前編で作成したクレンジング済みのデータをレイクハウスからデルタ テーブルに読み込みます。
import pandas as pd SEED = 12345 df_clean = spark.read.format("delta").load("Tables/df_clean").toPandas()
2) モデルの追跡とログ記録のための実験を生成する MLflow*1のライブラリをインポートします。 その後、bank-churn-experimentという名前で実験を作成して自動ログ機能を設定します。
import mlflow # 実験名の設定 EXPERIMENT_NAME = "bank-churn-experiment" # MLflow experiment name mlflow.set_experiment(EXPERIMENT_NAME) # 自動ログ機能の設定 mlflow.autolog(exclusive=False)
3) 機械学習モデルのライブラリのインポート ランダム フォレストと LightGBM のトレーニングのために、scikit-learn と LightGBM をインポートします。
# ライブラリのインポート from sklearn.model_selection import train_test_split from lightgbm import LGBMClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, f1_score, precision_score, confusion_matrix, recall_score, roc_auc_score, classification_report
補足:用語解説
ランダムフォレスト
バギングと決定木を使用するアンサンブル学習のアルゴリズムです。
サンプル抽出した複数のデータ群に対してそれぞれ決定木モデルを構築します。
また、それぞれのモデルを構築する際に一部の学習データを使用せず、分割する特徴量をランダムに決定することで、互いに相関の低い複数の決定木モデルが生成されます。
LightGBM
決定木の勾配ブースティングアルゴリズムの一つです。
最初の決定木モデルの出力結果と正解値との誤差を計算し,二つ目の決定木ではその誤差を埋めるような値を学習します。これを何度も繰り返して誤差を小さくしていき、最終的な予測値を求めます。
他の決定木の勾配ブースティングアルゴリズムと比べて高速化が図られており、学習スピードが早いのが特徴です。
各種データセットの準備
クレンジング済みのデータをトレーニング、検証、およびテスト セットに分割します。
y = df_clean["Exited"] X = df_clean.drop("Exited",axis=1) # Split the dataset to 60%, 20%, 20% for training, validation, and test datasets # トレーニングデータとテストデータに分割 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=SEED) # トレーニングデータと検証データに分割 X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=SEED) # テストデータの保存 table_name = "df_test" # Create PySpark DataFrame from Pandas df_test=spark.createDataFrame(X_test) df_test.write.mode("overwrite").format("delta").save(f"Tables/{table_name}") print(f"Spark test DataFrame saved to delta table: {table_name}")
マイノリティクラスの新しいサンプルの合成
今回のサンプルデータでは、10,000 人の顧客のうち解約した顧客は 2,037 人 (約 20%) 程となります。
これは、データセットが非常に不均衡で、モデルが決定境界を効果的に学習するにはマイノリティ クラスの例が少なすぎることになります。そのため、 SMOTE を使用して、マイノリティ クラスの新しいサンプルを合成します。
なお、SMOTE はトレーニング データセットにのみ適用します。 元のデータに対する機械学習モデルのパフォーマンスの有効な近似を得るには、テスト データセットは元の不均衡な分布のままにしておく必要があります。
from collections import Counter from imblearn.over_sampling import SMOTE sm = SMOTE(random_state=SEED) X_res, y_res = sm.fit_resample(X_train, y_train) new_train = pd.concat([X_res, y_res], axis=1)
モデルのトレーニング
トレーニング
3つのモデルに対してトレーニングを行います。
1) 最大深度が 4 で 4 つの特徴を持つランダム フォレスト
mlflow.sklearn.autolog(registered_model_name='rfc1_sm') # Register the trained model with autologging rfc1_sm = RandomForestClassifier(max_depth=4, max_features=4, min_samples_split=3, random_state=1) # Pass hyperparameters with mlflow.start_run(run_name="rfc1_sm") as run: rfc1_sm_run_id = run.info.run_id # Capture run_id for model prediction later print("run_id: {}; status: {}".format(rfc1_sm_run_id, run.info.status)) # rfc1.fit(X_train,y_train) # Imbalanaced training data rfc1_sm.fit(X_res, y_res.ravel()) # Balanced training data rfc1_sm.score(X_val, y_val) y_pred = rfc1_sm.predict(X_val) cr_rfc1_sm = classification_report(y_val, y_pred) cm_rfc1_sm = confusion_matrix(y_val, y_pred) roc_auc_rfc1_sm = roc_auc_score(y_res, rfc1_sm.predict_proba(X_res)[:, 1])
2) 最大深度が 8 で 6 つの特徴を持つランダム フォレスト
mlflow.sklearn.autolog(registered_model_name='rfc2_sm') # Register the trained model with autologging rfc2_sm = RandomForestClassifier(max_depth=8, max_features=6, min_samples_split=3, random_state=1) # Pass hyperparameters with mlflow.start_run(run_name="rfc2_sm") as run: rfc2_sm_run_id = run.info.run_id # Capture run_id for model prediction later print("run_id: {}; status: {}".format(rfc2_sm_run_id, run.info.status)) # rfc2.fit(X_train,y_train) # Imbalanced training data rfc2_sm.fit(X_res, y_res.ravel()) # Balanced training data rfc2_sm.score(X_val, y_val) y_pred = rfc2_sm.predict(X_val) cr_rfc2_sm = classification_report(y_val, y_pred) cm_rfc2_sm = confusion_matrix(y_val, y_pred) roc_auc_rfc2_sm = roc_auc_score(y_res, rfc2_sm.predict_proba(X_res)[:, 1])
3) LightGBM
# lgbm_model mlflow.lightgbm.autolog(registered_model_name='lgbm_sm') # Register the trained model with autologging lgbm_sm_model = LGBMClassifier(learning_rate = 0.07, max_delta_step = 2, n_estimators = 100, max_depth = 10, eval_metric = "logloss", objective='binary', random_state=42) with mlflow.start_run(run_name="lgbm_sm") as run: lgbm1_sm_run_id = run.info.run_id # Capture run_id for model prediction later # lgbm_sm_model.fit(X_train,y_train) # Imbalanced training data lgbm_sm_model.fit(X_res, y_res.ravel()) # Balanced training data y_pred = lgbm_sm_model.predict(X_val) accuracy = accuracy_score(y_val, y_pred) cr_lgbm_sm = classification_report(y_val, y_pred) cm_lgbm_sm = confusion_matrix(y_val, y_pred) roc_auc_lgbm_sm = roc_auc_score(y_res, lgbm_sm_model.predict_proba(X_res)[:, 1])
実験結果の確認
各実験結果はMLflowの自動ログ機能を介してワークスペースに保存されています。
※ 毎回の実験結果が記録されています。
ワークスペースに移動すると bank-churn-experiment というアイテムが存在しているので選択します。
ページ左の枠から、各実験を選択します。
それぞれの結果を確認することができます。
トレーニング済みモデルのパフォーマンス評価
2つの方法でトレーニング済みモデルの評価が行われています。
どちらの方法でも特に問題なく、似たようなパフォーマンス結果が得られます。
1) ワークスペースに保存した実験から機械学習モデルを読み込み、検証データセットでパフォーマンスを評価
# Define run_uri to fetch the model # mlflow client: mlflow.model.url, list model load_model_rfc1_sm = mlflow.sklearn.load_model(f"runs:/{rfc1_sm_run_id}/model") load_model_rfc2_sm = mlflow.sklearn.load_model(f"runs:/{rfc2_sm_run_id}/model") load_model_lgbm1_sm = mlflow.lightgbm.load_model(f"runs:/{lgbm1_sm_run_id}/model") # Assess the performance of the loaded model on validation dataset ypred_rfc1_sm_v1 = load_model_rfc1_sm.predict(X_val) # Random Forest with max depth of 4 and 4 features ypred_rfc2_sm_v1 = load_model_rfc2_sm.predict(X_val) # Random Forest with max depth of 8 and 6 features ypred_lgbm1_sm_v1 = load_model_lgbm1_sm.predict(X_val) # LightGBM
2) トレーニング済みの機械学習モデルを直接使用して検証データセットでパフォーマンスを評価
ypred_rfc1_sm_v2 = rfc1_sm.predict(X_val) # Random Forest with max depth of 4 and 4 features ypred_rfc2_sm_v2 = rfc2_sm.predict(X_val) # Random Forest with max depth of 8 and 6 features ypred_lgbm1_sm_v2 = lgbm_sm_model.predict(X_val) # LightGBM
混同行列について
次の精度の評価で使用する混同行列と呼ばれる表について説明します。
2値分類問題で出力されたクラス分類の結果をまとめた表で、2 値分類機械学習モデルの性能を測る指標として使用されています。
実際の結果と機械学習の推測結果から以下の4つのパターンが表示されます。
機械学習モデルの予測 | ||||
---|---|---|---|---|
Positive | Negative | |||
実際の結果 | Positive | TP(True Positive) | FN(False Negative) | |
Negative | FP(False Positive) | TN(True Positive) |
TP(True Positive) | 実際の結果が正しいものを正しいと推測している |
---|---|
TN(True Positive) | 実際の結果が正しくないものを正しくないと推測している |
FP(False Positive) | 実際の結果が正しくないものを正しいと推測している |
FN(False Negative) | 実際の結果が正しいものを正しくないと推測している |
また、この表から、機械学習モデルの性能を図るための以下のような指標を算出できます。
正確さ (Accuracy) |
全体の中で正しく分類できたデータ(TP+TN)の割合。 高いほど性能がいいと言える。 |
Accuracy = (TP + TN) / (TP + TN + FN + FP) |
---|---|---|
適合率 (Precision) |
Positiveと分類されたデータ(TP+FP)の中で実際にPositiveだったデータ(TP)の割合。 高いほど間違った判断が少ないと言える。 |
Presision = TP / (TP + FP) |
真陽性率 (True Positive Rate) |
Positiveなデータを取りこぼしなく正しく推測できているかどうか。 この値が高ければ間違ったPositiveの判断が少ないということ。 |
True Positive Rate = TP / (TP + FN) |
真陰性率 (True Negative Rate) |
Negativeなデータを取りこぼしなく正しく推測できているかどうか。 この値が高ければ間違ったNegativeの判断が少ないということ。 |
True Negative Rate = TN / (TN + FP) |
トレーニング済みモデルの精度の評価
混同行列を表示するスクリプトを使用して、先ほどのパフォーマンス評価を行った結果から各モデルのTP/TN/FP/FNを表示し分類の精度を評価します。
本記事では1つ目の方法の結果を使用します。
混同行列を表示するスクリプト
import seaborn as sns sns.set_theme(style="whitegrid", palette="tab10", rc = {'figure.figsize':(9,6)}) import matplotlib.pyplot as plt import matplotlib.ticker as mticker from matplotlib import rc, rcParams import numpy as np import itertools def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues): print(cm) plt.figure(figsize=(4,4)) plt.rcParams.update({'font.size': 10}) plt.imshow(cm, interpolation='nearest', cmap=cmap) plt.title(title) plt.colorbar() tick_marks = np.arange(len(classes)) plt.xticks(tick_marks, classes, rotation=45, color="blue") plt.yticks(tick_marks, classes, color="blue") fmt = '.2f' if normalize else 'd' thresh = cm.max() / 2. for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): plt.text(j, i, format(cm[i, j], fmt), horizontalalignment="center", color="red" if cm[i, j] > thresh else "black") plt.tight_layout() plt.ylabel('True label') plt.xlabel('Predicted label')
最大深度が 4 で 4 つの特徴を持つランダム フォレストの精度の評価
cfm = confusion_matrix(y_val, y_pred=ypred_rfc1_sm_v1) plot_confusion_matrix(cfm, classes=['Non Churn','Churn'], title='Random Forest with max depth of 4') tn, fp, fn, tp = cfm.ravel()
最大深度が 8 で 6 つの特徴を持つランダム フォレストの精度の評価
cfm = confusion_matrix(y_val, y_pred=ypred_rfc2_sm_v1) plot_confusion_matrix(cfm, classes=['Non Churn','Churn'], title='Random Forest with max depth of 8') tn, fp, fn, tp = cfm.ravel()
LightGBMの精度の評価
cfm = confusion_matrix(y_val, y_pred=ypred_lgbm1_sm_v1) plot_confusion_matrix(cfm, classes=['Non Churn','Churn'], title='LightGBM') tn, fp, fn, tp = cfm.ravel()
3つのモデルの結果と指標の比較
TP | TN | FN | FP | 正確さ (Accuracy) | 適合率 (Precision) | 真陽性率 (True Positive Rate) | 真陰性率 (True Negative Rate) | |
---|---|---|---|---|---|---|---|---|
最大深度が 4 で 4 つの特徴を持つランダム フォレスト | 1365 | 247 | 231 | 157 | 0.806 | 0.896846255 | 0.855263158 | 0.611386139 |
最大深度が 8 で 6 つの特徴を持つランダム フォレスト | 1427 | 229 | 169 | 175 | 0.828 | 0.890761548 | 0.894110276 | 0.566831683 |
LightGBM | 1485 | 215 | 111 | 189 | 0.85 | 0.887096774 | 0.930451128 | 0.532178218 |
LightGBM が他の2つと比べ正確さ (Accuracy)や真陽性率 (True Positive Rate)の指標が高く性能がいいと判断しました。
まとめ
今回の記事では、機械学習モデルのトレーニングと登録を実施しました。
次回の後編では、予測データの作成とレポートの作成を行いたいと思います。
*1:機械学習ライフサイクルを管理するために設計されたオープンソースフレームワーク