我搭的神經網路不work該怎麼辦!看看這11條新手最容易犯的錯誤

王瀚宸 @王小新 編譯自 TheOrangeDuck

量子位 出品 | 公眾號 QbitAI

每個人在調試神經網路的時候,大概都遇到過這樣一個時刻:

什麼鬼!我的神經網路就是不work!到底該怎麼辦!

機器學習博客TheOrangeDuck的作者,育碧蒙特利爾實驗室的機器學習研究員Daniel Holden,也就是這個人:

根據自己工作中失敗的教訓,整理了一份神經網路出錯原因清單,一共11條。量子位搬運過來,各位被神經網路虐待的時候,可以按圖索驥。

當然,也祝你們看了這11條之後,功力大進,煉丹順利。

1. 忘了數據規範化

What?

在使用神經網路的過程中,非常重要的一點是要考慮好怎樣規範化(normalize)你的數據。

這一步不能馬虎,不正確、仔細完成規範化的話,你的網路將會不能正常工作。

因為規範化數據這個重要的步驟在深度學習圈中早已被大家熟知,所以論文中很少提到,因此常會成為初學者的阻礙。

How?

大體上說,規範化是指從數據中減去平均值,然後再除以標準差的操作。

通常這個操作對每個輸入和輸出特徵是分別完成的,但你可能會想同時對一整組的特徵進行規範化,再挑出其中一些特殊處理。

Why?

我們需要規範化數據的主要原因是,在神經網路中幾乎所有的數據傳輸途徑中,都是假設輸入和輸出的數據結構滿足標準差接近於1,平均值幾乎為0。這個假設在深度學習中的每個地方都會出現,從權重因子的初始化,到活化函數,再到訓練網路的優化演算法。

And?

一個未訓練的神經網路通常輸出的結果範圍從-1到1。如果你希望它的輸出值在其它的範圍,比如說RGB圖片表示顏色的值域就是0到255,你將會遇到麻煩。

當期望的輸出值是255,神經網路開始訓練時情況會極不穩定,因為實際產生的值為-1或者1,對大多數用來訓練神經網路的優化演算法來說,這和255相比都有巨大的誤差。這將會產生巨大的梯度,你的訓練誤差很可能會爆表。

就算碰巧在你訓練的起始階段,誤差沒有爆表,這個過程仍然是沒有意義的,因為神經網路在向錯誤的方向學習和發展。

如果你先將你的數據規範化(在這個例子中你可以將RGB值除以128然後減去1),那麼這些情況就都不會發生。

總體來說,神經網路中各種特徵的值域決定了他們的重要性。

如果輸出中的一項特徵的值域很大,那麼意味著與其他特徵相比,它將會產生更大的誤差。同樣地,輸入中值域大的特徵也會支配著網路,在下游中引起更大的變化。

因此,僅僅依靠許多神經網路庫中的自動規範化,盲目地減去平均值後再除以方差,並不總是合適的做法。可能有這樣一個輸入特徵,取值範圍通常在0到0.001之間,它的值域這麼小是因為這個特徵不重要,還是因為它與其他特徵相比有著更小的單位呢?這決定了你要不要將它規範化。

類似地,還要謹慎對待那些值域較小的特徵,因為它們的標準差可能很小,接近或者嚴格等於0。如果你對它們進行規範化,可能會產生NaN(Not a Number)的錯誤。

這種情況需要謹慎地對待,要仔細琢磨你的這些特徵真正代表著什麼,以及考慮規範化的過程是為了將所有輸入的特徵等價。

這是少數幾個我認為在深度學習中需要人類完成的任務。

2. 沒有檢查結果

What?

當你訓練網路經過了幾個epoch之後,誤差(error)開始下降了——成功!

但這是否意味著你完成了訓練呢?博士能畢業了嗎?很不幸,答案是否定的。

你的代碼中,基本上還肯定還存在一些錯誤。這個bug可能存在於數據預處理,訓練網路甚至是最後給出推斷結果的過程中。

只是誤差開始下降,並不意味著你的網路學到了「真功夫」。

How?

毋庸置疑,在數據傳輸過程中的每個階段檢查數據正確性都很重要,通常這意味著要通過一些方法來對結果進行可視化。

如果你的數據是圖像,那麼情況就很簡單,相應的動畫數據很好生成。但如果你的數據比較奇葩,也要找出一種合適的方法,能夠在預處理、網路訓練和數據傳遞的每個階段來檢查數據的正確性,將其與原始的真實數據比較。

Why?

跟傳統的編程過程不同,機器學習系統失敗時都不出聲。

在傳統編程中,我們習慣了當遭遇狀況時計算機報錯,隨後我們可以結合報錯內容來debug。不幸的是,這個過程並不適用於機器學習應用。

