ハイパラメータの検証
今日もアウトプット。
ハイパーパラメータとは
各層のニューロンの数とか、バッチサイズ、学習係数やWeight decayなど、
人の手で適正な値を設定しないといけないパラメータのことである。
検証データ
これまでは、学習データとテストデータの2つを用いていた。
用途としては、訓練データで学習を行い、テストデータでモデルの評価を行う。
そうすることで過学習になってないか?汎用性能はいいのか?とか判断してた。
じゃぁ、ハイパーパラメータもテストデータで検証…
はどうやらNGみたいだ。
それは、
テストデータを使ってハイパーパラメータを調整すると、
テストデータに対して適応してしまい、テストデータに過学習を起こしてしまうからである。
この、ハイパーパラメータの調整を実施するときのデータを検証データ(Validation data)という。
訓練データ→学習用
テストデータ→モデルの汎化性能用
検証データ→ハイパーパラメータ用
モデルによっては、
検証データを無理やり作らないといけな場合がある。
(x_train , t_train) , (x_test,y_test) = load_mnist() x_train , t_train = shuffle_dataset(x_train,t_train) validation_rate=0.2 validation_data = int(x_train.shape[0]*0.2) x_val = xtrain[:validation_data] t_val = t_train[:validation_data]modwe x_train = x_train[validation_data:] t_train = t_train[validation_data:]
学習データの2割をバリデーションデータとして使うコード。
ハイパーパラメータの最適化
では、やはり本筋になってくるのが、
パラメータの最適化(SGDやAdamとか…)が重要であったように、
ハイパーパラメータの最適化の手法も重要になってくる。
上記の通り、ハイパーパラメータは人の手で操作しなくてはいけないパラメータ。
残念ながら、パラメータみたいにテンプレート化されてない。
そこで、まずは下記の手順で行ってみるのが正。
①ハイパーパラメータの範囲の設定(ざっくりでOK)
②設定した範囲の中からランダムにサンプリング。
③ステップ2でサンプリングされたハイパーパラメータの値で学習を実施。
検証データで精度を確認する。(エポック数は小さくでOK。本は50回とかでやってた)
④繰り返す
じゃあサンプルコードを丸記載。
今回は学習係数とWeight decayの2つを変える。
import sys, os sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定 import numpy as np import matplotlib.pyplot as plt from dataset.mnist import load_mnist from common.multi_layer_net import MultiLayerNet from common.util import shuffle_dataset from common.trainer import Trainer (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True) # 高速化のため訓練データの削減 x_train = x_train[:500] t_train = t_train[:500] # 検証データの分離 validation_rate = 0.20 validation_num = int(x_train.shape[0] * validation_rate) x_train, t_train = shuffle_dataset(x_train, t_train) x_val = x_train[:validation_num] t_val = t_train[:validation_num] x_train = x_train[validation_num:] t_train = t_train[validation_num:] def __train(lr, weight_decay, epocs=50): network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10, weight_decay_lambda=weight_decay) trainer = Trainer(network, x_train, t_train, x_val, t_val, epochs=epocs, mini_batch_size=100, optimizer='sgd', optimizer_param={'lr': lr}, verbose=False) trainer.train() return trainer.test_acc_list, trainer.train_acc_list # ハイパーパラメータのランダム探索====================================== optimization_trial = 100 results_val = {} results_train = {} for _ in range(optimization_trial): # 探索したハイパーパラメータの範囲を指定=============== weight_decay = 10 ** np.random.uniform(-8, -4) lr = 10 ** np.random.uniform(-6, -2) # ================================================ val_acc_list, train_acc_list = __train(lr, weight_decay) print("val acc:" + str(val_acc_list[-1]) + " | lr:" + str(lr) + ", weight decay:" + str(weight_decay)) key = "lr:" + str(lr) + ", weight decay:" + str(weight_decay) results_val[key] = val_acc_list results_train[key] = train_acc_list # グラフの描画======================================================== print("=========== Hyper-Parameter Optimization Result ===========") graph_draw_num = 20 col_num = 5 row_num = int(np.ceil(graph_draw_num / col_num)) i = 0 for key, val_acc_list in sorted(results_val.items(), key=lambda x:x[1][-1], reverse=True): print("Best-" + str(i+1) + "(val acc:" + str(val_acc_list[-1]) + ") | " + key) plt.subplot(row_num, col_num, i+1) plt.title("Best-" + str(i+1)) plt.ylim(0.0, 1.0) if i % 5: plt.yticks([]) plt.xticks([]) x = np.arange(len(val_acc_list)) plt.plot(x, val_acc_list) plt.plot(x, results_train[key], "--") i += 1 if i >= graph_draw_num: break plt.show()
上記のコードの中で、一つまたもや恥ずかしい話で、
マイナスの数字を累乗したらなんで小数点が増えるのか、についてまた一つ知った。
10の4乗=10000
10の3乗=1000
10の2乗=100
10の1乗=10
10の0乗=1
10の-1乗=0.1
10の-2乗=0.01
↓結果です。
これを見ると、一番上のところがうまく最適化していることがわかる。
一応まとめ。
最適化の手法についてはSGD Adamとかあって、個々の目的は同じ。最適な重みやバイアスの数値を見つけて、
差分が最小になるように学習を重ねていくって話。
でも、複雑なネットワークや、学習データが少なかったりすると過学習が起こるので、
過学習を抑える努力がいる。
その方法としては、DropoutとかWeight decayといった方法がある。
また、ハイパーパラメータについても過学習、性能の良いモデルの構築には重要になる。
では、また。