【本記事の内容】LightGBMの基本的な使い方(多クラス分類編)
近年Kaggleなどで主流となっているデータ分析のモデル「LightGBM」の基本的な使い方について初学者向けに丁寧に解説します。
※ とりあえず「LightGBMの使い方 (ソースコード)」を見たい人はこちら
※次に該当する方は以下の記事を参照してください。
・LightGBMの基本的な使い方(回帰分析編)を見たい方
・LightGBMとは何かを知りたい方
・LightGBMの2種類のAPI(文法)について知りたい方
・Pythonおよび周辺パッケージのインストール方法を知りたい方、Pythonおよび基本ライブラリについて勉強したい方

※次に該当する方は以下の記事を参照してください。
・LightGBMの基本的な使い方(2値分類編)を見たい方

説明の準備
では、実際にLightGBMの使い方の説明をする前に「そもそも多クラス分類とは何か」の説明 と 使用するデータセットの「irisデータ」の説明 を軽くしていきます。
※ 説明が不要でコードのみを見たい方はこちら
多クラス分類とは ?
多クラス分類はそのままの意味で「データを複数のクラスに分類する分析」のことです。
具体例としては、「現時点の気象情報(気温、湿度、降水量など)から明日の天気(晴れ、曇り、雨)を予測する」分析が多クラス分類です。
※分類方法はいろいろあるのでここでは詳しくは触れませんが、例えば、決定木ベースのアルゴリズムであれば「気温が20℃以上か?」「湿度が60以上か?」…といった質問を繰り返して明日の天気を予測します。
irisデータセット
iris(setosa, versicolor, virginicaの3種類がある花)における花びらの4つの特徴量(ガクの長さ、幅、花弁の長さ、幅)が含まれるデータセット。
※特徴量 : 特徴を表す量のこと。変数とほぼ同じ。
※詳しくは以下を参照してください。
[機械学習] iris データセットを用いて scikit-learn の様々な分類アルゴリズムを試してみた
【目的変数】
target : 0(=setosa), 1(=versicolor), 2(=virginica)
【説明変数】
1.sepal length : ガクの長さ(cm)
2.sepal width : ガクの幅(cm)
3.petal length : 花弁の長さ(cm)
4.petal width : 花弁の幅(cm)
LightGBMの基本的な使い方(多クラス分類編)
では、実際に「iris」データセットを題材にしてLightGBMで多クラス分類を行っていきます。具体的には、花びらの情報からその花がsetosa , versicolor , virginica のどの花であるかを予測していきます。(目的変数は「target」 : 0(=setosa), 1(=versicolor), 2(=virginica) )
準備
【ライブラリのインポート、関数の定義】
# ライブラリのインポート
import pandas as pd # 基本ライブラリ
import numpy as np # 基本ライブラリ
import matplotlib.pyplot as plt # グラフ描画用
import seaborn as sns; sns.set() # グラフ描画用
import warnings # 実行に関係ない警告を無視
warnings.filterwarnings('ignore')
import lightgbm as lgb #LightGBM
from sklearn import datasets
from sklearn.model_selection import train_test_split # データセット分割用
from sklearn.metrics import accuracy_score # モデル評価用(正答率)
from sklearn.metrics import log_loss # モデル評価用(logloss)
from sklearn.metrics import roc_auc_score # モデル評価用(auc)
# データフレームを綺麗に出力する関数
import IPython
def display(*dfs, head=True):
for df in dfs:
IPython.display.display(df.head() if head else df)
【データの読み込み、トレーニングデータ・テストデータの分割】
# iris データセットを読み込む
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names) # データフレームへの格納
df['target'] = iris.target # 目的変数の追加
# データの確認
print(df.shape) #データサイズの確認
display(df) # df.head()に同じ(文中に入れるときはdisplay()を使う)
# 説明変数,目的変数
X = df.drop('target',axis=1).values # 説明変数(target以外の特徴量)
y = df['target'].values # 目的変数(target)
# トレーニングデータ,テストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.20, random_state=2)
【出力】
sepal length (cm) | sepal width (cm) | … | target | |
---|---|---|---|---|
0 | 5.1 | 3.5 | … | 0 |
1 | 4.9 | 3.0 | … | 0 |
2 | 4.7 | 3.2 | … | 0 |
3 | 4.6 | 3.1 | … | 0 |
4 | 5.0 | 3.6 | … | 0 |
【クラスの比率】
#クラスの比率
n_target0, n_target1,n_target2 = len(df[df['target'] == 0]), len(df[df['target'] == 1]) , len(df[df['target'] == 2])
n_all = n_target0+n_target1+n_target2
print('target0 の割合 :', n_target0/n_all) # target0の割合
print('target1 の割合 :', n_target1/n_all) # target1の割合
print('target2 の割合 :', n_target2/n_all) # target2の割合
【出力】
target0 の割合 : 0.3333333333333333
target1 の割合 : 0.3333333333333333
target2 の割合 : 0.3333333333333333
LightGBM (scikit-learn API)
【モデルの学習】
# モデルの学習
model = lgb.LGBMClassifier() # モデルのインスタンスの作成
model.fit(X_train, y_train) # モデルの学習
# テストデータの予測クラス (予測クラス(0 or 1 or 2)を返す)
y_pred = model.predict(X_test)
# テストデータのクラス予測確率 (各クラスの予測確率 [クラス0の予測確率,クラス1の予測確率,クラス2の予測確率] を返す)
y_pred_prob = model.predict_proba(X_test)
【予測値の確認】
# 真値と予測値の表示
df_pred = pd.DataFrame({'target':y_test,'target_pred':y_pred})
display(df_pred)
# 真値と予測確率の表示
df_pred_prob = pd.DataFrame({'y':y_test, 'target0_prob':y_pred_prob[:,0], 'target1_prob':y_pred_prob[:,1], 'target2_prob':y_pred_prob[:,2]})
display(df_pred_prob)
【出力】
※target : 正解クラス, target_pred : 予測クラス, target0_prob : クラス0である予測確率, target1_prob : クラス1である予測確率, target2_prob : クラス1である予測確率。
イメージとしては、target_pred : 天気(晴れ or 曇り or 雨), target0_prob : 晴れの確率, target1_prob : 曇りの確率, target2_prob : 雨の確率という感じです。予測クラスは予測確率が最も高いクラスを出力としています。
【モデルの評価】
# モデル評価
# acc : 正答率
acc = accuracy_score(y_test,y_pred)
print('Acc :', acc)
【出力】
Acc : 0.9666666666666667
LightGBM (Python API)
【モデルの学習】
# 学習に使用するデータを設定
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# LightGBM parameters
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'multiclass', # 目的 : 多クラス分類
'num_class': 3, # クラス数 : 3
'metric': {'multi_error'}, # 評価指標 : 誤り率(= 1-正答率)
# 他には'multi_logloss'など
}
# モデルの学習
model = lgb.train(params,
train_set=lgb_train, # トレーニングデータの指定
valid_sets=lgb_eval, # 検証データの指定
)
# テストデータの予測 ((各クラスの予測確率 [クラス0の予測確率,クラス1の予測確率,クラス2の予測確率] を返す))
y_pred_prob = model.predict(X_test)
# テストデータの予測 (予測クラス(0 or 1 or 2)を返す)
y_pred = np.argmax(y_pred_prob, axis=1) # 一番大きい予測確率のクラスを予測クラスに
【出力】
[1] valid_0's multi_error: 0.0333333
[2] valid_0's multi_error: 0.0333333
[3] valid_0's multi_error: 0.0333333
[4] valid_0's multi_error: 0.0333333
[5] valid_0's multi_error: 0.0333333
...
[96] valid_0's multi_error: 0.0333333
[97] valid_0's multi_error: 0
[98] valid_0's multi_error: 0.0333333
[99] valid_0's multi_error: 0.0333333
[100] valid_0's multi_error: 0.0333333
【予測値の確認】
# 真値と予測値の表示
df_pred = pd.DataFrame({'target':y_test,'target_pred':y_pred})
display(df_pred)
# 真値と予測確率の表示
df_pred_prob = pd.DataFrame({'y':y_test, 'target0_prob':y_pred_prob[:,0], 'target1_prob':y_pred_prob[:,1], 'target2_prob':y_pred_prob[:,2]})
display(df_pred_prob)
【出力】
【モデルの評価】
# モデル評価
# acc : 正答率
acc = accuracy_score(y_test,y_pred)
print('Acc :', acc)
【出力】
Acc : 0.9666666666666667
LightGBM (Python API) – カスタム(パラメータを少し調整した場合)
※ソースコードのみを載せます。
【モデルの学習】
# 学習に使用するデータを設定
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# LightGBM parameters
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'multiclass', # 目的 : 多クラス分類
'num_class': 3, # クラス数 : 3
'metric': {'multi_error'}, # 評価指標 : 誤り率(= 1-正答率)
#他には'multi_logloss'など
'learning_rate': 0.1,
'num_leaves': 23,
'min_data_in_leaf': 1,
'num_iteration': 1000, #1000回学習
'verbose': 0
}
# モデルの学習
model = lgb.train(params, # パラメータ
train_set=lgb_train, # トレーニングデータの指定
valid_sets=lgb_eval, # 検証データの指定
early_stopping_rounds=100 # 100回ごとに検証精度の改善を検討 → 精度が改善しないなら学習を終了(過学習に陥るのを防ぐ)
)
# テストデータの予測 (クラス1の予測確率(クラス1である確率)を返す)
y_pred_prob = model.predict(X_test)
# テストデータの予測 (予測クラス(0 or 1 or 2)を返す)
y_pred = np.argmax(y_pred_prob, axis=1) # 一番大きい予測確率のクラスを予測クラスに
【予測値の確認】
# 真値と予測値の表示
df_pred = pd.DataFrame({'target':y_test,'target_pred':y_pred})
display(df_pred)
# 真値と予測確率の表示
df_pred_prob = pd.DataFrame({'y':y_test, 'target0_prob':y_pred_prob[:,0], 'target1_prob':y_pred_prob[:,1], 'target2_prob':y_pred_prob[:,2]})
display(df_pred_prob)
簡単な分析結果・実行結果の解説
評価指標
・accuracy : 正答率。0以上1以下の値をとり、値が大きいほどモデルの精度が良いとされる。
・logloss : 対数損失。予測確率と正解クラスの誤差を表し、値が小さいほどモデルの精度が良いとされる。
(指標の存在意義) 「正解クラス:1, クラス1の予測確率:0.8のモデル」と「正解クラス:1, クラス1の予測確率:0.6のモデル」の精度の比較ができる。(どちらも予測クラスは1であるがモデルの精度の細かい差を捉えることができる。)
結果まとめ
- scikit-learn APIのモデル(デフォルト)による予測結果とPython APIのモデル(デフォルト)による予測結果は全く同じであった。
- scikit-learn API, PythonAPI(デフォルト) : Acc : 9666666666666667 → 精度の良いモデルといえる
おわりに
このblogでは、今後「数学 (主に統計学,微積分,線形代数)・AI機械学習(特にデータ解析系)」の「データサイエンス」に関するトピックを自分の学習の振り返りもかねて投稿していきます。
初学者にもわかりやすいようになるべく丁寧に書いていくのでよろしくお願いします。
何かあればコメントをお願いします。
参考文献
【今回の記事に関する参考文献】
① Python API — LightGBM 2.3.2 documentation (公式ドキュメント)
https://lightgbm.readthedocs.io/en/latest/Python-API.html
②Python: LightGBM を使ってみる
https://blog.amedama.jp/entry/2018/05/01/081842
【オススメ文献】
①logloss について
機械学習でLog Lossとは何か
1c6c9a3fae2d97bfa0c7
コメント
lightgbmのlgb_evalにtestデータを用いていますが、これではモデルの学習段階でtestデータを用いていることになり、いわゆる「リーク」が発生しているかと思われます。
(この状態では、モデルがtestデータに過学習している可能性があります)
正しくは、trainデータを実際に学習に使うデータと検証データに分割して、lgb_trainにtrainデータの一部(7割くらい?)を入れて、lgb_evalに残りのtrainデータを入れることで、モデルの学習段階においては、一切テストデータを使わないといった実装にした方が良いと思います。
コメントありがとうございます。
ご指摘に関しては全くその通りです。
正しいデータの使い方としては、
1. データセットを「trainデータセット」と「testデータセット」に分ける
2. trainデータセットから「lgb_trainに入れる用のデータ」と「lgb_evalに入れる用のデータ」を用意する
3. モデルの学習は2で用意したデータセットのみを用いる
4. モデルの評価は1で用意した「testデータセット」を用いる
ということだと理解しております。(この記事は初めて学習した際に忘備録的に書いていたので理解が正しくない部分もあったことをお詫び申し上げます。)
時間ができ次第、ご指摘部分の修正および全体的に更新しようと思っております。