編者按:還認為調參是“玄學”?快來看Mikko Kotila分享的調參心得。
TL;DR
只需采用正確的過程,為給定的預測任務找到頂尖的超參數配置并非難事。超參數優化主要有三種方法:手工、機器輔助、基于算法。本文主要關注機器輔助這一方法。本文將介紹我是如何優化超參數的,如何證實方法是有效的,理解為何起效。我把簡單性作為主要原則。
模型表現
關于模型表現,首先需要指出的是,使用精確度(及其他魯棒性更好的測度)等衡量模型表現可能有問題。例如,假設一個二元預測任務中只有1%的樣本值為1,那么預測所有值為0的模型將達到近乎完美的精確度。采用更合適的測度可以克服這類問題,但限于本文的主題,我們不會詳細討論這些。我們想要強調的是,優化超參數的時候,這是一個非常重要的部分。即使我們采用了世界上最酷炫的模型(通常是非常復雜的模型),但如果評估模型所用的是無意義的測度,那到頭來不過是白費工夫。
別搞錯;即使我們確實正確使用了表現測度,我們仍然需要考慮優化模型的過程中發生了什么。一旦我們開始查看驗證集上的結果,并基于此做出改動,那么我們就開始制造傾向驗證集的偏差。換句話說,模型的概括性可能不怎么好。
更高級的全自動(無監督)超參數優化方法,首先需要解決以上兩個問題。一旦解決了這兩個問題——是的,存在解決這兩個問題的方法——結果測度需要實現為單一評分。該單一評分將作為超參數優化過程所依據的測度。
工具
本文使用了Keras和Talos。Talos是我創建的超參數優化方案,它的優勢在于原樣暴露了Keras,沒有引進任何新語法。Talos把超參數優化的過程從若干天縮短到若干分鐘,也使得優化過程更有意思,而不是充滿了痛苦的重復。
你可以親自嘗試Talos:
pip install talos
或者在GitHub上查看它的代碼和文檔:autonomio/talos
但我打算在本文中分享的信息,提出的觀點,是關于優化過程的,而不是關于工具的。你可以使用其他工具完成同樣的過程。
自動化超參數優化及其工具最主要的問題之一,是你常常偏離原本的工作方式。預測任務無關的超參數優化的關鍵——也是所有復雜問題的關鍵——是擁抱人機之間的協作。每次試驗都是一個學習更多(深度學習的)實踐經驗和技術(比如Keras)的機會。不應該因為自動化過程而失去這些機會。另一方面,我們應該移除優化過程中明顯多余的部分。想象一下在Jupyter notebook中按幾百次shift-enter(這一快捷鍵表示執行代碼),然后在每次執行時等待一兩分鐘。總之,在現階段,我們的目標不應該是全自動方法,而是最小化讓人厭煩的重復多余部分。
開始掃描超參數
在下面的例子中,我使用的是Wisconsin Breast Cancer數據集,并基于Keras構建了以下模型:
def breast_cancer_model(x_train, y_train, x_val, y_val, params):
model = Sequential()
model.add(Dense(10, input_dim=x_train.shape[1],
activation=params['activation'],
kernel_initializer='normal'))
model.add(Dropout(params['dropout']))
hidden_layers(model, params, 1)
model.add(Dense(1, activation=params['last_activation'],
kernel_initializer='normal'))
model.compile(loss=params['losses'],
optimizer=params['optimizer'](lr=lr_normalizer(params['lr'],params['optimizer'])),
metrics=['acc', fmeasure])
history = model.fit(x_train, y_train,
validation_data=[x_val, y_val],
batch_size=params['batch_size'],
epochs=params['epochs'],
verbose=0)
return history, model
定義好Keras模型后,通過Python字典指定初始參數的邊界。
p = {'lr': (0.5, 5, 10),
'first_neuron':[4, 8, 16, 32, 64],
'hidden_layers':[0, 1, 2],
'batch_size': (1, 5, 5),
'epochs': [150],
'dropout': (0, 0.5, 5),
'weight_regulizer':[None],
'emb_output_dims': [None],
'shape':['brick','long_funnel'],
'optimizer': [Adam, Nadam, RMSprop],
'losses': [logcosh, binary_crossentropy],
'activation':[relu, elu],
'last_activation': [sigmoid]}
一切就緒,到了開始試驗的時候了:
t = ta.Scan(x=x,
y=y,
model=breast_cancer_model,
grid_downsample=0.01,
params=p,
dataset_name='breast_cancer',
experiment_no='1')
注意,為了節省篇幅,代碼省略了引入語句等非關鍵性的代碼。下文修改超參數字典時也不再貼出代碼。
因為組合太多(超過180000種組合),我隨機從中抽取了1%的組合,也就是1800種組合。
在我的2015年MacBook Air上,試驗1800種組合大約需要10800秒,也就是說,我可以和朋友見一面,喝上一兩杯咖啡。
可視化超參數掃描
試驗了1800種組合后,讓我們看看結果,從而決定如何限制(或者調整)參數空間。
我們使用val_acc(驗證精確度)作為評估模型表現的指標。我們所用的數據集類別比較均衡,因此val_acc是個不錯的測度。在類別顯著失衡的數據集上,精確度就不那么好了。
從上圖我們可以看到,似乎hidden_layers(隱藏層數目)、lr(學習率)、dropout對val_acc的影響較大(負相關性),而正相關性比較強的只有epoch數。
我們將val_acc、hidden_layers、lr、dropout這些單獨抽出來繪制柱狀圖:
其中,y軸為精確度,x軸為epoch數,色彩濃淡表示dropout率,刻面(facet)表示學習率。
上面的兩張圖告訴我們,在這一任務上,看起來相對簡單的模型表現較好。
現在讓我們通過核密度估計仔細看看dropout。縱軸為精確度,橫軸為dropout率。
從圖中我們可以看到,dropout為0到0.1時,更可能得到較高的驗證精確度(縱軸0.9附近),不太可能得到較低的精確度(縱軸0.6附近)。
所以我們下一回合掃描超參數的時候就可以去掉較高的dropout率,集中掃描0到0.2之間的dropout率。
接下來我們再仔細看看學習率(不同優化算法的學習率經過了歸一化處理)。這次我們將繪制箱形圖,縱軸為驗證精確度,橫軸為學習率。
很明顯,在兩種損失函數上,都是較低的學習率表現更好。在logcosh上,高低學習率的差異尤為明顯。另一方面,在所有學習率水平上,交叉熵的表現都超過了logcosh,因此在之后的試驗中,我們將選擇交叉熵作為損失函數。不過,我們仍需要驗證一下。因為我們看到的結果可能是過擬合訓練集的產物,也可能兩者的驗證集損失(val_loss)都很大。所以我們進行了簡單的回歸分析。回歸分析表明,除了少數離散值外,絕大多數損失都聚集在左下角(這正是我們所期望的)。換句話說,訓練損失和驗證損失都接近零。回歸分析打消了我們之前的疑慮。
我覺得我們已經從初次試驗中得到足夠多的信息,是時候利用這些信息開始第二次試驗了。除了上面提到的改動之外,我還額外增加了一個超參數,kernel_initializer。在第一次的試驗中,我們使用的都是默認的高斯分布(normal),其實均勻分布(uniform)也值得一試。所以我在第二次試驗中補上了這一超參數。
第二回合——進一步關注結果
之前我們分析第一回合的結果時,關注的是超參數和驗證精確度的相關性,但并沒有提到驗證精確度有多高。這是因為,在一開始,我們更少關注結果(更多關注過程),我們最終取得較好結果的可能性就越高。也就是說,在開始階段,我們的目標是了解預測任務,而并不特別在意找到答案。而在第二回合,我們仍然不會把全部注意力放到結果上,但查看一下結果也是有必要的。
第一回合的驗證精確度峰值是94.1%的,而第二回合的驗證精確度峰值是96%的。看起來我們的調整還是有效的。當然,峰值可能僅僅源于抽樣的隨機性,所以我們需要通過核密度分布估計來驗證一下:
第一回合的核密度分布估計
第二回合的核密度分布估計
對比核密度分布估計,我們看到,我們的調整確實有用。
下面我們再次繪制相關性熱圖:
我們看到,除了epoch數以外,沒有什么對驗證精確度影響非常大的因素了。在下一回合的試驗中,我們該調整下epoch數。
另外,熱圖并沒有包含所有超參數,比如上一節中的損失函數。在第一次試驗后,我們調整了損失函數,移除了logcosh損失。現在讓我們查看一下優化算法。
首先,上面的箱形圖再次印證了我們之前提到的,較低的epoch數表現不好。
其次,由于在epoch數為100和150的情形下,RMSprop的表現都不怎么好,所以我們將在下一回合的試驗中移除RMSprop。
第三回合——概括性和表現
經過調整之后,第三回合試驗的驗證精確度峰值提高到了97.1%,看起來我們的方向沒錯。在第三回合的試驗中,epoch數我去掉了50,將最高epoch數增加到了175,另外還在100和150中間加了125。從下圖來看,我可能過于保守了,最高epoch數應該再大一點。這讓我覺得……也許最后一回合步子可以邁得大一點?
正如我們一開始提到的,在優化超參數時,同時考慮概括性很重要。每次我們查看在驗證集上的結果并據此調整時,我們增加了過擬合驗證集的風險。模型的概括性可能因此下降,雖然在驗證集上表現更好,但在“真實”數據集上的表現可能不好。優化超參數的時候我們并沒有很好的測試這類偏差的方法,但至少我們可以評估下偽概括性(pseudo-generalization)。讓我們先看下訓練精確度和驗證精確度。
雖然這并不能確認我們的模型概括性良好,但至少回歸分析的結果不錯。接下來讓我們看下訓練損失和驗證損失。
這比訓練精確度和驗證精確度的回歸分析看起來還要漂亮。
在最后一回合,我將增加epoch數(之前提到,第三回合的增加太保守)。另外,我還會增加batch尺寸。到目前為止,我僅僅使用了很小的batch尺寸,這拖慢了訓練速度。下一回合我將把batch尺寸增加到30,看看效果如何。
另外提下及早停止(early stopping)。Keras提供了非常方便的及早停止回調功能。但你可能注意到我沒有使用它。一般來說,我會建議使用及早停止,但在超參數優化過程中加入及早停止不那么容易。正確配置及早停止,避免它限制你找到最優結果,并不那么直截了當。主要是測度方面的原因;首先定制一個測度,然后使用及早停止,效果比較好(而不是直接使用val_acc或val_loss)。雖然這么說,但對超參數優化而言,及早停止和回調其實是很強大的方法。
第四回合——最終結果
在查看最終結果之前,先讓我們可視化一下剩下的超參數(核初始化、batch尺寸、隱藏層、epoch數)的效果。
縱軸為驗證精確度
大部分結果都是肩并肩的,但還是有一些東西突顯出來。如果因為隱藏層層數不同(色彩濃淡)導致驗證精確度下降,那么大多數情形下,下降的都是1個隱藏層的模型。至于batch尺寸(列)和核初始化(行)的差別,很難說出點什么。
下面讓我們看看縱軸為驗證損失的情況:
縱軸為驗證損失
在各種epoch數、batch尺寸、隱藏層層數的組合下,均勻核初始化都能將驗證損失壓得很低。但因為結果不是特別一致,而且驗證損失也不如驗證精確度那么重要,所以我最后同時保留了兩種初始化方案。
最后的贏家
我們在最后時刻想到增加batch尺寸,這個主意不錯。
較小的batch尺寸下,驗證精確度的峰值是97.7%,而較大的batch尺寸(30)能將峰值提升至99.4%。另外,較大的batch尺寸也能使模型更快收斂(你可以在文末的視頻中親眼見證這一點)。老實說,當我發現較大的batch尺寸效果這么好時,我其實又進行了一次試驗。因為只需要改動batch尺寸,不到一分鐘我就配置好了這次試驗,而超參數掃描則在60分鐘內完成了。不過這次試驗并沒有帶來什么新發現,大部分結果都接近100%.
另外我還想分享下精確度熵和損失熵(基于驗證/訓練精確度、驗證/訓練損失的KL散度),它們是一種有效評估過擬合的方法(因此也是間接評估概括性的方法)。
總結
盡可能保持簡單和廣泛
從試驗和假設中分析出盡可能多的結果
在初次迭代時不用在意最終結果
確保采用了恰當的表現測度
記住表現本身并不是全部,提升表現的同時往往會削弱概括性
每次迭代都應該縮減超參數空間和模型復雜性
別害怕嘗試,畢竟這是試驗
使用你可以理解的方法,例如,清晰的可視化描述性統計
-
參數
+關注
關注
11文章
1842瀏覽量
32303 -
數據集
+關注
關注
4文章
1208瀏覽量
24738
原文標題:調參心得:超參數優化之旅
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論