いつか学びたかったLightGBM

タイトルは[これ](http://www.copymecha.com/index.php)で適当につけています。

# きっかけ
一回試してみたかったので、以下を読みながら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

## ファイルアップロード
```python
# filesモジュールでアップロード
from google.colab import files
uploaded = files.upload()
```
これでファイル選択できるようになるので、対象ファイルをアップロードする

## ファイル読み込み
```python
# 訓練データの読み込み
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)
```
アップロードしたファイルを読み込み、想定通りの件数になっているかを確認

## データの確認
```python
print(X_train.min())
print(X_train.max())

print(y_train[423])

plt.imshow(X_train[423], cmap = plt.cm.gray)
plt.show()
```
どういう中身かを確認

## データ前処理
### 正規化
```python
# 処理前データ確認
X_train[0,10:15,10:15]
```
```python
# 訓練/テストデータの正規化
X_train = X_train / 255 
X_test = X_test/ 255
```
```python
# 処理後データ確認
X_train[0,10:15,10:15]
```

### リサイズ
```python
# 処理前データ確認
print(X_train.shape)
print(X_test.shape)
```
```python
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 モデル訓練
```python
# 訓練・テストデータの設定
train_data = lgb.Dataset(X_train, label=y_train)
eval_data = lgb.Dataset(X_test, label=y_test, reference= train_data)
```
ハイパーパラメータはとりあえず初期値としておく。

このタイミングでvalidation用のデータとラベルが必要なのだが、どちらも無いことに気が付く。
なので訓練データを分割して作成することにする。
件数的にはとりあえず3割ぐらいにしておく。

```python
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:]
```

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

```python
# 訓練・テストデータの設定
train_data = lgb.Dataset(train_imgs, label=train_lbls)
eval_data = lgb.Dataset(validation_imgs, label=validation_lbls, reference= train_data)
```
```python
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となります。

## 学習
```python
gbm = lgb.train(
params,
train_data,
valid_sets=eval_data,
num_boost_round=150,
verbose_eval=5,
)
```
サンプルでは100回訓練していたが、もちっと繰り返してもよさそうだったので150回にしてみる。

## 検証
```python
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)
```
```python
accuracy_score(validation_lbls, y_pred)
```
>0.9426111111111111

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

## 予測
```python
_predicts = gbm.predict(X_test)
predicts

predicts = []
for x in _predicts:
	predicts.append(np.argmax(x))
```
# ファイル作成
```python
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できる。

# 提出してみた結果
<img width="708" alt="2019-05-06_19h21_08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106639/41c9b930-355b-5ac6-e7cf-9bcb9ae44c7d.png">

>0.862

こんなもんか。過学習しちゃっているような感じなのかなぁ。
もちっとパラメータをいじってみたり、XGBoostも試してみての差を見てみたりしたいけど、とりあえず連休はもうおしまいなのである。。

今回作ったものは以下
https://github.com/RyoNakamae/LightGBM