「Kubeflow」を用いて機械学習モデル作成と精度評価をやってみた

## 1.はじめに
この記事は[NTTコムウェア Advent Calendar 2024](https://qiita.com/advent-calendar/2024/nttcomware)の23日目の記事です。

### 自己紹介
NTTコムウェアの工藤です。
普段は、Retrieval-Augmented Generation（RAG）機能の精度を向上させるために、チームリーダーとして活動しています。具体的には、評価データ分析や考察を行いながら、精度を高めるための具体的なアイデアを検討しています。現案件に配属されてから3か月が経ちましたが、特に重要な精度向上のアイデアについては日々悩みながら取り組んでいます。

そこで、もともと興味のあったデータサイエンスを学ぶことで確度の高いアイデアを提案できると考え「Kubeflow」を用いて機械学習のモデル作成と精度評価を試みました。

この記事では、Kubeflowの構築方法よりも、モデル作成と精度評価に重点を置いています。機械学習モデルの作成に興味がある方は、ぜひ読んでいただければ嬉しいです。


## 2.データ読み込み
モデル作成する前に、対象となるライブラリを読み込む必要があります(下記参照)。また、データセットはオープンデータである「[Bank Marketing　Donated on 2/13/2012](https://archive.ics.uci.edu/dataset/222/bank+marketing)」を使用しました。当データセットには、顧客の年齢、職業、家を持っているかなどのデータがあります。


```ruby:ライブラリのインポート
import pandas as pd
from sklearn.model_selection import train_test_split # データを学習用と検証用に分割
from sklearn.tree import DecisionTreeClassifier # 決定木による分類のためのライブラリ
from sklearn.ensemble import RandomForestClassifier # ランダムフォレストによる分類のためのライブラリ
import subprocess
from sklearn.metrics import roc_auc_score # AUCを算出するライブラリ
from sklearn.metrics import roc_curve # ROC曲線を算出するライブラリ
from sklearn.metrics import accuracy_score # 正解率を算出するライブラリ
import matplotlib.pyplot as plt
import seaborn as sns

random_state = 1 # 乱数固定
sns.set() 

data = pd.read_csv('data_ml.csv')
```

## 3.モデル作成
前項のデータ読み込みの準備が完了すると、実際にモデルを作成することが可能となります。今回は機械学習の手法（アルゴリズム）として決定木と呼ばれる手法とランダムフォレストと呼ばれる手法を使います。
決定木は、データから「顧客が40歳以上か40歳未満か」「住宅ローンがあるかないか」のような傾向を発見することで、「顧客が40代以上で住宅ローンがないと定期預金をする」というようなパターンに基づく予測ができるようになります。
ランダムフォレストは、決定木を多数作り予測結果の多数決をとることで精度向上を目指す手法です。


```ruby:決定木モデル
X_train = data.drop(['y'], axis=1)
y_train = data['y']

# モデルの定義
tree = DecisionTreeClassifier(random_state=random_state) 

# 学習
tree.fit(X_train, y_train) 

# 予測
y_pred_tree = tree.predict(X_test) 
```

```ruby:ランダムフォレストモデル
# モデルの定義
forest = RandomForestClassifier(random_state=random_state)   
# 学習
forest.fit(X_train, y_train)  
# 予測
y_pred_forest = forest.predict(X_test) 
```

## 4.精度評価
予測モデルが学習できたので、予測精度を評価してみます。学習に使用していない検証用データに対してどのくらい予測が当たるのかを確認します。「正解率」と「AUC」で評価してみます。

```ruby:正解率での評価
ac_tree = accuracy_score(y_test, y_pred_tree) 
ac_forest = accuracy_score(y_test, y_pred_forest) 

print("決定木の正解率：{:.3f}".format(ac_tree))
print("ランダムフォレストの正解率：{:.3f}".format(ac_forest))
```
実行結果
`決定木の正解率：0.837　ランダムフォレストの正解率：0.896` 
ランダムフォレストのほうが5.9%ほど、正解率が決定木よりも高いことがわかりました。


```ruby:AUCでの評価
auc_tree = roc_auc_score(y_test, y_pred_tree[:,1]) 
auc_forest =  roc_auc_score(y_test, y_pred_forest[:,1]) 

print("決定木のAUC：{:.3f}".format(auc_tree))
print("ランダムフォレストのAUC：{:.3f}".format(auc_forest))
```
実行結果
`決定木のAUC：0.639　ランダムフォレストのAUC：0.802` 
AUCとは、Area Under the Curve の略であり、ROC 曲線の下部分の面積のことです。AUCが1.00に近ければ近いほど、モデルとして優れているということになります。
したがって、ランダムフォレストのほうが0.163ほど決定木よりも高いことから、優れたモデルということがわかります。

※補足
決定木、ランダムフォレストのROC曲線は下記のようになります。
```ruby:決定木ROC曲線
y_pred_tree = tree.predict_proba(X_test) 
fpr, tpr, thresholds = roc_curve(y_test, y_pred_tree[:,1]) 

# グラフ描画の設定のコード
plt.figure(figsize=(4,4))
plt.plot(fpr, tpr)
plt.plot([0,1],[0,1],color="gray",linestyle="dotted")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
```

![画像 (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3944167/c4501c40-4d06-59d1-5020-ce64b9bee77a.png)


```ruby:ランダムフォレストROC曲線
y_pred_forest = forest.predict_proba(X_test) 
fpr, tpr, thresholds = roc_curve(y_test, y_pred_forest[:,1]) 

# グラフ描画の設定のコード
plt.figure(figsize=(4,4))
plt.plot(fpr, tpr)
plt.plot([0,1],[0,1],color="gray",linestyle="dotted")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
```

![画像 (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3944167/445d9047-f09f-80a4-874d-db0dcf58c34b.png)


## 5.作成したモデルの理解
最後に、実際にどのようなモデルが作成されたのか確認してみました。
今回は優れたモデルであるランダムフォレストにおいて、どの説明変数（特徴量）が予測に効果的なのかを確認していきます。

```ruby:ランダムフォレストの特徴量重要度（上位20個）
# 特徴量重要度の表示
feature_importances = pd.DataFrame(forest.feature_importances_,
                              index = X_test.columns,
                              columns=["importance"]).sort_values("importance",ascending=False)
# 上位20位を棒グラフで描画
feature_importances.head(20).plot.bar(figsize=(13,4), fontsize=18)
```

![画像 (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3944167/e06f4e0a-5b25-b554-bfda-c4696e93398a.png)

balanceやageなどが予測に影響を与えていることがわかりました。

## 6.最後に
「Kubeflow」を用いて機械学習のモデル作成と精度評価の一連の流れを実施してみました。
初めて触れたPythonという言語でしたが、そのシンプルなコーディングスタイルに驚かされ、さらに機械学習の強力なツールであることを実感しました。現在の業務では、具体的なコード設計は求められていないものの、この分野を深く理解しておくことは新たな改善案提案にもつながると強く感じました。今後はよりPythonについて理解を深めたいと思います。

記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。