【Python】Kaggleで引っ張りだこ!lightgbmの2種類の使い方!Training APIとScikit-learn API!【lightgbm】

Sponsored Links

はじめに

Kaggleに挑戦したことのある方はKernelsでlightgbmを使用したものを見ることが多いと思います!

lighthbmは計算速度も速い、予測精度も高い、さらに決定木をベースにしたアルゴリズムのため前処理もほとんど必要ないという三拍子揃っためちゃくちゃ強力なモデルです!

Kaggleなどのコンペではとりあえずこれを使ってみるのがスタンダードらしく、実際Kaggleの上位入賞者の約半数が使用した経験があるほど人気のモデルです!

ですがこのlightgbm実は使い方(API)が2種類あるんです。それが「lightgbmのdocumentに書かれている使い方と違う!」などといった混乱が生じる原因となります。。。実際私も最初めっちゃ混乱しました。。。

そこでここではlightgbmの2種類のAPI(Training APIとScikit-learn API)をそれぞれサンプルを使って説明していきます!

「lightgbmをどっちの書き方で使ったらいいか分からない」、「これからKaggleに挑戦したい!」という方のお役に立てば幸いです!

まず必要なライブラリーをインストールします!

import lightgbm as lgb
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import seaborn as sns

データ準備

今回はscikit-learnのbreast cancerデータセットを使います!

breast cancerデータセットはとても簡単な2値分類のデータセットです!

乳腺腫のデジタル画像を解析して得られた30の特徴量を用いて、その画像の乳がんが良性か悪性か(それぞれ0と1)を予測するものです!

import lightgbm as lgb
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import seaborn as sns

cancer = load_breast_cancer()
print(cancer.keys())
# dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])

load_breast_cancer()によって辞書としてデータセットがダウンロードできます!

cancerは辞書なのでcancer[‘data’]とすることで特徴量が、cancer[‘target’]とすることで正解ラベルが取得できます!

ここでcancer[‘data’]とcancer[‘target’]はNumpy arrayです!

これをscikit-learnのtrain_test_splitでtrain用のデータセットとvalidation用のデータセットに分割します!

X = cancer.data # Numpy array
y = cancer.target # Numpy array
feature_names = cancer.feature_names.tolist()

X_train, X_validation, y_train, y_validation  = \
    train_test_split(X, y, test_size = 0.3, random_state = 0)
print(X_train.shape, y_train.shape)
# (398, 30) (398, 1)

ここまででX_trainには訓練用の特徴量、y_trainには訓練用の正解ラベル、X_validationにはテスト用の特徴量、y_validationにはテスト用の正解ラベルがそれぞれ保存されています!

さて!ここまででデータの準備ができました!早速lightgbmを使って乳がんが良性か悪性かの二値分類をやってみましょう!

Sponsored Links

Training API

まずはlightgbmのDocumentのPython Quick Startで紹介されているTraining APIから説明していきます!

後ほど紹介するScikit-learn APIとは違ってmodelオブジェクトを作成してfit()メソッドを使うのではなく、train()を使用するからこの名前がついています!

Training APIの使い方はざっくり以下のようになります!

  1.  lgb,Dataset()でDatasetオブジェクトを作成
  2. ハイパーパラメータを辞書として用意する
  3. lgb.train()にDatasetオブジェクトとハイパーパラメータ辞書を渡す

Datasetオブジェクトってなんぞやって話ですよね笑 これはコードをみるのが一番理解が速いです!

早速実践してみましょう!

train_data = lgb.Dataset(
    data=X_train, 
    label=y_train, 
    feature_name=feature_names
)

validation_data = lgb.Dataset(
    data=X_validation, 
    label=y_validation, 
    feature_name=feature_names
)

print(train_data.data)
print(train_data.label)

Datasetオブジェクトは特徴量とそれに対する正解ラベルをセットで保持することのできるオブジェクトです!

data引数には予測に使用する特徴量を渡します!今回のようにNumpy arrayを渡すこともできますし、Pandas DataFrameも渡せます!文字列を渡せばファイルパスとして認識してくれます!

label引数には正解ラベルを渡します!これもdata引数と同様にNumpy arrayやPandas DataFrameを渡します!こちらは文字列を渡すことはできないようです。

