神經網絡中,除了尋找最優的權重和偏置等參數,設定合適的超參數也同樣重要。比如各層神經元的數量、batch_size的取值、參數更新時的學習率、權值衰減係數或學習的epoch等。超參數尋找過程一般會伴隨很多試錯,所以用儘可能高效的方法找到超參數非常重要。
下面介紹一種實踐性的方法,說實話是有一些實踐者經驗的感覺。
步驟0
設定超參數的範圍。
說明:超參數的範圍只要“大致的指定”就可以了。也就是類似於,確定量級即可。
步驟1
從設定的超參數範圍中隨機採樣。
例如:權重衰減係數和學習率隨機採樣
weight_decay = 10 ** np.random.uniform(-8, -4)
lr = 10 ** np.random.uniform(-6, -2)
步驟2
使用步驟1中採樣到的超參數的值進行學習,通過驗證數據評估識別精度(但是要將epoch設置的很小)。
Q&A:
1、什麼是驗證數據?
驗證數據是用來調整超參數的數據。根據不同的數據集,有的會事先分成訓練數據、驗證數據、測試數據三部分,有的只分成訓練數據和測試數據兩部分,有的不進行分割。例如mnist數據集分爲訓練數據和測試數據。在這種情況下獲得驗證數據最簡單的方法就是從訓練數據中事先分割20%作爲驗證數據。
# 分離驗證數據
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:]
2、爲什麼不用測試數據而用驗證數據?
因爲如果使用測試數據調整超參數,超參數的值會對測試數據發生過擬合,只擬合測試數據,模型的泛化能力低。
3、epoch爲什麼要設置的很小?
深度學習往往要花費很多時間,因此,在超參數的尋找過程中,需要儘早放棄那些不符合邏輯的超參數。於是,在超參數的最優化中,減少學習的epoch,縮短一次評估所需的時間是一個不錯的辦法。
步驟3
重複步驟1和步驟2(100次等),根據它們的識別精度的結果,縮小超參數範圍。
反覆進行上述操作,不斷縮小超參數的範圍,在縮小到一定程度時,從該範圍選出一個超參數的值。這是其中一種方法。在超參數的最優化中,如果需要更精煉的方法,可以使用貝葉斯最優化。參考論文:Practical Bayesian Optimization of Machine Learning Algorithms.
python實現:
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)
# 爲了提高訓練數據的速度只取500張圖片
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
# 超大參數隨機搜索======================================
epoch = 100
results_val = {}
results_train = {}
for _ in range(epoch):
# 指定搜索到的超參數的範圍===============
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 = 4
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)
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()
實驗結果:
從這個結果可以看出,前七張圖片學習都進行的很順利。因此可以將學習率的範圍定在0.001到0.01、權值衰減係數在之間。然後在這個縮小的範圍中重複相同的操作。這樣就能縮小到合適的超參數的存在範圍,然後在某個階段,選擇一個最終的超參數的值。