はじめに
前回は、いきなりデータセットに対して(irisとtitanic)LightGBMを使ってみました。ただ、なんだかよくわからないままコードだけ走らせてしまったような不完全燃焼な感じがしたので、少し復習も兼ねてまとめたいと思います。
LightGBMとは
勾配ブースティングと呼ばれる方法で、複数の決定木を作成しながら学習をすすめていきます。ある時点で作成した決定木の予測結果を確認し、誤差の大きいデータをうまく予測できるように、次の決定木を作成していきます。最終的な予測値は、学習の過程で作成したすべての」決定木での予測値を利用して算出されます。
高い予測性能が期待され、大量のデータに対しても比較的、高速で扱うことができる、という特徴があります。
LightGBM特有の事前準備
scikitlearnで実行できる機械学習アルゴリズムとは異なり、LightGBM特有の準備が必要な部分があります。
- データはすべて数値に変換する必要がある
- カテゴリ変数はリスト形式で明示的に渡す必要がある
- 学習用と検証用にデータセットを分割する必要がある
- 学習用(検証用含む)データとテストデータに同じ加工を施す必要がある
はじめてLightGBMのコードをみたときにわかりにくい部分は、この辺かと思います。基本的な簡単な実行例なのにわかりにくい部分があったら、まずはこれらの処理をしていないかをチェックしてみるとよいでしょう。
手順
必ずこの順番でこの処理がされるわけではありませんが、大まかに次の流れとなります。
1.ライブラリのインポート
2.データの読み込み
3.(データの連結):トレーニング/テストデータに同じ加工をするため
4.探索的データ分析
5.特徴量エンジニアリング
・数値への変換
・特徴量選択
・新しい特徴量の作成
・ホールドアウト検証用のデータ作成
・LightGBM用のデータ作成
6.ハイパーパラメータの設定
7.学習と予測
8.評価
ライブラリのインポート
テーブルデータの処理や可視化用のライブラリなども含めて、初めに必要なライブラリをまとめてインポートしておきます。使わないときもあるのですが、僕はいつもseabornを合わせてインポートしています。
# ライブラリのインポート
import pandas as pd
import pandas_profiling
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import seaborn as sns
# LightGBMのインポート
import lightgbm as lgb
# scikitlearnから前処理用のライブラリのインポート
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
# scikitlearnから評価用のライブラリをインポート
from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score
# 初期設定
sns.set_style('whitegrid',{'linestyle.grid':'--'})
%matplotlib inline
データの読み込み
コンペティションであれば、トレーニングデータとテストデータに分かれていることが多いです。kaggleのタイタニックの例だと、次のようにローカルに持ってきたtrain.csv/test.csvをインポートしておくとよいでしょう。
# データの読み込み
train_df=pd.read_csv('C:/python/book\kaggle_start/data/train.csv')
test_df=pd.read_csv('C:/python/book\kaggle_start/data/test.csv')
データの連結
これはコンペティションでなければ、連結して処理することはないでしょう。LightGBMではテストデータもトレーニングデータと同じ加工が必要となるt前、まとめてしまった方が効率がよいため、先に連結することがあります。
但し、必ずしも連結する必要はありません。同じトレーニングデータ/テストデータに同じ加工さえ施されれば、どちらの方法でも構いません。ここでは、トレーニングデータとテストデータを連結したものをdataという変数として扱うことにしましょう。
# トレーニングデータとテストデータの連結
data=pd.concat([train_df,test_df],sort=False)
探索的データ分析
これは前の「東京都のコロナウィルス感染者数について」の投稿でも扱いましたが、pandas_profilingで各特徴量を把握することができます。
今回は先ほど連結したdataではなく、train_dfでの実施例とします。
# 探索的データ分析:pandas_profiling
train_df.profile_report()
特徴量エンジニアリング
ここでは「数値への変換」「特徴量選択」「新しい特徴量の作成」「アルゴリズムに合わせたデータの加工」をおこないます。
- 数値への変換
これはいろんなやり方があります。たとえば、titanicのデータでは性別を表す「Sex」という特徴量があります。「male」と「female」という値をとります。これはたとえば次のように変換します。
train_df['Sex'].replace(['male','female'],[0,1],inplace=True)
このような処理は、トレーニングデータ、テストデータ共通なので先ほど連結した「data」にやると漏れがなくてよいかもしれません。数値への変換ですが、次のようにダイレクトにやっても構いません。
train_df.loc[train_df['Sex'] == 'female','Sex'] = 0
train_df.loc[train_df['Sex'] == 'male','Sex'] = 1
また、順序性のあるカテゴリ変数に関しては、LabelEncodingをします。次のようにまずはLabelEncodingのインスタンスを作って、fit→transformとします。
# 順序性があるものをlabel encodingする
embarked_encoder = LabelEncoder()
embarked_encoder.fit(train_df['Embarked'].fillna('Null'))
# label encodingしたEmbarkedを数値に変換する
train_df['Embarked'].fillna('Null', inplace=True)
train_df['Embarked'] = embarked_encoder.transform(train_df['Embarked'])
特徴量選択
探索的データ分析により不要と判断できる特徴量について処理をしてしまいましょう。dropや必要に応じてpopなどのメソッドを使います。
# 不要な特徴量を取り除く
train_df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)
新しい特徴量の作成
これも探索できデータ分析から得た仮説から、作り出します。これは一概には言えませんが、扱うデータによりいろんなことが考えられるので、ここでは割愛します。
アルゴリズムに合わせたデータの加工
LightGBMでは、トレーニングデータを学習用と検証用のデータに分ける必要があります。これはscikitlearnのtrain_test_splitを使います。次のようにします。
# Hold-Out法でトレーニングデータとテストデータに2:8で分ける
# 説明変数をtrain_df、説明変数をtrain_yとしています
X_train, X_valid, y_train, y_valid = train_test_split(train_df, y, test_size=0.2, random_state=1)
ここでは単純なホールドアウト検証としましたが、より汎化性能を高めるにはCross Validation(交差検証)をおこないます。これは、scikitlearnにKFoldというクラスが用意されています。
# 交差検証(n_splitsで分割数を指定する)
from sklearn.model_selection import KFold
kf=KFold(n_splits=5, shuffle=True, random_state=0)
交差検証については、別途、取り上げることにし、ソースコードの全容はここでは割愛します。
ハイパーパラメータの調整
次に、ハイパーパラメータについても設定が必要です。この辺はdefaultの値が与えられているので、ある程度端折っても動くことは動きますが、より精度をあげるには、調整が必要となります。この辺もたくさんのパラメータがあるため、詳細は別途、扱うことにしましょう。たとえば、以下のようなものです。
lgbm_params = {
'boosting': 'gbdt', # default
'application': 'binary', # 二値分類なのでbinaryを選びます
'learning_rate': 0.05, # learning rateは小さめとしておきます
'num_leaves': 41, # num_leavesは少し大きめにする
'metric': 'binary_logloss', # 損失関数
}
学習と予測
学習はlgbm.train()でおこないます。ここにもいろんなオプションがあります。これも別途、扱うことにしてサンプルを記述しておきます。
evaluation_results = {}
clf = lgbm.train(train_set=train_data,
params=lgbm_params,
valid_sets=[train_data, test_data],
valid_names=['Train', 'Test'],
evals_result=evaluation_results,
num_boost_round=1000,
early_stopping_rounds=50,
verbose_eval=20
)
optimum_boost_rounds = clf.best_iteration
評価
評価はscikitlearnに用意されているmetricsを使うとよいでしょう。冒頭で「from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score」とインポートしています。これを使いましょう。
preds = np.round(clf.predict(X_test))
print('Accuracy score = \t {}'.format(accuracy_score(y_test, preds)))
print('Precision score = \t {}'.format(precision_score(y_test, preds)))
print('Recall score = \t {}'.format(recall_score(y_test, preds)))
print('F1 score = \t {}'.format(f1_score(y_test, preds)))
まとめ
いかがでしたか?途中、交差検証、ハイパーパラメータ、LightGBMの学習時のオプションについては、詳しい説明を省きました。また、学習経過の表示や可視化についても説明を省いています。この辺は、次回以降に扱うことにしましょう。今回は、LightGBMを使うときの流れの確認をおこないました。
▶ まずLightGBMでの実装をしたいのであれば以下の記事をどうぞ。2つのサンプルデータで実装例を示しています。
コメント