feature_nameには特徴量の名前をリストで渡します!もしdataにPandas DataFrameを渡しているならfeature_name=’auto’とすることで列名を自動で認識してくれます!

返り値のDatasetオブジェクト(今回だとtrain_data)に対してtrain_data.dataとすることで特徴量に、train_data.labelとすることで正解ラベルにそれぞれアクセスすることができます!

今回は訓練用のtrain_dataとテスト用のvalidation_dataの2つを用意しました!

Datasetオブジェクトが準備できたので次はハイパーパラメータの辞書を準備します!

params = {
    'task': 'train',
    'boosting_type': 'gbdt',
    'objective': 'binary',
    'metric': 'binary_logloss',
    'num_leaves': 64,
    'min_data_in_leaf': 20,
    'max_depth': 7,
    'verbose': 0,
}

ここで設定した辞書をこのあとすぐに出てくるlgb.train()のparam引数に渡します!

こういう形でハイパーパラメータを設定させるのって珍しいですよね!このあたりがScikit-liearnなど他のAPIになれた人が戸惑うポイントだと思いますが慣れればコードもスッキリしてとてもいいです!

以下、一つ一つを簡単に説明していきますが詳細はlightgbm documentを参考にしてください!

  • task: デフォルトは’train’です。’train’, ‘prediction’, ‘refit’などから選択します!
  • boosting_type: ‘gbdt’がデフォルトでGradient Boosting Decision Treeを使用することを指示します。他には’rf’とすることでランダムフォレストを選択する事もできます!基本的には’gbdt’一択だと思います!
  • objective: 何を目的としたモデルを訓練するのかを指定します!’regression’がデフォルトでこれは回帰を行うモデルを訓練するときに使用します!他には今回選択した二値分類を目的とする’binary’や多クラス分類を行うための’multiclass’などがあります!
  • metric: 訓練の指標とする損失関数を指定します!今回は二値分類なので’binary_logloss’を選択しました!他には回帰のためのモデルだと’rmse’や多クラス分類だと’multi_logloss’などを指定します!
  • num_leaves, min_data_in_leaf, max_depth: この3つは決定木のハイパーパラメータでどれくらい複雑な木を作成するかを指定します!決定木のハイパーパラメータは他にもたくさんありますが、lightgbmはleaf-wise tree growthアルゴリズムを用いているためここで指定した3つが過学習を回避する上で最も大切になります!詳しくはDocumentのParameters-Tuningを参照してください!
  • verbose: どれくらい詳しく訓練の途中経過を表示するかを指定します!0だとエラーと警告を表示するだけで、大きくするほどより多くの情報を表示してくれます!

さて!ここまでで訓練の準備はできました!それでは実際に訓練を実行してみましょう!

bst = lgb.train(
    params=params, 
    train_set=train_data, 
    num_boost_round=100, 
    early_stopping_rounds=20,
    valid_sets=[validation_data]
)

lgb.train()に上で用意したtrain_data, validation_dataとparamsを渡して、エポック数などの訓練に関するパラメータを設定します!この関数の返り値として訓練されたモデルが帰ってきます!返り値のモデルのクラスはBoosterクラスといいます!

bst,paramsで設定したパラメータにアクセスできたり、以下のようにbst.save_model()で訓練したモデルをファイルとして保存するメソッドも用意してくれています!

bst.save_model('model.txt', num_iteration=bst.best_iteration)

モデルの訓練が終わったのでこのモデルを使って値の予測をやってみます!

y_pred = bst.predict(X_validation, num_iteration=bst.best_iteration)
y_pred = y_pred.round(0)

このモデルによる予測がどれほどの精度を持っているのかをscikit-learnのaccuracy_scoreを使って確認してみます!

accuracy = accuracy_score(y_pred, y_test)
print(f"accuracy score: {accuracy:0.4f}")
# accuracy score: 0.9825

かなりの精度が出ていますね!最後に混合行列も確認してみます!

cm = confusion_matrix(y_test, y_pred)
cm_matrix = pd.DataFrame(data=cm, columns=['Actual Positive:1', 'Actual Negative:0'], 
                                 index=['Predict Positive:1', 'Predict Negative:0'])

