引言
最近依舊在做命名實體識別的任務消请,一直在想如何能在保證效率的前提下杂腰,提升BERT+BiLSTM+CRF這個主流模型的準確率复亏≈和蓿“ 達觀杯 ”的獲獎方案中有的隊伍使用了Lookahead+Adam的優(yōu)化器,所以我也打算從優(yōu)化器的方向入手看看能否有效果的提升缔御。本以為BERT中使用的是用爛了的Adam抬闷,一看源碼發(fā)現是重寫的優(yōu)化器,叫AdamWeightDecayOptimizer耕突,本來Adam都沒太搞懂笤成,又來一個WeightDecay,一起學一下吧眷茁。
不查不知道炕泳,一查嚇一跳,2014年被提出的Adam優(yōu)化器的收斂性被證明是錯誤的上祈,之前大部分機器學習框架中對于Adam的權重衰減的實現也都是錯誤的喊崖。關注其收斂性的論文也獲得了ICLR 2017的Best Paper,在2017年的論文《Fixing Weight Decay Regularization in Adam》中提出了一種新的方法用于修復Adam的權重衰減錯誤雇逞,命名為AdamW。實際上茁裙,L2正則化和權重衰減在大部分情況下并不等價塘砸,只在SGD優(yōu)化的情況下是等價的。而大多數框架中對于Adam+L2正則使用的是權重衰減的方式晤锥,兩者不能混為一談掉蔬。
先回顧一下Adam優(yōu)化器的前置知識廊宪,并結合源碼理解Adam優(yōu)化器,再來看AdamW與之的不同之處女轿,本文依舊不會有復雜的數學公式箭启,相關實現以python代碼的形式展示。
Adam前置知識
1. 梯度下降法
最基本的優(yōu)化方法蛉迹,沿著負梯度的方向更新參數傅寡,實現如下:
# 梯度下降法
x += - learning_rate * dx
其中l(wèi)earning_rate是超參數代表學習率,被更新的變量為x北救,其梯度為dx荐操,梯度->位置,很好理解珍策。
但是梯度下降法相關的優(yōu)化方法容易產生震蕩托启,且容易被困在鞍點,遲遲不能到達全局最優(yōu)值攘宙。
2. 動量法
動量法是一類從物理中的動量獲得啟發(fā)的優(yōu)化方法屯耸,可以簡單理解為:當我們將一個小球從山上滾下來時,沒有阻力的話蹭劈,它的動量會越來越大疗绣,但是如果遇到了阻力,速度就會變小链方。實現如下:
# 動量法
v = mu * v - learning_rate * dx # 梯度影響速度
x += v # 速度決定位置
變量v的初始值被定為0持痰,超參數mu在優(yōu)化過程中被視為動量,其物理意義可以視為摩擦系數祟蚀,加入的這一項工窍,可以使得梯度方向不變的維度上速度變快,梯度方向有所改變的維度上的更新速度變慢前酿,這樣就可以加快收斂并減小震蕩患雏。和之前不同的是梯度不會直接對位置造成影響,梯度->速度->位置罢维。
3. RMSprop
RMSprop是一種自適應學習率方法淹仑,依舊是基于梯度對位置進行更新。為了消除梯度下降中的擺動肺孵,加入了梯度平方的指數加權平均匀借。梯度大的指數加權平均就大,梯度小的指數加權平均就小平窘,保證各維度的梯度都在一個良機吓肋,進而減少擺動。
關于指數加權平均的通俗理解可以參考https://zhuanlan.zhihu.com/p/29895933
# RMSprop
cache = decay_rate * cache + (1 - decay_rate) * dx**2 # 梯度平方的指數加權平均
x += - learning_rate * dx / (np.sqrt(cache) + eps) # 基于梯度更新
其中decay_rate和eps都是超參數瑰艘,每一步的變量cache的值都不同是鬼,所以可以看做自適應得對學習率進行調整肤舞。
還有一些其他效果較好的優(yōu)化器,由于這些前置知識已經足夠理解Adam了均蜜,所以在此不做過多介紹李剖。
Adam
Adam可以看做動量法和RMSprop的結合
# Adam
m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)
對于m和v的處理,同樣使用了指數加權平均囤耳。相比于RMSprop篙顺,梯度換為了平滑的m,而cache的處理基本沒有變化紫皇。超參數beta1和beta2的初始值接近于1慰安,因此,計算出的偏差項接近于0聪铺。
AdamW
AdamW是在Adam+L2正則化的基礎上進行改進的算法化焕。
使用Adam優(yōu)化帶L2正則的損失并不有效。如果引入L2正則項铃剔,在計算梯度的時候會加上對正則項求梯度的結果撒桨。那么如果本身比較大的一些權重對應的梯度也會比較大,由于Adam計算步驟中減去項會有除以梯度平方的累積键兜,使得減去項偏小凤类。按常理說,越大的權重應該懲罰越大普气,但是在Adam并不是這樣谜疤。而權重衰減對所有的權重都是采用相同的系數進行更新,越大的權重顯然懲罰越大现诀。在常見的深度學習庫中只提供了L2正則夷磕,并沒有提供權重衰減的實現。
圖片中紅色是傳統(tǒng)的Adam+L2 regularization的方式仔沿,綠色是Adam+weightdecay的方式坐桩。可以看出兩個方法的區(qū)別僅在于“系數乘以上一步參數值“這一項的位置封锉。再結合代碼來看一下AdamW的具體實現绵跷。
以下代碼來自https://github.com/macanv/BERT-BiLSTM-CRF-NER/blob/master/bert_base/bert/optimization.py中的AdamWeightDecayOptimizer中的apply_gradients函數中,BERT中的優(yōu)化器就是使用這個方法成福。在代碼中也做了一些注釋用于對應之前給出的Adam簡化版公式碾局,方便理解∨可以看出update += self.weight_decay_rate * param這一句是Adam中沒有的擦俐,也就是Adam中綠色的部分對應的代碼,weightdecay這一步是是發(fā)生在Adam中需要被更新的參數update計算之后握侧,并且在乘以學習率learning_rate之前蚯瞧,這和圖片中的偽代碼的計算順序是完全一致的∑非妫總之一句話埋合,如果使用了weightdecay就不必再使用L2正則化了。
# m = beta1*m + (1-beta1)*dx
next_m = (tf.multiply(self.beta_1, m) + tf.multiply(1.0 - self.beta_1, grad))
# v = beta2*v + (1-beta2)*(dx**2)
next_v = (tf.multiply(self.beta_2, v) + tf.multiply(1.0 - self.beta_2, tf.square(grad)))
# m / (np.sqrt(v) + eps)
update = next_m / (tf.sqrt(next_v) + self.epsilon)
# Just adding the square of the weights to the loss function is *not*
# the correct way of using L2 regularization/weight decay with Adam,
# since that will interact with the m and v parameters in strange ways.
#
# Instead we want ot decay the weights in a manner that doesn't interact
# with the m/v parameters. This is equivalent to adding the square
# of the weights to the loss with plain (non-momentum) SGD.
if self._do_use_weight_decay(param_name):
update += self.weight_decay_rate * param
update_with_lr = self.learning_rate * update
# x += - learning_rate * m / (np.sqrt(v) + eps)
next_param = param - update_with_lr
原有的英文注釋中也解釋了Adam和傳統(tǒng)Adam+L2正則化的差異萄传,好了到這里應該能理解Adam了甚颂,并且也能理解AdamW在Adam上的改進了。
Lookahead秀菱,RAdam?
Lookahead和RAdam都是比較新的優(yōu)化器振诬,具體原理在此不過多介紹。但是我有疑問需要大神來解答一下衍菱。
在BERT中引入優(yōu)化器的源碼中有這樣一句注釋
# It is recommended that you use this optimizer for fine tuning, since this
# is how the model was trained (note that the Adam m/v variables are NOT
# loaded from init_checkpoint.)
也就是說在微調BERT的時候強烈建議使用AdamW優(yōu)化器赶么。在自己的NER數據集上使用6層BERT,AdamW能得到98%左右的F1值脊串,我嘗試使用了RAdam辫呻,Lookahead+RAdam和Lookahead+AdamW,還有Ranger琼锋,得到的效果都非常差放闺,要不就0的F1值,要不就是30%左右缕坎,好像完全沒有效果怖侦。
項目參考源碼:https://github.com/macanv/BERT-BiLSTM-CRF-NER
RAdam,Lookahead:https://github.com/lifeiteng/Optimizers
https://github.com/michaelrzhang/lookahead
Ranger:https://github.com/jyhengcoder/Ranger_tensorflow
請大神解答一下,為什么達觀杯有的隊伍用了Lookahead取得了較好的效果(可能在訓練BERT模型時就用了Lookahead谜叹?)匾寝。那是什么原因導致的我實驗中微調官方提供的BERT模型時Lookahead和RAdam效果不好,是超參數的問題還是預訓練好的BERT模型不適用于這些優(yōu)化器那叉谜?
參考資料
https://www.zhihu.com/question/323747423/answer/790457991
https://www.cnblogs.com/guoyaohua/p/8542554.html
https://zhuanlan.zhihu.com/p/63982470
https://zhuanlan.zhihu.com/p/38945390