【Point】一般的な機械学習のモデルの生成の流れ
- 与えられたデータを学習データ、検証データに分割
- 学習データを使ってモデルのパラメータをチューニング
- 検証データでモデルの精度を測る
機械学習タスクの第一歩としてはデータを学習データと検証データに分割することから始まります。
import numpy as np
import pandas as pd
import pandas_profiling as pdp
import matplotlib.pyplot as plt
%matplotlib inline
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
import pickle
from sklearn.model_selection import train_test_split,GridSearchCV
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, f1_score, roc_curve, roc_auc_score
from sklearn.ensemble import RandomForestClassifier
import xgboost as xgb
import warnings
warnings.simplefilter('ignore')
csvDataPath = './UCI_Credit_Card_light.csv'
#csvDataPath = 'UCI_Credit_Card.csv'
data = pd.read_csv(csvDataPath, engine='python', encoding='utf-8')
- train_test_split()でデータを分割
- train_test_split()で引数stratifyの指定
- train_test_split()で引数random_stateの指定
- train_test_split()で引数test_sizeの指定
- 学習
- ランダムフォレスト
- XGB
- 予測
- .predict()で予測
- .scoreでモデルの精度を取得
- accuracy_score()でaccuracy、f1_score()でf値`の取得
- confusion_matrix()で混同行列、classification_report()で様々な分類の評価指標を取得
- ROC曲線とAUC
- ランダムフォレストモデルのROC曲線とAUC
- XGBモデルのROC曲線とAUC
- pickleを用いてモデルの保存
train_test_split()でデータを分割
引数にリスト
、numpy配列
、DataFrame
など長さをもった配列を指定すると、
データを (0.75, 0.25) の割合で2つのデータに分割可能train_test_split()
の引数は可変長で指定可能です。
ただし、与えた配列が分割されて返ってくるので「与えた配列の数×2」の数が返り値として必要になります。
y_target = data['default.payment.next.month']
col = data.columns.tolist()
print(col[::-1])
col.remove('default.payment.next.month')
print("")
print(col[::-1])
['default.payment.next.month', 'PAY_AMT6', 'PAY_AMT5', 'PAY_AMT4', 'PAY_AMT3', 'PAY_AMT2', 'PAY_AMT1', 'BILL_AMT6', 'BILL_AMT5', 'BILL_AMT4', 'BILL_AMT3', 'BILL_AMT2', 'BILL_AMT1', 'PAY_6', 'PAY_5', 'PAY_4', 'PAY_3', 'PAY_2', 'PAY_0', 'AGE', 'MARRIAGE', 'EDUCATION', 'SEX', 'LIMIT_BAL', 'ID'] ['PAY_AMT6', 'PAY_AMT5', 'PAY_AMT4', 'PAY_AMT3', 'PAY_AMT2', 'PAY_AMT1', 'BILL_AMT6', 'BILL_AMT5', 'BILL_AMT4', 'BILL_AMT3', 'BILL_AMT2', 'BILL_AMT1', 'PAY_6', 'PAY_5', 'PAY_4', 'PAY_3', 'PAY_2', 'PAY_0', 'AGE', 'MARRIAGE', 'EDUCATION', 'SEX', 'LIMIT_BAL', 'ID']
default.payment.next.month
がなくなったことを確認
x_explanatory = data[col]
x_explanatory
ID | LIMIT_BAL | SEX | EDUCATION | MARRIAGE | AGE | PAY_0 | PAY_2 | PAY_3 | PAY_4 | … | BILL_AMT3 | BILL_AMT4 | BILL_AMT5 | BILL_AMT6 | PAY_AMT1 | PAY_AMT2 | PAY_AMT3 | PAY_AMT4 | PAY_AMT5 | PAY_AMT6 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 20000.0 | 2 | 2 | 1 | 24 | 2 | 2 | -1 | -1 | … | 689.0 | 0 | 0 | 0 | 0 | 689 | 0 | 0.0 | 0.0 | 0 |
1 | 2 | 120000.0 | 2 | 2 | 2 | 26 | -1 | 2 | 0 | 0 | … | 2682.0 | 3272 | 3455 | 3261 | 0 | 1000 | 1000 | 1000.0 | 0.0 | 2000 |
2 | 3 | 90000.0 | 2 | 2 | 2 | 34 | 0 | 0 | 0 | 0 | … | 13559.0 | 14331 | 14948 | 15549 | 1518 | 1500 | 1000 | 1000.0 | 1000.0 | 5000 |
3 | 4 | 50000.0 | 2 | 2 | 1 | 37 | 0 | 0 | 0 | 0 | … | 49291.0 | 28314 | 28959 | 29547 | 2000 | 2019 | 1200 | 1100.0 | 1069.0 | 1000 |
4 | 5 | 50000.0 | 1 | 2 | 1 | 57 | -1 | 0 | -1 | 0 | … | 35835.0 | 20940 | 19146 | 19131 | 2000 | 36681 | 10000 | 9000.0 | 689.0 | 679 |
… | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
995 | 996 | 200000.0 | 1 | 1 | 2 | 39 | -2 | -2 | -2 | -2 | … | -200.0 | 0 | 60800 | 0 | 0 | 0 | 200 | 60800.0 | 0.0 | 0 |
996 | 997 | 140000.0 | 1 | 1 | 1 | 45 | 0 | 0 | 0 | 0 | … | 41853.0 | 44452 | 45433 | 46383 | 1600 | 1600 | 3169 | 1700.0 | 1700.0 | 1495 |
997 | 998 | 360000.0 | 1 | 1 | 1 | 38 | 1 | -2 | -2 | -2 | … | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 |
998 | 999 | 50000.0 | 2 | 2 | 2 | 23 | -1 | -1 | -1 | 0 | … | 780.0 | 390 | 390 | 500 | 0 | 780 | 0 | 390.0 | 500.0 | 18300 |
999 | 1000 | 120000.0 | 1 | 2 | 2 | 25 | 2 | 2 | 0 | 0 | … | 111700.0 | 83858 | 86434 | 88802 | 0 | 5000 | 3158 | 3934.0 | 3802.0 | 2000 |
x_train,x_test,y_train, y_test = train_test_split(x_explanatory, y_target)
train, test, train, testの順になる
len(x_train), len(x_test)
(750, 250)
train_test_split()で引数stratifyの指定
【Point】学習データと検証データで正解ラベルの割合を揃える
本当に(真に)予測のしたいデータは正解ラベルが存在しないですが、
学習データと検証データに分割する場合では正解ラベルの割合を揃えることで
データを分割する際のデータの偏りを抑え、分割したデータに依存する
機械学習モデルを生成する危険性を回避します
x_train,x_test,y_train, y_test = train_test_split(x_explanatory, y_target,stratify = y_target)
train_test_split()で引数random_stateの指定
【Point】データ分割の再現性を確保
この講座に限った話ではありませんが、自分以外の人が実行しても、
同様の結果が得られるように、再現性がとれるようにしておくことはとても重要です。
引数のrandom_state
の指定をすることで、データの分割方法が固定されて何度実行しても同じ結果になります。
※random_state
の固定はあくまでの再現性の観点での考え方になります。
あえてrandom_state
を指定せずに実装する場合もあります。
x_train,x_test,y_train, y_test = train_test_split(x_explanatory, y_target, random_state = 1)
train_test_split()で引数test_sizeの指定
x_train,x_test,y_train, y_test = train_test_split(x_explanatory, y_target, test_size =0.1)
こうすると、テストサイズが9:1になる
学習
機械学習モデルの生成をします。
まずはパラメータの調整などはせずにパッケージのbaseモデルで学習させます。
これは、分析者がパラメータ調整をしないモデルでこの機械学習タスクにおける精度を測ることで、このタスクにおける 「おおよその精度を把握する」 ことが狙いです。
※量的、質的データの取り扱いや一意のデータをそのまま扱っているので、
データサイエンスの観点ではこの状態のデータからモデルを生成するのは大きな誤りです
※ただ実際のデータ分析では、厳密なパラメータ調整などはせずにますは学習させてみる
ということは経験論な観点から頻繁に行われています。
例えば、特にパラメータを調整をしないモデルでも精度が十分にでるのであれば、
余計なチューニングは不要となります。逆は非常に大変ですが…
y_target = data['default.payment.next.month']
col = data.columns.tolist()
col.remove('default.payment.next.month')
x_explanatory = data[col]
x_train,x_test,y_train, y_test = train_test_split(
x_explanatory, y_target,stratify = y_target,random_state=1)
ランダムフォレスト
今回の機械学習のタスクは支払いが行われるか否かの2値分類問題です。
【Point】
- 分類のタスクでは
RandomForestClassifier()
を使用 - 回帰のタスクでは
RandomForestRegressor()
を使用
rf_model = RandomForestClassifier(random_state=0)
rf_model.fit(x_train,y_train)
RandomForestClassifier()
XGB
【Point】
- 分類のタスクでは
xgb.XGBClassifier()
を使用 - 回帰のタスクでは
xgb.XGBRegressor()
を使用
xgb_model = xgb.XGBClassifier(random_state=0)
xgb_model.fit(x_train,y_train)
XGBClassifier(base_score=0.5, booster='gbtree', callbacks=None, colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1, early_stopping_rounds=None, enable_categorical=False, eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise', importance_type=None, interaction_constraints='', learning_rate=0.300000012, max_bin=256, max_cat_to_onehot=4, max_delta_step=0, max_depth=6, max_leaves=0, min_child_weight=1, missing=nan, monotone_constraints='()', n_estimators=100, n_jobs=0, num_parallel_tree=1, predictor='auto', random_state=0, reg_alpha=0, reg_lambda=1, ...)
予測
前章で各アルゴリズムのbaseモデルで学習が完了したので、モデルの精度を算出します。
扱う機械学習のタスクによって 何の評価指標を用いるのが最適なのか は変化します。
今回のタスクであれば、分類問題かつデータの正解ラベルの割合が不均衡なので(次章以降で取り扱い)
単純にaccuracy
を用いるのではなく、最終的にはf値
、混同行列
、ROC曲線
, AUC
など
を用いて総合的に判断する必要があります。
※回帰問題であれば、RMSE
,MSE
,MAE
などを評価指標として用います。
.predict()で予測
【Point】引数には予測に用いたいデータをとる
※予測時には学習で用いたデータは使用しない
ランダムフォレストで予測
rf_pred = rf_model.predict(x_test)
XGBで予測
xgb_pred = xgb_model.predict(x_test)
.scoreでモデルの精度を取得
【Point】.score
で出力される値はaccuracy
であることに注意
- 第1引数に予測に用いたいデータ
- 第2引数に正解データ
ランダムフォレストのbaseモデルの精度
rf_model.score(x_test,y_test)
0.804
XGBのbaseモデルの精度
xgb_model.score(x_test,y_test)
0.788
accuracy_score()でaccuracy、f1_score()でf値`の取得
- 第1引数に正解データ
- 第2引数に予測した値データ
この節ではaccuracy
とf値
を比較して、大きく値が異なっていることを確認します。
この状態は7章の冒頭で述べた通り、データが不均衡でデータに偏りが発生していることに起因します。例えば、どのような値を入力としても予測では全て0
と出力するモデルが作成された場合、accuracy
だけで判断すると精度が0.8
が出てしまうので、データサイエンスに趣がない人は
「良いモデルができた!やった!!」
と判断してしまいがちですが、本当は汎化性能のないモデルだった。
なんてことは現場で働いている人でも陥りがりなミスです。
【Point】モデルの精度を確認する場合は
- モデルの評価指標は何の指標なのか
- 今回はその評価指標で精度を判断することは妥当なのか
を必ず確認するようにしましょう!!
ランダムフォレストのaccuracy
accuracy_score(y_test, rf_pred)
0.796
ランダムフォレストのf値
f値を確認すると精度が低いことが確認できます。
f1_score(y_test, rf_pred)
0.32
XGBのaccuracy
accuracy_score(y_test, xgb_pred)
0.788
XGBのf値
f1_score(y_test, xgb_pred)
0.39080459770114945
confusion_matrix()で混同行列、classification_report()で様々な分類の評価指標を取得
分類のタスクによっては前項の通り、accuracy
だけど測ることが良いとは限りません。confusion_matrix()
で混同行列を確認したり、 様々な指標を一括で確認したいときはclassification_report()
が有効です。
使用方法は他の評価指標と同様に
- 第1引数に正解データ
- 第2引数に予測した値データ
で取得できます。
【Point】 confusion_matrix()で取得できる値は混同行列と並びが異なるのでラベルのPositive
,Negative
の意味を考えて確認する
※今回のデータではTP
が実際に未払いで、予測でも未払いと分類された人の数です。
0 = Negative, 1 = Positive
Predicted
0 1
Actual 0 TN FP
1 FN TP
普通はTPが左上なので注意
ランダムフォレストの混同行列を取得
rf_cm = confusion_matrix(y_test, rf_pred)
rf_cm
array([[188, 8], [ 41, 13]], dtype=int64)
sns.heatmap(混同行列)
で混同行列をヒートマップ形式にして可視化
annot
: 可視化のセルに値を出力するか否かcmap
:Matplotlib
のカラーマップを指定 ※今回は青系統の色を指定
sns.heatmap(rf_cm, annot=True, cmap='Blues')
ランダムフォレストでレポート取得
rf_report = classification_report(y_test, rf_pred)
print(rf_report)
precision recall f1-score support 0 0.82 0.96 0.88 196 1 0.62 0.24 0.35 54 accuracy 0.80 250 macro avg 0.72 0.60 0.62 250 weighted avg 0.78 0.80 0.77 250
XGBで混同行列を取得
xgb_cm = confusion_matrix(y_test, xgb_pred)
xgb_cm
array([[180, 16], [ 37, 17]], dtype=int64)
sns.heatmap(xgb_cm, annot=True, cmap='Blues')
XGBでレポート取得
xgb_report = classification_report(y_test, xgb_pred)
print(xgb_report)
precision recall f1-score support 0 0.83 0.92 0.87 196 1 0.52 0.31 0.39 54 accuracy 0.79 250 macro avg 0.67 0.62 0.63 250 weighted avg 0.76 0.79 0.77 250
ROC曲線とAUC
2つの値(クラス0
、クラス1
)を分類する機械学習モデルは、クラス1
に属する確率を返します。一般的な分類モデルは閾値を0.5
に設定し、
0.5
以上ならクラス1
0.5
未満ならクラス0
などのように分類結果を解釈します。
ROC曲線
は以下の指標を用いて表現されます。
False Positive Rate(FPR : 偽陽性率)
: FP / (FP+TN)- 正解データが
クラス0
(負)で、誤ってクラス1
(正)と予測した割合
- 正解データが
True Positive Rate(TPR : 真陽性率)
:TP / (TP+FN)- 正解データ
クラス1
(正)で、正しくクラス1
(正)と予測した割合
- 正解データ
閾値を0.5
から変化させたときのFPR
とTPR
の値の変化を捉えたものがROC曲線
です。FPR
が大きくなるということは、閾値を下げて全て予測値をクラス1
(正)と分類していることになるので、FPR
が大きくなると、必然的にTPR
も大きくなります。
なので、良い分類モデルとはFPR
が低い時点で既にTPR
に高い数値が出ているモデルになります。AUC
はROC曲線
と下側の面積をとったものでランダムに予測するモデルの場合は0.5を取り、理想的なモデルでは1を取ります。
【Point】 ROC曲線
とAUC
は分類モデルの性能を人間の目で直感で判別する指標です。
roc_curve()
を用いることで、FPR
, TPR
, FPTとTPRが変化する閾値
を取得できます。
- 第1引数に正解データ
- 第2引数に予測した値データ
ランダムフォレストモデルのROC曲線とAUC
fpr, tpr, thresholds = roc_curve(y_test, rf_pred)
fpr, tpr, thresholds
(array([0. , 0.04081633, 1. ]), array([0. , 0.24074074, 1. ]), array([2, 1, 0], dtype=int64))
auc = roc_auc_score(y_test, rf_pred) ここがスコア
auc
0.5881519274376418
plt.plot(fpr, tpr, label='ROC curve (area = %.2f)' % auc)
plt.legend()
plt.title('ROC curve')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.grid(True)
XGBモデルのROC曲線とAUC
fpr, tpr, thresholds = roc_curve(y_test, xgb_pred)
fpr, tpr, thresholds
(array([0. , 0.08163265, 1. ]), array([0. , 0.31481481, 1. ]), array([2, 1, 0]))
auc = roc_auc_score(y_test, xgb_pred)
auc
0.6165910808767953
plt.plot(fpr, tpr, label='ROC curve (area = % 2f)' % auc)
plt.legend()
plt.title('ROC curve')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.grid(True)
baseモデルの段階ではランダムフォレストモデル、XGBモデル、どちらをとっても良い分類モデルが作成できているとは言い難い状態です。
pickleを用いてモデルの保存
【Point】pickle
はPythonオブジェクトをファイルとして保存し、
呼び出せるようにするためのパッケージです。
【Point】機械学習に限らずAIのモデル開発では、
学習したモデルを保存しておき、後から呼び出すことが頻繁にあります。
下記コードを参考にモデルを.pkl
ファイル形式で保存していきます。
with open('fileName.pkl', mode='wb') as f:
pickle.dump(variable, f)
ランダムフォレストモデルの保存
with open('rf_model.pkl', mode='wb') as f:
pickle.dump(rf_model, f)
XGBモデルの保存
with open('xgb_model.pkl', mode='wb') as f:
pickle.dump(xgb_model, f)
コメント