sns.heatmap(cm_matrix, annot=True, fmt='d', cmap='YlGnBu')

本来がんでないものを3つだけがんであると予測してしまっていますがそれ以外は素晴らしい結果ですね!

以上がTraining APIでのlightgbmの使い方です!

 

Scikit-learn API

次はScikit-learn APIの使い方を紹介します!

lightgbmのドキュメントで紹介されているのはTraining APIなのですが、scikit-learnライブラリーのmodelオブジェクトを作成してそれにメソッドを作用させていくようなプログラムの書き方(以下で詳しく説明していきます!)に慣れている方はこちらのほうが親しみやすいかもしれません!

実際私が書くときもこちらで書くことが多いです!

ただ、Training APIに劣る点として、Scikit-learn APIはTraining APIのtrain関数をラップして実装されているためtrainよりも細かな調整をしづらいという点が挙げられます!

しかしほとんど気にすることはないと思いますので書きやすい方で書くのがいいと思います!

以下が

  1.  pandas.DataFrameやNumpy arrayとしてデータセットを用意
  2. LGBMClassifierオブジェクトを作成する
  3. データセットをLGBMClassifierオブジェクトのfit()メソッドに渡してモデルの訓練を行う
  4. 訓練したLGBMClassifierオブジェクトにpredict()メソッドを作用させて予測

では早速こちらでも上と同じbreast cancer データセットの予測をやってみましょう!

まずはデータセットを用意します!上のTraining APIではNumpy arrayとしてデータセットを用意したので今回はpandas.DataFrameとして作成してみます!

X = pd.DataFrame(cancer.data, columns=cancer.feature_names)
y = pd.DataFrame(cancer.target, columns=['diagnosis'])

X_train, X_validation, y_train, y_validation  = \
    train_test_split(X, y, test_size = 0.3, random_state = 0)

データセットの用意ができたので、次にmodelオブジェクトを作成します!

今回は分類のタスクなのでLGBMClassifierを使います!(回帰の場合にはLGBMRegressorが用意されています!)

オブジェクトをインスタンス化したあと、fitメソッドの引数にX_train, y_trainを渡してモデルを訓練します!

 

clf = lgb.LGBMClassifier(
    objective='binary',
    num_leaves=64,
    min_child_samples=20,
    max_depth=7
)
clf.fit(X_train, y_train)

さあこれでモデルの訓練ができました!こちらのAPIはモデルのインスタンス化の時点でハイパーパラメータを設定しています!こちらのほうが個人的には直感的でわかりやすい気がします

訓練もモデルをデータにfitさせるという直感的なメソッドなので何をやっているのか、どこで何を設定すればいいのかがわかりやすいと思います!

さて訓練したモデルを使って予測をしてみましょう!

y_pred = clf.predict(X_validation)

予測結果の確認は上のTraining APIのものと同じものを使います!

accuracy = accuracy_score(y_pred, y_test)
print(f"accuracy score: {accuracy:0.4f}")
# accuracy score: 0.9825

もちろんですが、ハイパーパラメータも同じものを使ったので結果も同じになりますね!

cm = confusion_matrix(y_test, y_pred)
cm_matrix = pd.DataFrame(data=cm, columns=['Actual Positive:1', 'Actual Negative:0'], 
                                 index=['Predict Positive:1', 'Predict Negative:0'])

sns.heatmap(cm_matrix, annot=True, fmt='d', cmap='YlGnBu')

 

Sponsored Links

まとめ

lightgbmパッケージの2つの使い方Training APIとScikit-learn APIの紹介をしました!

lightgbm Turorialでも記述されているTraining API(こっちの方が作者的には推してる?)とscikit-learnに慣れ親しんだ人にはとても使いやすいScikit-learn APIどちらがお好みだったでしょうか?

ぜひこれを使ってKaggleなどのコンペに挑戦してみてください!

ここまで読んでいただいてありがとうございました!

参考


https://lightgbm.readthedocs.io/en/latest/Python-Intro.html
https://www.kaggle.com/prashant111/lightgbm-classifier-in-python

Sponsored Links
SKJブログはTwitterでも役に立つ情報を発信しています!