所以,我們需要極其小心地在每個階段檢查我們的過程是否有問題,從而能夠察覺到bug的產生,以及在需要回頭仔細檢查代碼的時候及時發現。

And?

有許多種方法來檢查你的網路是否有效。其中之一是要明確訓練誤差的意義。將在訓練集上運行的神經網路的輸出結果進行可視化——輸出結果跟實際情況相比怎樣?

你可能看到在訓練過程中誤差從100下降到1,但最終結果仍然是不可用的,因為在實際場景中誤差為1仍然是不可接受的結果。如果網路在訓練集上有效,那麼再在驗證集上測試——它是否同樣適用於之前沒有見過的數據呢?

我的建議是從一開始就可視化所有過程,不要等網路不奏效時再開始做,在你開始嘗試不同的神經網路結構之前,你要確保整個流程沒有一絲差錯。這是你能夠正確評估不同網路模型的唯一方式。

3. 忘了數據預處理

What?

絕大部分數據都很tricky。我們認為非常相似的事物,從數據上看可能擁有完全不同的數值表達形式。

就拿視頻中的人物動作來說,如果我們數據是在一個特定地點或是特點方向上,記錄人物的關節相對於錄像中心的3D位置,那麼換一個方向或地點,可能同一套動作會擁有完全不同的數字表達形式。

因此,我們需要用新的方式來表達我們的數據,比如說放到一些本地參考系中(諸如跟人物的質心相關的一些),讓相似的動作有相似的數值表達。

How?

思考你的特徵具體代表著什麼——你是否可以在它們上面做一些簡單的變換,來確保用來代表相似事物的數據點通常具有相似的數值表達?是否存在一個本地坐標系,能以一種不同的形式更自然地表達你的數據?比如說一個更好的色彩空間?

Why?

神經網路只對輸入的數據做一些最基本的假設,但是這些假設中有一條,是認為這些數據分布的空間是連續的,即對於空間中的大部分,兩個數據點間的點類似這兩個數據點的「混合」,相鄰的數據點在某種意義上代表著相似的事情。

當數據空間中存在較大的不連續時,亦或者一大組分開的數據均代表著同一件事情時,將會使得學習任務的難度大大增加。

And?

理解數據預處理(preprocess)的另一種方式,是把它作為減少由排列組合導致的數據激增的一種嘗試。

舉例來說,如果一個基於人物動作訓練過的神經網路需要學習在該人物在各個地點、各個方向上的同一組動作,那麼將會耗費大量的資源,學習的過程將會是冗餘的。

4. 忘了正則化

What?

正則化(regularization)方式是訓練神經網路時另一個不可或缺的方面,通常以Dropout層、小雜訊或某種形式的隨機過程等方式應用到網路中。

即使在你看來當前數據規模遠大於參數規模,或是在某些情況下,不會出現過擬合效應,或者就算出現也不影響效果,你仍然應該加入Dropout層或一些其他形式的小雜訊。

How?

向神經網路添加正則化的一種最基本方法,是在網路中的每個線性層(如卷積層或稠密層)前加入Dropout層。

在開始設置Dropout值時,可定義中等值到較低值,如0.25或0.1。你可根據網路的各項指標,來判斷過擬合程度並進行調整,若仍覺得不可能出現過擬合效應,可以將Dropout值設置到非常小,如0.01。

Why?

正則化方式不僅僅是用來控制過擬合效應,它在訓練過程中引入了一些隨機過程,在某種意義上「平滑」了代價格局。這種方式可加快訓練進程,有助於處理數據中的異常值,並防止網路中出現極端權重結構。

And?

跟Dropout層一樣,數據增強或者其他類型的雜訊也可作為正則化方式。

雖然Dropout層通常被認為是一種將許多隨機子網路的預測結果結合起來的技巧,但它也可看作是一種通過在訓練時產生多種輸入數據的相似變體來動態擴展訓練集大小的方法。

而且要知道,防止過擬合併提高網路準確性的最佳方法是向神經網路輸入大量且不重複的訓練數據。

5. 設置了過大的批次大小

What?

設置了過大的批次(batch)大小,可能會對訓練時網路的準確性產生負面影響,因為它降低了梯度下降的隨機性。

How?

要在可接受的訓練時間內,確定最小的批次大小。一個能合理利用GPU並行性能的批次大小可能不會達到最佳的準確率,因為在有些時候,較大的批次大小可能需要訓練更多迭代周期才能達到相同的正確率。

在開始時,要大膽地嘗試很小的批次大小,如16、8,甚至是1。

Why?

較小的批次大小能帶來有更多起伏、更隨機的權重更新。這有兩個積極的作用,一是能幫助訓練「跳出」之前可能卡住它的局部最小值,二是能讓訓練在「平坦」的最小值結束,著通常會帶來更好的泛化性能。

