機械学習実践 学習データ 検証データ

【Point】一般的な機械学習のモデルの生成の流れ

  1. 与えられたデータを学習データ、検証データに分割
  2. 学習データを使ってモデルのパラメータをチューニング
  3. 検証データでモデルの精度を測る

機械学習タスクの第一歩としてはデータを学習データと検証データに分割することから始まります。

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()でデータを分割

引数にリスト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
IDLIMIT_BALSEXEDUCATIONMARRIAGEAGEPAY_0PAY_2PAY_3PAY_4BILL_AMT3BILL_AMT4BILL_AMT5BILL_AMT6PAY_AMT1PAY_AMT2PAY_AMT3PAY_AMT4PAY_AMT5PAY_AMT6
0120000.02212422-1-1689.0000068900.00.00
12120000.022226-12002682.03272345532610100010001000.00.02000
2390000.022234000013559.01433114948155491518150010001000.01000.05000
3450000.022137000049291.02831428959295472000201912001100.01069.01000
4550000.012157-10-1035835.0209401914619131200036681100009000.0689.0679
995996200000.011239-2-2-2-2-200.006080000020060800.00.00
996997140000.011145000041853.04445245433463831600160031691700.01700.01495
997998360000.0111381-2-2-20.00000000.00.00
99899950000.022223-1-1-10780.039039050007800390.0500.018300
9991000120000.0122252200111700.08385886434888020500031583934.03802.02000
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引数に予測した値データ

この節ではaccuracyf値を比較して、大きく値が異なっていることを確認します。
この状態は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: 可視化のセルに値を出力するか否か
  • cmapMatplotlibのカラーマップを指定 ※今回は青系統の色を指定
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から変化させたときのFPRTPRの値の変化を捉えたものがROC曲線です。

FPRが大きくなるということは、閾値を下げて全て予測値をクラス1(正)と分類していることになるので、FPRが大きくなると、必然的にTPRも大きくなります。
なので、良い分類モデルとはFPRが低い時点で既にTPRに高い数値が出ているモデルになります。
AUCROC曲線と下側の面積をとったものでランダムに予測するモデルの場合は0.5を取り、理想的なモデルでは1を取ります。
【Point】 ROC曲線AUCは分類モデルの性能を人間の目で直感で判別する指標です。

roc_curve()を用いることで、FPRTPRFPTと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)

コメント

タイトルとURLをコピーしました