人工知能エンジニア修行日記

人工知能エンジニアを目指して修行します

6章 学習に関するテクニック ハイパーパラメータ(hyper-parameter)の検証 『ゼロから作るDeep Learning』

f:id:kaeken:20161112201050p:plain 6章 最後に、ハイパーパラメータ(hyper-parameter)の検証について。

ハイパーパラメータは人為的に試行錯誤しながら調整がするので検証が必要である。

しかし、汎化性能を評価するには、最終的に評価するための本番のテストデータが使えない。

そこで、ハイパーパラメータ専用の検証データvalidation dataを訓練データから分離して検証が必要。

次にハイパーパラメータの最適化は以下の手順で行う。

ステップ0:ハイパーパラメータの範囲を10^-3から10^3などの範囲でざっくり指定する
↓
ステップ1:設定された範囲からランダムにサンプリングする
↓
ステップ2:サンプリングされた値で学習し、検証データで認識精度を評価する。ただしエポックは小さく設定しておく。
↓
ステップ3:ステップ1−2を一定回数(100回など)繰り返し、さらにハイパーパラメータの範囲を狭めていく

以下、ハイパーパラメータの最適化をしていくサンプルコード

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')

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 = 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()
plt.savefig('hyperparameter_optimization_save.png')

以下、実行結果とグラフ(検証データ:実線、訓練データ:点線)

=========== Hyper-Parameter Optimization Result ===========
Best-1(val acc:0.77) | lr:0.00757365478878744, weight decay:4.612578587686737e-07
Best-2(val acc:0.76) | lr:0.007433061542623079, weight decay:1.0344933324636203e-07
Best-3(val acc:0.76) | lr:0.0069442614770180554, weight decay:3.5083892101171096e-07
Best-4(val acc:0.76) | lr:0.00786568274411027, weight decay:2.128003163191066e-06
Best-5(val acc:0.76) | lr:0.009977128388270529, weight decay:5.034544517460483e-07
Best-6(val acc:0.75) | lr:0.006651552434409378, weight decay:9.770817734920272e-08
Best-7(val acc:0.72) | lr:0.006787994923710441, weight decay:1.538788471810156e-08
Best-8(val acc:0.69) | lr:0.005309167300473579, weight decay:7.092519614173075e-07
Best-9(val acc:0.65) | lr:0.004171913096880143, weight decay:3.7534502493629925e-07
Best-10(val acc:0.64) | lr:0.0036177186197134006, weight decay:3.419650414313433e-07
Best-11(val acc:0.55) | lr:0.004657623419509241, weight decay:1.3150591635104507e-05
Best-12(val acc:0.45) | lr:0.002600118643665009, weight decay:6.101368606264845e-06
Best-13(val acc:0.42) | lr:0.0014781722372820818, weight decay:6.555096165359242e-07
Best-14(val acc:0.35) | lr:0.00112894903737969, weight decay:6.35129426114411e-08
Best-15(val acc:0.35) | lr:0.001122116867042764, weight decay:2.8146026473082357e-05
Best-16(val acc:0.31) | lr:0.0019504915229014257, weight decay:4.5759839999890385e-08
Best-17(val acc:0.29) | lr:0.001523802348758895, weight decay:1.3898280180637062e-07
Best-18(val acc:0.22) | lr:0.0011058186976594655, weight decay:2.7502587954667702e-08
Best-19(val acc:0.21) | lr:0.0004461535980654127, weight decay:7.17362065696084e-05
Best-20(val acc:0.21) | lr:0.0006365291753190107, weight decay:4.449107159041093e-06

f:id:kaeken:20161112201050p:plain

Best-5ぐらいまで順調に学習が進んでいることが分かる。

学習係数lrが0.001から0.01

Weight decay係数が10^-8から10^-6

このように、試行錯誤しながら、最適なハイパーパラメータを人為的に特定していかなければならない。

最後に、6章のまとめ問題

・パラメータの更新方法には、SGD以外に◯◯、◯◯、◯◯、などの手法がある
・重みの初期値の推奨は、「◯◯」や「◯◯」である
・◯◯を用いることで学習を加速でき、初期値に左右されにくい◯◯になる
・過学習を抑制する正則化技術として、◯◯や◯◯がある
・ハイパーパラメータの探索は、◯◯するのが効率的である