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

説明の準備
では、実際にLightGBMの使い方の説明をする前に「そもそも2クラス分類とは何か」の説明 と 使用するデータセットの「Breast Cancerデータ」の説明 を軽くしていきます。
※ 説明が不要でコードのみを見たい方はこちら
2値分類とは ?
2値分類はそのままの意味で「データを2クラスに分類する分析」のことです。
具体例としては、「ある人の健康状態(体温、血圧など)からその人がある病気にかかっているかを判別する」分析が2値分類です。
※分類方法はいろいろあるのでここでは詳しくは触れませんが、例えば、決定木ベースのアルゴリズムであれば「体温が40℃以上か?」「血圧が140/90mmHg 以上か?」…といった質問を繰り返してその人が病気かどうかを予測します。
Breast Cancerデータセット
Breast Cancer(乳がん)データセットの中身を簡単に見ていきます。データの概要としては、乳がん発症者および健常者の「乳がん検査に関するデータセット」で、具体的には「乳房塊の微細針吸引物(FNA)のデジタル化画像から計算される細胞核の特徴」を捉えたものだそうです。
【目的変数】
target : 乳がんでない = 0, 乳がんである = 1
【説明変数】
mean radius : 平均半径
mean texture : テクスチャをグレースケールにした際の平均
mean perimeter : 平均外周の長さ
mean area : 平均面積
mean smoothness : 平均なめらかさ(半径の分散)
mean compactness : 外周長さ^2 / 面積 – 1.0で示すコンパクトさ平均
mean concavity : 輪郭の凹部の重要度の平均
mean concave points : 輪郭の凹部の数の平均
mean symmetry : 対称性
mean fractal dimension : フラクタル次元の平均
radius error : 半径誤差
texture error : テクスチャの誤差
perimeter error : 外周の誤差
area error : 面積の誤差
smoothness error : なめらかさの誤差
compactness error : コンパクトさの誤差
concavity error : 輪郭の凹部の重要度の誤差
concave points error : 輪郭の凹部の数の誤差
symmetry error : 対称性の誤差
fractal dimension error : フラクタル次元の誤差
worst radius : 半径最悪値
worst texture : テクスチャ最悪値
worst perimeter : 外周の長さ最悪値
worst area : 面積の最悪値
worst smoothness : なめらかさの最悪値
worst compactness : コンパクトさの最悪値
worst concavity : 輪郭の凹部の重要度の最悪値
worst concave points : 輪郭の凹部の数の最悪値
worst symmetry : 対称性の最悪値
worst fractal dimension : フラクタル次元の最悪値
※https://ensekitt.hatenablog.com/entry/2018/08/22/200000 から引用
LightGBMの基本的な使い方(2値分類編)
では、実際に「Breast Cancer」データセットを題材にしてLightGBMで2値分類を行っていきます。(目的変数は「target」 : 乳がんでない = 0, 乳がんである = 1)
準備
【ライブラリのインポート、関数の定義】
# ライブラリのインポート
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)
【データの読み込み、トレーニングデータ・テストデータの分割】
# Breast Cancer データセットの読み込み
bc = datasets.load_breast_cancer()
df = pd.DataFrame(bc.data, columns=bc.feature_names) # データフレームへの格納
df['target'] = bc.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)
【出力】
mean radius | mean texture | mean perimeter | … | worst fractal dimension | target | |
---|---|---|---|---|---|---|
0 | 17.99 | 10.38 | 122.80 | … | 0.11890 | 0 |
1 | 20.57 | 17.77 | 132.90 | … | 0.08902 | 0 |
2 | 19.69 | 21.25 | 130.00 | … | 0.08758 | 0 |
3 | 11.42 | 20.38 | 77.58 | … | 0.17300 | 0 |
4 | 20.29 | 14.34 | 135.10 | … | 0.07678 | 0 |
【クラスの比率】
#クラスの比率
n_target0, n_target1 = len(df[df['target'] == 0]), len(df[df['target'] == 1])
n_all = n_target0+n_target1
print('target0 の割合 :',n_target0/n_all) # target0(健常者)の割合
print('target1 の割合 :',n_target1/n_all) # target1(がん患者)の割合
【出力】
target0 の割合 : 0.37258347978910367
target1 の割合 : 0.6274165202108963
LightGBM (scikit-learn API)
【モデルの学習】
# モデルの学習
model = lgb.LGBMClassifier() # モデルのインスタンスの作成
model.fit(X_train, y_train) # モデルの学習
# テストデータの予測クラス (予測クラス(0 or 1)を返す)
y_pred = model.predict(X_test)
# テストデータのクラス予測確率 (各クラスの予測確率 [クラス0の予測確率,クラス1の予測確率] を返す)
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({'target':y_test, 'target0_prob':y_pred_prob[:,0], 'target1_prob':y_pred_prob[:,1]})
display(df_pred_prob)
【出力】
target | target_pred | |
---|---|---|
0 | 1 | 1 |
1 | 1 | 1 |
2 | 1 | 1 |
3 | 0 | 0 |
4 | 1 | 1 |
target | target0_prob | target1_prob | |
---|---|---|---|
0 | 1 | 0.001106 | 0.998894 |
1 | 1 | 0.043994 | 0.956006 |
2 | 1 | 0.000138 | 0.999862 |
3 | 0 | 0.999936 | 0.000064 |
4 | 1 | 0.043777 | 0.956223 |
※target : 正解クラス, target_pred : 予測クラス, target0_prob : クラス0である予測確率, target1_prob : クラス1である予測確率
イメージとしては、target_pred : 天気(晴れ or 雨), target0_prob : 晴れの確率, target1_prob : 雨の確率(降水確率)という感じです。予測確率0.50以上のクラスを予測クラスとして出力しています。
【モデルの評価】
# モデル評価
# acc : 正答率
acc = accuracy_score(y_test,y_pred)
print('Acc :', acc)
# logloss
logloss = log_loss(y_test,y_pred_prob) # 引数 : log_loss(正解クラス,[クラス0の予測確率,クラス1の予測確率])
print('logloss :', logloss)
# AUC
auc = roc_auc_score(y_test,y_pred_prob[:,1]) # 引数 : roc_auc_score(正解クラス, クラス1の予測確率)
print('AUC :', auc)
【出力】
Acc : 0.956140350877193 logloss : 0.25212782936353206 AUC : 0.9861513687600645
【ROC曲線の描画】
# ROC曲線の描画
# cf : https://tips-memo.com/python-roc
from sklearn import metrics
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_test,y_pred_prob[:,1])
auc = metrics.auc(fpr, tpr)
plt.plot(fpr, tpr, label='ROC curve (area = %.2f)'%auc)
plt.legend()
plt.xlabel('FPR: False positive rate')
plt.ylabel('TPR: True positive rate')
plt.grid()
plt.show()
※ROC曲線 : 評価指標AUCを算出する際に用いる曲線のことを指します。詳しくは下記を参照してください。
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': 'binary', # 目的 : 2クラス分類
'metric': {'binary_error'}, # 評価指標 : 誤り率(= 1-正答率)
}
# モデルの学習
model = lgb.train(params,
train_set=lgb_train, # トレーニングデータの指定
valid_sets=lgb_eval, # 検証データの指定
)
# テストデータの予測 (クラス1の予測確率(クラス1である確率)を返す)
y_pred_prob = model.predict(X_test)
# テストデータの予測 (予測クラス(0 or 1)を返す)
y_pred = np.where(y_pred_prob < 0.5, 0, 1) # 0.5より小さい場合0 ,そうでない場合1を返す
【出力】
[1] valid_0's binary_error: 0.394737
[2] valid_0's binary_error: 0.394737
[3] valid_0's binary_error: 0.0701754
[4] valid_0's binary_error: 0.0789474
[5] valid_0's binary_error: 0.0701754
...
[96] valid_0's binary_error: 0.0438596
[97] valid_0's binary_error: 0.0438596
[98] valid_0's binary_error: 0.0438596
[99] valid_0's binary_error: 0.0438596
[100] valid_0's binary_error: 0.0438596
【予測値の確認】
# 真値と予測値の表示
df_pred = pd.DataFrame({'target':y_test,'target_pred':y_pred})
display(df_pred)
# 真値と予測確率の表示
df_pred_prob = pd.DataFrame({'target':y_test, 'target0_prob':1-y_pred_prob, 'target1_prob':y_pred_prob})
display(df_pred_prob)
【出力】
target | target_pred | |
---|---|---|
0 | 1 | 1 |
1 | 1 | 1 |
2 | 1 | 1 |
3 | 0 | 0 |
4 | 1 | 1 |
target | target0_prob | target1_prob | |
---|---|---|---|
0 | 1 | 0.001106 | 0.998894 |
1 | 1 | 0.043994 | 0.956006 |
2 | 1 | 0.000138 | 0.999862 |
3 | 0 | 0.999936 | 0.000064 |
4 | 1 | 0.043777 | 0.956223 |
【モデルの評価】
# モデル評価
# acc : 正答率
acc = accuracy_score(y_test,y_pred)
print('Acc :', acc)
# logloss
logloss = log_loss(y_test,y_pred_prob) # 引数 : log_loss(正解クラス,[クラス0の予測確率,クラス1の予測確率])
print('logloss :', logloss)
# AUC
auc = roc_auc_score(y_test,y_pred_prob) # 引数 : roc_auc_score(正解クラス, クラス1の予測確率)
print('AUC :', auc)
【出力】
Acc : 0.956140350877193 logloss : 0.25212782936353206 AUC : 0.9861513687600645
【ROC曲線の描画】
# ROC曲線の描画
# cf : https://tips-memo.com/python-roc
from sklearn import metrics
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_test,y_pred_prob)
auc = metrics.auc(fpr, tpr)
plt.plot(fpr, tpr, label='ROC curve (area = %.2f)'%auc)
plt.legend()
plt.xlabel('FPR: False positive rate')
plt.ylabel('TPR: True positive rate')
plt.grid()
plt.show()
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': 'binary', # 目的 : 2値分類
'metric': {'binary_error'}, # 評価指標 : 誤り率(= 1-正答率)
#他には 'binary_logloss','auc'など
'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)
# テストデータの予測
y_pred = np.where(y_pred_prob < 0.5, 0, 1) # 0.5より小さい場合0 ,そうでない場合1を返す
【予測値の確認】
# 真値と予測値の表示
df_pred = pd.DataFrame({'target':y_test,'target_pred':y_pred})
display(df_pred)
# 真値と予測確率の表示
df_pred_prob = pd.DataFrame({'target':y_test, 'target0_prob':1-y_pred_prob, 'target1_prob':y_pred_prob})
display(df_pred_prob)
簡単な分析結果・実行結果の解説
評価指標
・accuracy : 正答率。0以上1以下の値をとり、値が大きいほどモデルの精度が良いとされる。
・logloss : 対数損失。予測確率と正解クラスの誤差を表し、値が小さいほどモデルの精度が良いとされる。
(指標の存在意義) 「正解クラス:1, クラス1の予測確率:0.8のモデル」と「正解クラス:1, クラス1の予測確率:0.6のモデル」の精度の比較ができる。(どちらも予測クラスは1であるがモデルの精度の細かい差を捉えることができる。)
・AUC : ROC 曲線下の面積。0以上1以下の値をとり、値が大きいほどモデルの精度が良いとされる。クラス間の偏りに依存しない指標で、不均衡データを扱う際のモデルの指標としてよく使われる。
(指標の存在意義) 例えば、99.9%の人が感染しない病気があったとして、ランダムに選んだ1000人について「病気にかかっているかどうか」を判断する際に「正答率」のみを評価することを考える。この場合、例えば、医者でない人でも全員陰性と予測すれば99.9%の正答率を得ることができてしまう。こうした不均衡データの分類モデルの性能をより正確に測るために、「クラス間の偏りに依存しない指標」としてAUCが使われる。
結果まとめ
- scikit-learn APIのモデル(デフォルト)による予測結果とPython APIのモデル(デフォルト)による予測結果は全く同じであった。
- scikit-learn API, PythonAPI(デフォルト) : Acc : 0.956140350877193 logloss : 0.25212782936353206 AUC : 0.9861513687600645 → 精度の良いモデルといえる
おわりに
この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
【オススメ文献】
①AUC, ROC曲線について
【ROC曲線とAUC】機械学習の評価指標についての基礎講座
②logloss について
機械学習でLog Lossとは何か
1c6c9a3fae2d97bfa0c7
コメント
他クラス分類の方でも書きましたが、一応こちらにも書き込んでおきます。
lightgbmのlgb_evalにtestデータを用いていますが、これではモデルの学習段階でtestデータを用いていることになり、いわゆる「リーク」が発生しているかと思われます。
(この状態では、モデルがtestデータに過学習している可能性があります)
正しくは、trainデータを実際に学習に使うデータと検証データに分割して、lgb_trainにtrainデータの一部(7割くらい?)を入れて、lgb_evalに残りのtrainデータを入れることで、モデルの学習段階においては、一切テストデータを使わないといった実装にした方が良いと思います。