LightGBMはkaggleやSignateといったコンペに参加したことある人なら一度は目にしたことがあるであろう有名な機械学習アルゴリズムですが、使い方が複数あり、ややこしいんですよね。
普段はツールに頼って自分でモデリングをしない私のようなエンジニアは、使い方をすぐに忘れてしまうので今回は忘備録も兼ねて説明をします。
LightGBMの種類
LightGBMは大きく分けて以下二つのAPIユーザインターフェースを持っており、それぞれ使用方法が異なる。
- Training API
- Scikit-learn API
kaggleやGoogleでLightGBMの使用方法を確認したいときはどっちのインターフェースが使われているのか注意が必要となる。
(公式のDocsを確認するのが一番なのだが英語のみ)
それぞれ、簡単に使い方を説明する。
Training APIの使い方
Training APIの準備
まずはLightGBMのインポート。
import lightgbm as lgb # この時点でTraining APIとScikit-learn APIで異なる
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
データセットの指定
今回使用するデータセットはタイタニックのコンペで使用されたデータを用いる。
(Kaggleからダウンロード可能)
以下ではデータセットを一度DataFrameとして読み込んでいるが、csvファイル等のローカルファイルの場所を直接指定することも可能。
# データの読込み
df=pd.read_csv('train.csv')
# データを説明変数と目的変数に分離
X_train=df.drop('Survived',axis=1)
y_train=df['Survived']
# LightGBMに入力する形に指定
train_data = lgb.Dataset(
data = X_train, # 説明変数を指定。ローカルファイルのパスでもOK
label = y_train, # 予測ターゲットを指定
feature_name = df.columns # 特徴量名を指定
)
上記コードのlgb.Dataset()内では3種のみ指定しいてるがそれ以外にも様々な指定が可能
- data:説明変数を指定(DataFrame、numpy array、ファイルパス等でもOK、H20のテーブルも指定可能な模様)
- label:目的変数を指定
- feature_name:特徴量の名前を指定(オプション)
- categorical_feature:カテゴリ変数を指定可能
- …
もし、あらかじめ学習データと評価用データを分けているのであれば、次のようにして評価用データセットとして指定する
df=pd.read_csv('train.csv')
X = df.drop('Survived',axis=1)
y = df['Survived']
# データを学習用(説明変数/目的変数)、評価用(説明変数/目的変数)に分離
X_train, X_validation, y_train, y_validation = train_test_split(X, y, test_size =0.3, random_state =0)
# 学習データの指定
train_data = lgb.Dataset(
data = X_train, # 説明変数を指定。ローカルファイルのパスでもOK
label = y_train, # 予測ターゲットを指定
feature_name = df.columns # 特徴量名を指定
)
# 評価データの指定
valid_data = lgb.Dataset(
data = X_validation, # 説明変数を指定。ローカルファイルのパスでもOK
label = y_validation, # 予測ターゲットを指定
feature_name = df.columns # 特徴量名を指定
)
パラメータの指定(モデリング内容の指定)
続いては、LightGBMのハイパーパラメータを指定し、学習する。
パラメータの指定については、重要そうなパラメータをlgb.train()内で直接設定し、細かいところをparamsで辞書形式で指定するイメージ。
ここでは代表的なパラメータを指定する。以下コードを実行するとモデル作成が行われる
params = {
'task': 'train',
'objective': 'binary', # これは重要。指定しないとデフォルトではregression(回帰)となる
'boosting': 'gbdt', # 'gbdt'はGradient Boosting Decision TreeによるBoosting
'metric': 'binary_logloss',
'num_leaves': 64,
'min_data_in_leaf': 20,
'max_depth': 7,
'verbose': 0,
}
model = lgb.train(
params = params, # 以下詳細参照
train_set = train_data, # 先ほど作成したデータセット
num_boost_round = 100, # Boostingの繰り返し回数
early_stopping_rounds = 20, # valid_setsで評価用データセットを指定しないといけない
valid_sets = [validation_data] # 評価用データセットをリストで指定
)
paramsの詳細はこちら⇒Core parameters
lgb.train()の詳細はこちら⇒lightgbm.train
注意点としては、LightGBMを使う上で過学習や学習時間といったパフォーマンスに関わってくる”early_stopping_rounds”の設定だが、これを使う場合は必ず”valid_sets”で評価用データセット(上記コードではvalidation_data)を指定する必要がある。
交差検定(CV: Cross Validation)もできちゃう
予測の汎化性能を高めるために必須な交差検定によるモデル作成も可能である。デフォルトでは5回のCVが行われる。
model = lgb.cv(
params=params,
train_set=train_data,
num_boost_round=100,
early_stopping_rounds=20,
nfold=5
)
”nfold”で交差検定の回数を指定することができる。
モデル作成にはlgb.train()か、このlgb.cv()のどちらかを使う。
予測する
予測方法はいたってシンプル。モデル作成時に”early_stopping_round”を設定している場合は”model.best_iteration”で最適なラウンド数を指定することができる。
y_pred = model.predict(X_validation, num_iteration=bst.best_iteration)
Scikit-learn APIの使い方
続いてはLightGBMをScikit-learn APIで使ってみる。
普段からsklearnの機械学習アルゴリズムを使ってる人はこちらの使い方のほうがなじみ深いかもしれない。
Scikit-learn APIを使うために
Scikit-learn APIのLightGBMを使う場合は、予測のタイプによってその種類が異なる。
対応しているものは以下。
- LightGBMClassifier:2値分類といった分類問題を解くために使われる
- LightGBMRegressor:数値を予測する回帰問題を解くために使われる
- LightGBMRanker:ランキング問題を解くために使われる
ここでは、先ほどのタイタニックのデータを使うので生か死かの2値分類に対応したLightGBMClassifierを使っていく。
Scikit-learn APIをさっそく使ってみる!
細かい説明は抜きにして、早速コードを見ていこう。
# データの読込み
df=pd.read_csv('train.csv').drop(['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'],axis=1)
# 説明変数と目的変数に分ける
X = df.drop('Survived',axis=1)
y = df['Survived'] # データを学習用(説明変数/目的変数)、評価用(説明変数/目的変数)に分離
# データを学習用と評価用に分ける
X_train, X_validation, y_train, y_validation = train_test_split(X, y, test_size =0.3, random_state =0) # 学習データの指定
# モデルの定義。パラメータ指定はTraining APIの辞書型ではなく、変数として指定していく。
# パラメータの中身はTrainig APIと同じ感じにしてみた。
model = lgb.LGBMClassifier(
objective='binary',
boosting= 'gbdt', # 'gbdt'はGradient Boosting Decision TreeによるBoosting
metric= 'binary_logloss',
num_leaves= 64,
min_data_in_leaf= 20,
max_depth= 7,
verbose= 0,
)
# モデルの学習
model.fit(X_train,y_train)
# 予測 二値で出力する場合
pred_bi = model.predict(X_validation)
# 予測 二値の確率値確率値を出力する場合
pred_pr = model.predict_proba(X_validation)
ここでの注意点は予測の出力の仕方だ。
model.precit(x_validation)
を使うと、出力は
array([0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], dtype=int64)
というように二値のみになっている。
一方で、
model.predict_proba(X_validation)
を使うと、以下のように確率値で表示してくれる。
array([[0.89505531, 0.10494469], [0.81756035, 0.18243965], [0.94548698, 0.05451302], [0.20000037, 0.79999963], [0.7453854 , 0.2546146 ], [0.68728436, 0.31271564], ・・・
ただし、出力はTrue、Falseそれぞれ両方の確率値を出力する仕様になっているため、
model.predict_proba(X_validation)[:,1]
としてやることで、Trueになる確率だけ出力することが可能。
予測したい内容や後続の処理に合わせて、必要な出力の仕方を選択してやる必要がある。
Training APIとScilit-learn APIの比較
Training APIとScilit-learn APIの大きな違いは次の通り。
機能面
項目 | Training API | Scilit-learn API |
インスタンス生成 | lgb.train() | lgb.〇〇(LGBMClasssifier、LGBMRegressor等) |
モデル作成ステップ | 1ステップ (インスタンス生成と同時に学習) |
2ステップ (インスタンス生成⇒fit()で学習) |
交差検定対応 | あり | なし |
精度 | 同じ | 同じ |
精度比較
タイタニックのデータで簡単に検証してみた。
import lightgbm as lgb
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
df=pd.read_csv('train.csv').drop(['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'],axis=1)
X = df.drop('Survived',axis=1)
y = df['Survived'] # データを学習用(説明変数/目的変数)、評価用(説明変数/目的変数)に分離
X_train, X_validation, y_train, y_validation = train_test_split(X, y, test_size =0.3, random_state =0) # 学習データの指定
###Training API###
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'binary',
'metric': 'binary_logloss',
'num_leaves': 64,
'min_data_in_leaf': 20,
'max_depth': 7,
'verbose': 0,
}
model_tr = lgb.train(
params=params,
train_set=train_data,
num_boost_round=100,
early_stopping_rounds=20,
valid_sets=[valid_data]
)
######
###Scikit-learn API###
model_sc = lgb.LGBMClassifier(
objective='binary',
boosting= 'gbdt', # 'gbdt'はGradient Boosting Decision TreeによるBoosting
metric= 'binary_logloss',
num_leaves= 64,
min_data_in_leaf= 20,
max_depth= 7,
verbose= 0,
)
model_sc.fit(X_train,y_train,eval_set=[(X_validation, y_validation)],early_stopping_rounds=20)
######
# AUCをそれぞれ評価してみる
print('Training API:',roc_auc_score(y_validation, model_tr.predict(X_validation)))
print('Scikit learn API:',roc_auc_score(y_validation, model_sc.predict_proba(X_validation)[:,1]))
結果は…
Training API: 0.7838988095238096 Scikit learn API: 0.7838988095238096
全く同じでした。
結論
- 基本的に性能は同じっぽい
- Training APIのほうがコードがすっきるする
- Training APIを使うと交差検定を楽にやってくれる
- どっちでも使いやすい方を使うべし!
以上、最後までお読みいただきありがとうございました!
コメント