And?

數據中其他的一些要素有時也能起到批次大小的作用。

例如,以兩倍大小的先前解析度來處理圖像,得到的效果與用四倍批次大小相似。

做個直觀的解釋,考慮在CNN網路中,每個濾波器的權重更新值將根據輸入圖像的所有像素點和批次中的每張圖像來進行平均,將圖像解析度提高兩倍,會產生一種四倍像素量同樣的平均效果,與將批次大小提高四倍的做法相似。

總體來說,最重要的是要考慮到,在每次迭代中有多少決定性的梯度更新值被平均,並確保平衡好這種不利影響與充分利用GPU並行性能的需求之間的關係。

6. 使用了不適當的學習率

What?

學習率對網路的訓練效果有著巨大的影響。如果你剛入門,使用了常用深度學習框架中給出的各種默認參數,那幾乎可以肯定,你的設置不對。

How?

關閉梯度裁剪,找出學習率的最大值,也就是在訓練過程中不會讓誤差爆表的上限值。把學習率設置為比這小一點的值,很可能就非常接近最佳學習率了。

Why?

大多數深度學習框架會默認啟用梯度裁剪方式。這種方式通過限制在每個步驟中可以調整權重的數量,來防止訓練過程中優化策略出現崩潰。

當你的數據中包含許多異常值,會造成大幅度的梯度和權重更新,這種限制特別有用。但是在默認情況下,這種方式也會使用戶很難手動找到最佳學習率。

我發現,大多數深度學習新手會設置過高的學習率,並且通過梯度裁剪來緩解此問題,使得全局訓練過程變慢,並且改變學習率後的網路效果不可預測。

And?

如果你好好清洗了數據,刪除了大多數異常值,並設置了合理的學習率,實際上並不需要梯度裁剪方式。如果關閉了梯度裁剪之后里,你發現網路偶爾會發生訓練錯誤,那就再打開它。

但是要記住,發生訓練錯誤通常表明你的數據還存在一些問題,梯度裁剪只是一個暫時的解決方法。

7. 在最後一層使用了錯誤的激活函數

What?

在最後一層中,不合理的激活函數有時會導致你的網路無法輸出所需值的全部範圍。最常見的錯誤是,在最後一層使用ReLU函數,導致網路只能產生正值輸出。

How?

如果要實現回歸任務,那麼在最後一層通常不需要使用任何激活函數,除非你詳細地知道你想輸出哪一類值。

Why?

再次確認下你輸入數據的實際意義,以及歸一化後的具體範圍。

很可能出現的情況是,網路的輸出區間是從負無窮大到正無窮大,在這種情況下,你不該在最後一層使用激活函數。

如果網路輸出只在某個區間內有意義,則需使用一些特殊的激活函數。比如,某網路輸出為[0, 1]區間的概率值,根據這種情況可使用S形激活函數。

And?

在選擇最後一層的激活函數時,有許多玄學。

在神經網路產生輸出後,你也許會將其裁剪到[-1, 1]的區間。那將這個裁剪過程當作最後一層的激活函數,這似乎是有意義的,因為這將確保網路中的誤差函數不會對不在[-1, 1]區間外的值進行懲罰。但是沒有誤差意味著區間外的這些值沒有對應梯度,這在某些情況下無法進行網路訓練。

或者,你也可以在最後一層使用tanh函數,因為這個激活函數的輸出範圍是[-1, 1]。但是這也可能出現問題,因為這個函數在1或-1附近時斜率變得很大,可能會使權重大幅增加,最終只產生-1或1的輸出。

一般來說,最好的選擇通常是採用求穩策略,在最後一層不使用任何激活函數,而不是試圖使用一些機靈的技巧,可能會適得其反。

8. 網路含有不良梯度

What?

使用ReLU激活函數的深度神經網路通常可能遭受由不良梯度引起的所謂「死神經元」。這可能會對網路的性能產生負面影響,或者在某些情況下導致完全無法訓練。

How?

如果發現在epoch到epoch之間,你的訓練誤差不會變化,就可能是由於ReLU激活函數導致了所有的神經元已經死亡。

換一個激活函數試試,比如leaky ReLU或ELU,看看是不是還會發生同樣的情況。

Why?

ReLU激活函數的梯度對於正值為1,對於負值為0。這是因為對於小於0的輸入來說,輸入的很小變化不會影響輸出。

這可能看起來不是一個問題,因為正值的梯度很大。但是很多層疊在一起,而負權重可以將具有強梯度的大正值變為0梯度的負值。

你可能經常發現,無論輸入什麼,部分甚至全部隱藏單元對成本函數都是0梯度,這就是所謂的網路「已死」,所有權重都無法更新。

And?

