Python

Python基礎:交差検証について

スポンサーリンク

はじめに

前回はLightGBMを使用するときの手順を学びました。

このとき、Cross Validation(交差検証)に関しては、詳しい説明は省いたので、今回あらたに取り上げたいと思います。Scikitlearnを利用することで簡単に交差検証をおこなうことができます。但し、データセットを分割するときに注意する点もあります。この辺もとりあげることにしましょう。

Cross Validation(交差検証)

Cross Validation(交差検証)とは、異なる方法で複数のデータセットの分割をおこない、それぞれでホールドアウト検証を実行する方法です。そのスコアの平均を確認することで、1回のホールドアウト検証で生じる偏りを抑え、汎化性能を高めることが期待できます。。

単純なホールドアウト検証は、scikitlearnのtrain_test_split( ) で実装できます。そのため、train_test_split( )を複数回用いれば Cross Validation(交差検証) を実装できそうです。ただ、scikitlearnにはもっと便利なKFoldというクラスが用意されています。これを使った例をみていきましょう。

ライブラリのインポート

まずは利用するライブラリのインポートをします。train_test_splitはデータを分割する前処理用のクラスです。KFoldが交差検証用のクラス、lightgbmはLightGBMのライブラリになります。

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
import lightgbm as lgb

データの読み込み

train=pd.read_csv('data/train.csv')
test=pd.read_csv('data/test.csv')

data = pd.concat([train, test], sort=False)

特徴量エンジニアリング

Titanicのデータはこれまで何度か扱ったことがあり、その中で探索的データ解析をおこないました。以下のコードの前半部分は、その探索的データ解析の結果を反映させています。探索的データ解析については、下の投稿に記しています。

# 特徴量エンジニアリング
data['Sex'].replace(['male', 'female'], [0, 1], inplace=True)
data['Embarked'].fillna(('S'), inplace=True)
data['Embarked'] = data['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int)
data['Fare'].fillna(np.mean(data['Fare']), inplace=True)
data['Age'].fillna(data['Age'].median(), inplace=True)
data['FamilySize'] = data['Parch'] + data['SibSp'] + 1
data['IsAlone'] = 0
data.loc[data['FamilySize'] == 1, 'IsAlone'] = 1

# 特徴量選択
delete_columns = ['Name', 'PassengerId', 'Ticket', 'Cabin']
data.drop(delete_columns, axis=1, inplace=True)

train = data[:len(train)]
test = data[len(train):]

y_train = train['Survived']
X_train = train.drop('Survived', axis=1)
X_test = test.drop('Survived', axis=1)

交差検証

ここで扱うKFoldは、データセットをK個のグループ(フォールド)に分割し、そのうちの1グループ(フォールド)をテストデータ、他のグループ(フォールド)を訓練データとし一つのモデルを構築します。これをK回グループ間でテストデータをローテーションすることでK個のモデルを作ります。最終的な予測値は殆どの場合K個のモデルの予測値の平均で決めます。

OOF予測値とはOut Of Fold (Prediction)から来ており、文字通りフォールドから出てきた予測値という意味で頻繁に使われます。

ここからKFoldを使った交差検証を実装していきましょう。

# 各分割でのX_testに対する予測値を格納するリスト
y_preds = []
# 各分割で学習したモデルを格納するリスト
models = []
# 各分割での検証用データセット(X_val)に対する予測値を格納するndarray
oof_train = np.zeros((len(X_train),))

# 分割方法の指定
cv = KFold(n_splits=5, shuffle=True, random_state=0)

categorical_features = ['Embarked', 'Pclass', 'Sex']

params = {
    'objective': 'binary',
    'max_bin': 300,
    'learning_rate': 0.05,
    'num_leaves': 40
}

# 設定した分割方法に基づいて、各分割での学習用・検証用データセットに対応するindexを取得
for train_index, valid_index in cv.split(X_train):
    # 取得したindexに基づいて、データセットを分割
    X_tr = X_train.loc[train_index, :]
    X_val = X_train.loc[valid_index, :]
    y_tr = y_train[train_index]
    y_val = y_train[valid_index]

    lgb_train = lgb.Dataset(X_tr, y_tr,categorical_feature=categorical_features)
    lgb_eval = lgb.Dataset(X_val, y_val,reference=lgb_train,categorical_feature=categorical_features)

    model = lgb.train(params, lgb_train,
                                   valid_sets=[lgb_train, lgb_eval],
                                   verbose_eval=10,
                                   num_boost_round=1000,
                                   early_stopping_rounds=10)
    
    # 各分割での検証用のデータセット(X_val)に対する予測値を格納(oof_trainに格納)
    oof_train[valid_index] = model.predict(X_val, num_iteration=model.best_iteration)
    
    # 各分割でのX_testに対する予測値を、先に作成したy_predsというリストに格納
    y_pred = model.predict(X_test, num_iteration=model.best_iteration)
    y_preds.append(y_pred)
    
    # 各分割で学習したモデルをmodelsというリストに格納
    models.append(model)

評価

scores = [
    m.best_score['valid_1']['binary_logloss'] for m in models
]
score = sum(scores) / len(scores)
print('===CV scores===')
print(scores)
print(score)
from sklearn.metrics import accuracy_score

y_pred_oof = (oof_train > 0.5).astype(int)
accuracy_score(y_train, y_pred_oof)
スポンサーリンク

まとめ

いかがでしょうか?何度かやっているうちに、だんだん理解できたりすることもあります。まずは、自分でコードを書いてみて、わからなかったところを調べて写経するなどしているうちに、何故か、すーっと理解できる瞬間があったります。あきらめず頑張りましょう!

コメント

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