1. 程式人生 > >乾貨|如何除錯神經網路(深度神經網路)?

乾貨|如何除錯神經網路(深度神經網路)?

神經網路的除錯基本上難於絕大多數的程式,因為大部分的神經網路的錯誤不會以型別錯誤或執行時錯誤顯現,他們只是使得網路難以收斂。

如果你是一個新人,這可能會讓你非常沮喪。一個有經驗的網路訓練者可以系統的克服這些困難,儘管存在著大量似是而非的錯誤資訊,比如:

你的網路訓練的不太好。

對缺少經驗的人來說,這個資訊令人卻步;但對有經驗的人來說,這是一個非常好的錯誤訊息。它意味著樣板程式碼已經偏移了正確道路,而且是時候去深入發掘一下原因了。

如何應對NaN

“為什麼出現了NaN?”有時候,這個問題的答案很複雜。但大多數情況是,NaN在前100輪迭代中就出現了,這時候這個答案就非常簡單:學習率太高了。當學習率很高的時候,在訓練的前100輪迭代中就會出現NaN。嘗試不斷的把學習率除以三,直到前一百輪迭代中不再得到NaN,當這樣的嘗試起作用的時候,就得到了不錯的初始學習率。我的經驗是,最優的學習率一般在你得到NaN的學習率的1-10倍以下。

如果是在100輪迭代後出現了NaN,可能有兩個更深層的原因

- 原因1

如果你訓練的是RNN,請確保使用的是“梯度剪裁(clip gradient )”,這可以把全域性的梯度二範數限制在一定的範圍內。RNN傾向於在訓練早期產生梯度,其中10%或者更少的batch會出現學習尖峰,這些尖峰上的梯度值非常大。如果沒有限制幅度,這些尖峰就可能導致NaN。

 clip gradient工作的具體細節如下:

1.在solver中先設定一個clip_gradient 訓練中會把所有權值的梯度diff相加,如果這個值大於clip
2. gradient,就會求一個縮放因子 s = clip_gradient/sum_diff
3. 將所有的權值梯度乘以這個縮放因子,這時得到的梯度才是最後的梯度資訊

- 原因2

如果你寫了定製化的later,這個問題很可能是由這些定製化的layer中一些除零錯誤引發的。還有一個著名的產生NaN的layer就是softmax layer。 softmax的計算在分母和分子中都含有指數函式exp(x),當inf除以inf時就會產生NaN。所以要確定你用的是一個穩定版本的softmax的實現。

當神經網路不再學習的時候該怎麼做?

當你不再碰到NaN的時候,很可能就會遇到這樣一種情況,你的網路平滑的訓練的幾千次,但是loss卻在前幾百個回合後不再減小。如果你是初次構建程式碼庫的話,超過2000次的等待很難給出答案。這不是因為所有網路都能在2000次迭代內開始學習,而是因為你在編碼中引入bug的機率很高,與其等待長時間的迭代,不如早早的進入除錯模式。現在你應該不斷縮小問題的範圍,直到你的網路可以在2000次迭代內開始學習。幸運的是,有兩個不錯的維度來減小複雜度:

1、 把訓練集的樣本量減小到10。任何一個可用的網路通暢都能在幾百個迭代後過擬合十個樣本。而bug則會阻止過擬合發生。如果網路仍然不能過擬合十個樣本,再次確認樣本和label是正確對應的。然後把batch size設為1來檢查batch計算中的錯誤。在code中加入一些log輸出以確保是以你期望的方式執行的。一般來說,通過暴力排查總會找到這些錯誤。一旦網路可以擬合10個樣本,它也可以擬合100個。如果現在可以訓練但不如預期,則可以進入下一個步驟了。

2、解決你感興趣的問題的最簡單版本。如果你在做句子翻譯,先建立一個目標語言的語言模型。當上一步成功了,只給出三個源語言的單詞,嘗試著去預測翻譯的第一個詞。如果你打算從影象中檢測物體,訓練迴歸網路之前試著去分類影象中有多少個物體。在得到網路可以解決的好的子問題以及花費最少的時間來使用程式碼掛接資料之間存在著平衡點。創造力可以起到幫助作用。

為一個新的想法擴大網路的小技巧就是緩慢的縮小上述兩步中的簡化。這是座標上升法的一種形式,而且十分有用。一開始,你可以證明這個網路可以記住少量的樣本,然後可以證明它在一個簡化版的子問題中可以在驗證集上具有泛化能力。緩步提升難度,穩步前進。

有些時候你會發現有些問題本身十分困難,難以在2000次迭代內完成學習。這很棒,但這也很少需要以前那些難度的問題訓練時間的十倍以上的時間。如果真需要這麼多時間,可以嘗試尋找一箇中間的複雜度。

微調超引數

也許你的網路現在開始學習東西了,但你可能發現它不能解決這個問題中最困難的部分。超引數的調整就是其中的關鍵。也許有人下載了一個cnn包然後在上面跑自己的資料集,並告訴你超引數的調整並不會帶來改變。你要認識到他們在用已有的框架解決已有的問題。如果你在使用新架構解決新問題,則必須除錯超引數來獲得一個良好的配置。

1.視覺化是關鍵。不要怕浪費時間去寫一些好用的訓練過程中的視覺化工具。如果你還是從terminal中打印出來的loss裸眼的做視覺化,那那你該考慮一下升級了。

2.權值初始化很重要。一般來說,大一點幅度的初始化權值會好一些,但太大了就會導致NaN。因此初始化權值應該要和學習率一起除錯。

3.確保權值看起來是“健康的”。要了解這是什麼意思,我推薦ipython
notebook開啟現有網路的權值。花一些時間來熟悉在標準資料集(如ImageNet或Penn Tree
Bank)上訓練的成熟網路中的元件的權值直方圖應該是什麼樣子。

4.神經網路不是對輸入的尺度不敏感的,尤其當它使用SGD訓練而不是其他的二階方法訓練,因為SGD不是一個尺度無關的方法。在確定縮放尺度之前,花點時間來嘗試多次縮放輸入的資料和輸出。

5.在訓練結束之前減小學習率總能帶來提升。最佳的decay策略是:在k個epoch後,每n個epoch之後學習率除以1.5(k > n)。

6.使用超引數配置檔案,也可以把超引數寫在code裡。我使用https://github.com/Russell91/tensorbox
中的json檔案,使用命令列載入。應該避免因為超引數的問題來重構程式碼。

7.隨機的搜尋超引數。隨機搜尋可以組合出你想不到的超引數組合, 並且能減少你考慮在已有超引數訓練帶來什麼樣的影響時花費的大量精力。

總結

除錯神經網路花費的勞力比除錯傳統程式要多,因為幾乎所有的錯誤都會反應成一個問題:網路的表現不夠好。儘管如此,二分查詢仍然起作用。

1.調整問題的難度

2.使用少量的樣本

這兩個方法可以幫助你找到最初的問題。然後超引數調整和長時間的等待就可以解決你剩下的問題了。