タイトルはこれで適当につけています。
きっかけ
一回試してみたかったので、以下を読みながら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できる。
提出してみた結果
0.862
こんなもんか。過学習しちゃっているような感じなのかなぁ。
もちっとパラメータをいじってみたり、XGBoostも試してみての差を見てみたりしたいけど、とりあえず連休はもうおしまいなのである。。
今回作ったものは以下
https://github.com/RyoNakamae/LightGBM