很多運算都具有0梯度,比如裁剪,舍入,或取最大/最小值,如果用它們來計算成本函數相對於權重的導數,都會產生不良梯度。

如果它們出現在你的符號圖的任何地方,要非常小心,因為它們常常會導致意想不到的困難。

9. 沒有正確地初始化網路權重

What?

如果你沒有正確地初始化神經網路權重,那麼神經網路很可能根本就無法訓練。

神經網路中有許多其他組件,會假設你的權重初始化是正確的,或者標準的,它們會將權重設置為0,或者使用你自定義的隨機初始化權重,於是將不會起作用。

How?

「he」、「lecun」或「xavier」權重初始化都是受歡迎的選擇,在幾乎任何情況下都應該很好地工作。只要選一個(我最喜歡的是「lecun」)就行了。

但是一旦神經網路開始訓練了,你就可以自由的實驗,尋找最適合你任務的權重了。

Why?

你可能聽說過,可以使用「小隨機數」初始化神經網路權重,但並不那麼簡單。

所有上述初始化方法都是靠複雜、細緻的數學發現的,這也說明了為什麼它們是最佳的。

更重要的是,很多其他神經網路組件都是圍繞這些初始化構建的,並根據經驗使用它們進行測試 ,自己進行初始化可能會導致難以復現其他研究者的成果。

And?

其他層可能也需要仔細地初始化。網路偏移被初始化為零,而其他更複雜的層(如參數激活函數)可能會帶有自己的初始化,這與正確的同樣重要。

10. 神經網路太深了

What?

網路越深越好?不一定。

當你對網路進行基準測試,試著在一些任務上提高1%的準確度時,更深的網路通常會表現得更好。

但是如果你設計的淺層(3到5層)網路沒有學習任何特徵,那麼可以保證,你設計的超深(如100層)網路也會沒有效果,甚至更加糟糕。

How?

剛開始時,先試試淺層神經網路的效果,通常是3到8層。只有當你的網路有一定效果,要開始著手提高準確率時,再去研究更深層網路的結構。

Why?

看起來似乎是當有人決定堆一個幾百層的神經網路時,神經網路模型忽然得到了突破性的結果,但事實並非如此。

在過去十年中,神經網路中所有改良技術所取得的微小進步,對淺層和深層網路都同樣適用。如果你的網路不起作用,這很可能不是深度問題,是其他方面出錯了。

And?

從小型網路開始訓練,也意味著能更快地訓練網路、更快地完成模型推理及更快地完成不同結構和參數配置的迭代過程。首先,與僅堆疊更多網路層相比,上面提到的所有方面將對模型準確率產生更大的影響。

11. 隱藏unit的數量不對

What?

某些情況下,隱藏單元太多或者太少,都會導致網路難以訓練。

隱藏單元太少,可能會沒有能力表達所需的任務;太多單元又會導致網路緩慢、難以訓練,殘留雜訊難以消除。

How?

開始時的隱藏單元數量,最好在256到1024個之間。

然後,看一下研究類似應用的研究人員使用了多少個隱藏單元,找找靈感。如果你的同行所用的數量和上面給出的數字相差很遠,可能會有一些特殊的原因,這可能對你來說很重要。

Why?

當決定隱藏單元的數量時,關鍵在於考慮要表達你想通過網路傳遞的信息,所需的最小真實值是多少。

然後,考慮到dropout、網路使用冗餘的表示、以及為你的估計留一點餘地,可以將這個數字放大一點。

如果你正在做分類,可以使用類別數目的5到10倍,作為隱藏單元的數量;如果做回歸,可以使用輸入或輸出變數數目的2到3倍。

當然,所有這些都高度依賴於環境,沒有簡單的自動解決方案,決定隱藏單元數量時,最重要的依然是直覺。

And?

實際上,與其他因素相比,隱藏單元的數量通常對神經網路性能影響很小,而在許多情況下,高估所需隱藏單位的數量除了拖慢訓練速度之外,也不會有什麼負面影響。

一旦網路開始正常工作,如果你還是擔心,可以嘗試各種不同數量的隱藏單元,並測量網路精度,直到找到最合適的設置。

歡迎大家關注我們的專欄:量子位 - 知乎專欄

誠摯招聘

量子位正在招募編輯/記者,工作地點在北京中關村。期待有才氣、有熱情的同學加入我們!相關細節,請在量子位公眾號(QbitAI)對話界面,回復「招聘」兩個字。

量子位 QbitAI

?? ? 追蹤AI技術和產品新動態


推薦閱讀:

學習筆記 | 吳恩達之神經網路和深度學習
Python · 神經網路(二*)· 層
深度壓縮之蒸餾模型
「深度學習」和「多層神經網路」的區別?
關於深度神經網路壓縮(下)

TAG:机器学习 | 深度学习DeepLearning | 神经网络 |