#1.目的
MNISTデータ(0から9までの手書き数字)を分類するモデル(多クラス分類モデル)をLightGBMを用いて実装する。
その際にハイパーパラメータをチューニングしないモデルと、チューニングしたモデルの精度を比較する。
チューニングはOptunaを用いて行う。その使い方等をメモ程度に置いておく。
#2.MNISTとは
MNISTはMixed National Institute of Standards and Technology databaseの略で、手書き数字画像60,000枚とテスト画像10,000枚を集めた、画像データセット。
0~9の手書き数字が教師ラベルとして各画像に与えられている。
詳しくはこちら
今回はKerasで公開されているMNISTを使う。
#3.使用ライブラリ
import lightgbm as lgb
import pandas as pd
import numpy as np
from tensorflow.keras.datasets import mnist
from sklearn.model_selection import train_test_split
#評価指標は以下4つ
from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.metrics import f1_score
#4.モデル作成(チューニング無しVer)
コードは以下の通り。
# Kerasに付属の手書き数字画像データをダウンロード
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# training set を学習データ(X_train, y_train)と検証データ(X_valid, y_valid)に8:2で分割する
X_train,X_valid,y_train,y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=0)
# 各データを1次元に変換
X_train = X_train.reshape(-1,784)
X_valid = X_valid.reshape(-1,784)
X_test = X_test.reshape(-1,784)
#正規化
X_train = X_train.astype('float32')
X_valid = X_valid.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_valid /= 255
X_test /= 255
# 訓練・検証データの設定
train_data = lgb.Dataset(X_train, label=y_train)
eval_data = lgb.Dataset(X_valid, label=y_valid, reference=train_data)
#パラメータ設定
params = {
'task': 'train', #トレーニング用
'boosting_type': 'gbdt', #勾配ブースティング決定木
'objective': 'multiclass', #目的:多値分類
'num_class': 10, #分類クラス数
'metric': 'multi_logloss' #評価指標は多クラスのLog損失
}
#モデル作成
gbm = lgb.train(
params,
train_data,
valid_sets=eval_data,
num_boost_round=100,
early_stopping_rounds=10
)
こうして出来たモデルを用いて、testデータの予測を行う。
そのコードは以下。
#予測
preds = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred = []
for x in preds:
y_pred.append(np.argmax(x))
この予測y_predと教師データy_testを用いて評価指標を計算し、出力する。
ここでは説明を省くが、各評価指標についてはこちらでわかりやすく解説されている。
#正解率など評価指標の計算
print('正解率(accuracy_score):{}'.format(accuracy_score(y_test, y_pred)))
#適合率、再現率、F1値はマクロ平均を取る
print('再現率(recall_score):{}'.format(recall_score(y_test, y_pred, average='macro')))
print('適合率(precision_score):{}'.format(precision_score(y_test, y_pred, average='macro')))
print('F1値(f1_score):{}'.format(f1_score(y_test, y_pred, average='macro')))
出力結果は以下の通りである。
正解率(accuracy_score):0.9766
再現率(recall_score):0.9764353695446533
適合率(precision_score):0.9764619714676158
F1値(f1_score):0.9764381593889576
ハイパーパラメータをチューニングしなくても、正解率が97.6%とかなり高い数字になっている。
また、モデルの作成もわずか数分で終わったのでlightGBMの凄さが分かる。
ちなみに、上の4つの指標はclassification_reportを使うことで以下のように一気に計算できることが分かった。0から9の各数字についての指標も出してくれる。
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred, digits=5)) #digitsは表示桁数
precision recall f1-score support
0 0.97778 0.98776 0.98274 980
1 0.99120 0.99207 0.99163 1135
2 0.97282 0.97093 0.97187 1032
3 0.96961 0.97921 0.97438 1010
4 0.97955 0.97556 0.97755 982
5 0.97860 0.97422 0.97640 892
6 0.98115 0.97808 0.97961 958
7 0.97835 0.96693 0.97260 1028
8 0.96735 0.97331 0.97032 974
9 0.96822 0.96630 0.96726 1009
accuracy 0.97660 10000
macro avg 0.97646 0.97644 0.97644 10000
weighted avg 0.97661 0.97660 0.97660 10000
#5.モデル作成 (チューニング有り)
次に、ハイパーパラメータをチューニングしてモデルを作る。
Optunaを用いてチューニングをするため、以下をインポート。
# LightGBM Optunaを使ってハイパーパラメタチューニング
import optuna.integration.lightgbm as lgb_o
あとはパラメータ設定までは同じコードになる。
# Kerasに付属の手書き数字画像データをダウンロード
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# training set を学習データ(X_train, y_train)と検証データ(X_valid, y_valid)に8:2で分割する
X_train,X_valid,y_train,y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=0)
# 各データを1次元に変換
X_train = X_train.reshape(-1,784)
X_valid = X_valid.reshape(-1,784)
X_test = X_test.reshape(-1,784)
#正規化
X_train = X_train.astype('float32')
X_valid = X_valid.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_valid /= 255
X_test /= 255
# 訓練・検証データの設定
train_data = lgb_o.Dataset(X_train, label=y_train)
eval_data = lgb_o.Dataset(X_valid, label=y_valid, reference=train_data)
# パラメータ設定
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'multiclass',
'num_class': 10,
'metric': 'multi_logloss'
}
Optunaでパラメータチューニングをする。
チューニングされたパラメータをbest_paramsとして表示させてみる。
この実行はかなり時間がかかるため注意。(特徴量が784列分もあるためだろうか。。?)
best_params= {}
#モデル作成
gbm = lgb_o.train(
params,
train_data,
valid_sets=[train_data, eval_data], #ここがチューニングしない場合と違う
num_boost_round=100,
early_stopping_rounds=10
)
best_params = gbm.params
best_params
チューニングされたパラメータが次のようになった。
{'task': 'train',
'boosting_type': 'gbdt',
'objective': 'multiclass',
'num_class': 10,
'metric': 'multi_logloss',
'feature_pre_filter': False,
'lambda_l1': 1.4206763386954986e-08,
'lambda_l2': 0.0004970847920575475,
'num_leaves': 37,
'feature_fraction': 0.784,
'bagging_fraction': 0.9001098370804291,
'bagging_freq': 3,
'min_child_samples': 20,
'num_iterations': 100,
'early_stopping_round': 10}
先と同じように、このモデルを用いて予測してみる。
#予測
preds = gbm.predict(X_test, num_iteration=gbm.best_iteration)
y_pred = []
for x in preds:
y_pred.append(np.argmax(x))
#正解率など評価指標の計算
print('正解率(accuracy_score):{}'.format(accuracy_score(y_test, y_pred)))
#適合率、再現率、F1値はマクロ平均を取る
print('再現率(recall_score):{}'.format(recall_score(y_test, y_pred, average='macro')))
print('適合率(precision_score):{}'.format(precision_score(y_test, y_pred, average='macro')))
print('F1値(f1_score):{}'.format(f1_score(y_test, y_pred, average='macro')))
チューニング後のモデルでの評価指数が以下となる。
正解率(accuracy_score):0.978
再現率(recall_score):0.9778036992589507
適合率(precision_score):0.9778601989002492
F1値(f1_score):0.9778154759497815
チューニング前と比べてみる
正解率(accuracy_score):0.9766
再現率(recall_score):0.9764353695446533
適合率(precision_score):0.9764619714676158
F1値(f1_score):0.9764381593889576
いずれも0.0014ほど上昇していることが分かる。
が、チューニングせずともかなり良い精度で分類できていたので、あまりその恩恵を感じられなかった。。
次は違うデータセットでも試してみたい。