關於loss不收斂的一些建議

之前訓練網路時,會先編寫學習率隨訓練epoch的增加而逐漸減低的函式,然後選取一個相對較大的學習率(一般從e-2量級開始),選取一個epoch能夠接受的batchsize,如果loss穩定下降較快,則開始訓練。從未體驗過學習率和batchsize搭配之難。

最近新看了一篇論文ABCNN(有空再細講),採用開源的tensorflow工程訓練一下,效果很好,因工程需要,開始將其移植到pytorch框架下,移植完畢後,關於loss函式遇到不少問題,在此記錄。

學習率隨epoch降低的函式

def adjust_learning_rate(learning_rate, learning_rate_decay, optimizer, epoch):

“”“Sets the learning rate to the initial LR multiplied by learning_rate_decay(set 0。98, usually) every epoch”“”

learning_rate = learning_rate * (learning_rate_decay ** epoch)

for param_group in optimizer。param_groups:

param_group[‘lr’] = learning_rate

return learning_rate

loss不收斂

此處包含兩種情況,一種是loss一直在震盪,一種是loss下降一點後不再下降到理想水平,而驗證集上的表現保持不變。

1。保持需要的batchsize不變;

2。檢視是否有梯度回傳,檢視程式碼如下:

for name, parms in model。named_parameters():

print(‘——>name:’, name, ‘——>grad_requirs:’, parms。requires_grad, ‘——weight’, torch。mean(parms。data), ‘ ——>grad_value:’, torch。mean(parms。grad))

3。檢視資料是否有問題,如標籤錯亂等現象;

4。調節學習率,從大向小調,建議每次除以5;我的專案即是因為學習率過大過小都不收斂引起的;

5。如果學習率調好後,需要調節batchsize大小,如batchsize調大2倍,則將學習率對應調大(專案測試調大2~3倍OK),反之,學習率對應調小

loss震盪過於明顯/loss劇烈抖動

Loss曲線震盪分析_東方佑_51CTO部落格

前言

在訓練網路的時候,常常會出現loss出現非常明顯的劇烈抖動情況,雖然大多數情況可以繼續訓練下去,但是實際上還是預示著問題存在。而且,有不同維度的問題,這也需要不同的解決方法,但是具體究竟是哪一種,還得具體情況具體分析。

無過擬合

是否找到合適的loss函式

:在深度學習裡面,不同的loss針對的任務是有不同的,有些loss函式比較通用例如L1/L2等,而如perceptual loss則比較適合在影象恢復/生成領域的任務上。當loss出現問題的適合,想一想,是不是loss設定的有問題,別人在此領域的任務的方法是否也使用和你一樣的loss。

batch size是否合適

:batch size的問題一般是較大會有比較好的效果,一是更快收斂,二是可以躲過一些區域性最優點。但是也不是一味地增加batch size就好,太大的batch size 容易陷入sharp minima,泛化性不好。

較小的batch size,類別較多時,可能會使得網路有明顯的震盪

。batch size增大,處理相同的資料量速度加快;隨著batch size增大,達到相同精度的epoch數量變多;因此基於上述兩種情況,batch size要除錯到合適的數值;過大的batchsize會讓網路收斂到不好的區域性最優點;過小的batchsize訓練速度慢,訓練不收斂;

具體的batch size需要根據訓練集資料內容和數量進行除錯

是否使用合適的**函式

:一般來說,都幾乎使用RELU作為全域性**函式,儘可能少的使用sigmoid**函式(**範圍太小),容易造成梯度彌散、消失

學習率

學習率太大,一步前進的路程太長,會出現來回震盪的情況

,但是學習率太小,收斂速度會比較慢。在自己訓練新網路時,可以從0。1開始嘗試,如果loss不下降的意思,那就降低,除以10,用0。01嘗試,一般來說0。01會收斂,不行的話就用0。001。 學習率設定過大,很容易震盪。不過剛剛開始不建議把學習率設定過小,尤其是在訓練的開始階段。在開始階段我們不能把學習率設定的太低否則loss不會收斂。我的做法是逐漸嘗試,

從0.1,0.08,0.06,0.05 ......逐漸減小直到正常為止,有的時候學習率太低走不出低估,把衝量提高也是一種方法,適當提高mini-batch值,使其波動不大

是否選擇合適的最佳化演算法

:一般來說,我都使用Adam作為最佳化器(預設引數)。如果經過仔細調整的SGD演算法效能可能更好,但是時間上不太允許這樣做。

檢查輸入資料格式等資訊是否正確

:資料輸入不對包括資料的格式不是網路模型指定的格式,導致訓練的時候網路學習的資料不是想要的; 此時會出現loss曲線震盪;解決辦法:檢查資料輸入格式,資料輸入的路徑;

資料和標籤

:資料分類標註是否準確?資料是否乾淨?資料庫太小一般不會帶來不收斂的問題,只要你一直在train總會收斂(跑飛了不算)。反而不收斂一般是由於樣本的資訊量太大導致網路不足以fit住整個樣本空間。樣本少只可能帶來過擬合的問題。

網路設定不合理

如果做很複雜的分類任務,卻只用了很淺的網路,可能會導致訓練難以收斂,換網路

換網路換網路,重要的事情說三遍,或者也可以嘗試

