Posted at

いつか学びたかったLightGBM

タイトルはこれで適当につけています。


きっかけ

一回試してみたかったので、以下を読みながらLightGBMを試してみた。

https://www.codexa.net/lightgbm-beginner/

インストールするのがめんどくさかったので、環境はGoogle Colabにて。


座学


アンサンブル

アンサンブルの手法は大きく分けると「バギング」「ブースティング」「スタッキング」となる

参考:https://www.codexa.net/what-is-ensemble-learning/


  • バギング


    • 並列的に学習



  • ブースティング


    • 前の弱学習器の結果を次の学習データに反映



  • スタッキング


    • 二段階に学習を積み上げる



※今回はアンサンブルの各手法を掘り下げるのが目的ではないので、ふんわりとさせておく。


LightGBMって?


勾配ブースティングは複数の弱学習器(LightGBMの場合は決定木)を一つにまとめるアンサンブル学習の「ブースティング」を用いた手法

仮に最初に訓練を行う決定木を1号、次に訓練を行う決定木を2号としましょう。まずは決定木(1号)でモデル訓練を行い推測結果を評価します。決定木(1号)の推測結果と実際の値の「誤差」を訓練データとして、決定木(2号)の訓練を行います。N号の決定木はN-1号の決定木の誤差(Residuals)を学習するわけです。



実践

せっかくなので、こっちのデータでやってみる

https://prob.space/c

ただ、codexaとコンペだとファイルの数が違うけど、いったん無視して続けてみる。


kmnist-test-imgs.npz

kmnist-train-imgs.npz

kmnist-train-labels.npz



ファイルアップロード

# filesモジュールでアップロード

from google.colab import files
uploaded = files.upload()

これでファイル選択できるようになるので、対象ファイルをアップロードする


ファイル読み込み

# 訓練データの読み込み

X_train = np.load('kmnist-train-imgs.npz')['arr_0']
y_train = np.load('kmnist-train-labels.npz')['arr_0']
# テストデータの読み込み
X_test = np.load('kmnist-test-imgs.npz')['arr_0']

# データのサイズ確認
print(X_train.shape)
print(X_test.shape)

アップロードしたファイルを読み込み、想定通りの件数になっているかを確認


データの確認

print(X_train.min())

print(X_train.max())

print(y_train[423])

plt.imshow(X_train[423], cmap = plt.cm.gray)
plt.show()

どういう中身かを確認


データ前処理


正規化

# 処理前データ確認

X_train[0,10:15,10:15]

# 訓練/テストデータの正規化

X_train = X_train / 255
X_test = X_test/ 255

# 処理後データ確認

X_train[0,10:15,10:15]


リサイズ

# 処理前データ確認

print(X_train.shape)
print(X_test.shape)

X_train = X_train.reshape(X_train.shape[0], 784)

X_test = X_test.reshape(X_test.shape[0], 784)

# 処理後データ確認
print(X_train.shape)
print(X_test.shape)


LightGBM モデル訓練

# 訓練・テストデータの設定

train_data = lgb.Dataset(X_train, label=y_train)
eval_data = lgb.Dataset(X_test, label=y_test, reference= train_data)

ハイパーパラメータはとりあえず初期値としておく。

このタイミングでvalidation用のデータとラベルが必要なのだが、どちらも無いことに気が付く。

なので訓練データを分割して作成することにする。

件数的にはとりあえず3割ぐらいにしておく。

validation_size = 0.3

np.random.seed()
perm_idx = np.random.permutation(len(X_train))
_X_train = X_train[perm_idx]
_y_train = y_train[perm_idx]

# split train and validation
validation_num = int(len(y_train) * validation_size)

validation_imgs = _X_train[:validation_num]
validation_lbls = _y_train[:validation_num]

train_imgs = _X_train[validation_num:]
train_lbls = _y_train[validation_num:]

このデータを使って、改めてモデル設定

# 訓練・テストデータの設定

train_data = lgb.Dataset(train_imgs, label=train_lbls)
eval_data = lgb.Dataset(validation_imgs, label=validation_lbls, reference= train_data)

params = {

'task': 'train',
'boosting_type': 'gbdt',
'objective': 'multiclass',
'num_class': 10,
'verbose': 2,
}


boosting_typeのgbdtは「Gradient Boosting Decistion Tree」の略です。分類クラスは3つ以上ですので多項分類(multiclass)を目的(objective)に指定します。またターゲットクラスは10種類の平仮名ですのでnum_classは10となります。



学習

gbm = lgb.train(

params,
train_data,
valid_sets=eval_data,
num_boost_round=150,
verbose_eval=5,
)

サンプルでは100回訓練していたが、もちっと繰り返してもよさそうだったので150回にしてみる。


検証

y_pred = []

eval_predicts = gbm.predict(validation_imgs)
for x in eval_predicts:
y_pred.append(np.argmax(x))

confusion_matrix(validation_lbls, y_pred)

accuracy_score(validation_lbls, y_pred)


0.9426111111111111


思ってた以上よりいい感じ。高すぎる気もするけど。


予測

_predicts = gbm.predict(X_test)

predicts

predicts = []
for x in _predicts:
predicts.append(np.argmax(x))


ファイル作成

submit = pd.DataFrame(data={"ImageId": [], "Label": []})

submit.ImageId = list(range(1, _predicts.shape[0]+1))
submit.Label = predicts

submit.to_csv("submit.csv", index=False)
files.download('submit.csv')

これで予測した結果をDLできる。


提出してみた結果

2019-05-06_19h21_08.png


0.862


こんなもんか。過学習しちゃっているような感じなのかなぁ。

もちっとパラメータをいじってみたり、XGBoostも試してみての差を見てみたりしたいけど、とりあえず連休はもうおしまいなのである。。

今回作ったものは以下

https://github.com/RyoNakamae/LightGBM