加深當前網路

過擬合

**透過提前終止確定最優模型

:**在訓練的過程中,可能會出現訓練到最後的精度竟然還不如前面的epoch高,那麼可以直接終止訓練,然後將之前的model作為best model,之後使用這個model即可

Regularization(正則化)

:透過正則化進行約束,一般的方法可以透過最佳化器的權重衰減方法,即訓練到後期,透過衰減因子使權重的梯度下降越來越緩慢。或者BN、Dropout以及L1/L2

調整網路結構

:一句話,你的網路結構出了問題,是錯誤的,沒有科學性的

增加訓練資料量

:資料集太小太少,且沒有進行資料增強,就可能導致過擬合

loss變nan

現象:loss進行一次反傳後,loss變nan;

排查順序:

訓練資料(包括label)中有無異常值(nan, inf等);

網路中有無除法,確保分母不會出現0, 分母可以加一個eps=1e-8;

網路中有無開根號(torch。sqrt), 保證根號下>=0, 我的程式即是由此引起的(未保證不出現0或者極小正值的情況),解決也是加一個eps=1e-8。

1。如果在迭代的100輪以內,出現NaN,一般情況下的原因是因為你的學習率過高,需要降低學習率。可以不斷降低學習率直至不出現NaN為止,一般來說低於現有學習率1-10倍即可。

2。如果當前的網路是類似於RNN的迴圈神經網路的話,出現NaN可能是因為梯度爆炸的原因,一個有效的方式是增加“gradient clipping”(梯度截斷來解決)

3。可能用0作為了除數;

4。可能0或者負數作為自然對數

5。需要計算loss的陣列越界(尤其是自己,自定義了一個新的網路,可能出現這種情況)

6。在某些涉及指數計算,可能最後算得值為INF(無窮)(比如不做其他處理的softmax中分子分母需要計算exp(x),值過大,最後可能為INF/INF,得到NaN,此時你要確認你使用的softmax中在計算exp(x)做了相關處理(比如減去最大值等等))

7、對於層數較多的情況,各層都做batch_nomorlization;

8、對設定Weights權重使用tf。truncated_normal(0, 0。01, [3,3,1,64])生成,同時值的均值為0,方差要小一些;

9、啟用函式可以使用tanh;

10、減小學習率lr。

典型例項

梯度爆炸

原因:梯度變得非常大,使得學習過程難以繼續

現象:觀察log,注意每一輪迭代後的loss。loss隨著每輪迭代越來越大,最終超過了浮點型表示的範圍,就變成了NaN。

措施:

1、資料歸一化(減均值,除方差,或者加入normalization,例如BN、L2 norm等);

2、更換引數初始化方法(對於CNN,一般用xavier或者msra的初始化方法);

3、減小學習率、減小batch size;

4、加入gradient clipping;

5、減小solver。prototxt中的base_lr,

至少減小一個數量級。如果有多個loss layer,需要找出哪個損失層導致了梯度爆炸,並在train_val。prototxt中減小該層的loss_weight,而非是減小通用的base_lr。

6、設定clip gradient,

用於限制過大的diff

7、弱化場景,將你的樣本簡化,各個學習率等引數採用典型配置,比如10萬樣本都是同一張複製的,讓這個網路去擬合,如果有問題,則是網路的問題。否則則是各個引數的問題。

8、如果是網路的問題,則透過不斷加大樣本的複雜度和調整網路(調整擬合能力)來改變。

9、引數的微調,我個人感覺是在網路的擬合能力和樣本的複雜度匹配的情況下,就是可以train到一定水平,然後想進行進一步最佳化的時候採用。

10、引數的微調,樓上說得幾個也算是一種思路吧,其他的靠自己去積累,另外將weights視覺化也是一個細調起來可以用的方法,現在digits tf裡面都有相關的工具。

不當的損失函式

原因:有時候損失層中loss的計算可能導致NaN的出現。比如,給InfogainLoss層(資訊熵損失)輸入沒有歸一化的值,使用帶有bug的自定義損失層等等。

現象:觀測訓練產生的log時一開始並不能看到異常,loss也在逐步的降低,但突然之間NaN就出現了。

措施:看看你是否能重現這個錯誤,在loss layer中加入一些輸出以進行除錯。

不當的輸入

原因:

training sample中出現了髒資料

髒資料的出現導致我的logits計算出了0,0傳給

即nan。

所以我透過設定batch_size = 1,shuffle = False,一步一步地將sample定位到了

所有可能的髒資料,刪掉

。期間,刪了好幾個還依然會loss斷崖為nan,不甘心,一直定位一直刪。終於tm work out!

之所以會這樣,是因為我的實驗是實際業務上的

真實資料

,有實際經驗的就知道的,現實的資料非常之髒,基本上資料預處理佔據我80%的精力。

現象:每當學習的過程中碰到這個錯誤的輸入,就會變成NaN。觀察log的時候也許不能察覺任何異常,loss逐步的降低,但突然間就變成NaN了。

措施:重整你的資料集,確保訓練集和驗證集裡面沒有損壞的圖片。除錯中你可以使用一個簡單的網路來讀取輸入層,有一個預設的loss,並過一遍所有輸入,如果其中有錯誤的輸入,這個預設的層也會產生NaN。