每個人在調試神經網絡的時候,大概都遇到過這樣一個時刻:
什麼鬼!我的神經網絡就是不 work!到底該怎麼辦!
機器學習部落格 TheOrangeDuck 的作者,育碧蒙特利爾實驗室的機器學習研究員 Daniel Holden,也就是這個人:
根據自己工作中失敗的教訓,整理了一份神經網絡出錯原因清單,一共 11 條。量子位搬運過來,各位被神經網絡虐待的時候,可以按圖索骥。
當然,也祝你們看了這 11 條之後,功力大進,煉丹順利。
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) 的錯誤。
這種情況需要謹慎地對待,要仔細琢磨你的這些特征真正代表着什麼,以及考慮規範化的過程是為了将所有輸入的特征等價。
這是少數幾個我認為在深度學習中需要人類完成的任務。
當你訓練網絡經過了幾個 epoch 之後,誤差(error)開始下降了——成功!
但這是否意味着你完成了訓練呢?博士能畢業了嗎?很不幸,答案是否定的。
你的代碼中,基本上還肯定還存在一些錯誤。這個 bug 可能存在于資料預處理,訓練網絡甚至是最後給出推斷結果的過程中。
隻是誤差開始下降,并不意味着你的網絡學到了 “真功夫”。
毋庸置疑,在資料傳輸過程中的每個階段檢查資料正确性都很重要,通常這意味着要通過一些方法來對結果進行可視化。
如果你的資料是圖像,那麼情況就很簡單,相應的動畫資料很好生成。但如果你的資料比較奇葩,也要找出一種合适的方法,能夠在預處理、網絡訓練和資料傳遞的每個階段來檢查資料的正确性,将其與原始的真實資料比較。
跟傳統的程式設計過程不同,機器學習系統失敗時都不出聲。
在傳統程式設計中,我們習慣了當遭遇狀況時計算機報錯,随後我們可以結合報錯内容來 debug。不幸的是,這個過程并不适用于機器學習應用。
是以,我們需要極其小心地在每個階段檢查我們的過程是否有問題,進而能夠察覺到 bug 的産生,以及在需要回頭仔細檢查代碼的時候及時發現。
有許多種方法來檢查你的網絡是否有效。其中之一是要明确訓練誤差的意義。将在訓練集上運作的神經網絡的輸出結果進行可視化——輸出結果跟實際情況相比怎樣?
你可能看到在訓練過程中誤差從 100 下降到 1,但最終結果仍然是不可用的,因為在實際場景中誤差為 1 仍然是不可接受的結果。如果網絡在訓練集上有效,那麼再在驗證集上測試——它是否同樣适用于之前沒有見過的資料呢?
我的建議是從一開始就可視化所有過程,不要等網絡不奏效時再開始做,在你開始嘗試不同的神經網絡結構之前,你要確定整個流程沒有一絲差錯。這是你能夠正确評估不同網絡模型的唯一方式。
What?
絕大部分資料都很 tricky。我們認為非常相似的事物,從資料上看可能擁有完全不同的數值表達形式。
就拿視訊中的人物動作來說,如果我們資料是在一個特定地點或是特點方向上,記錄人物的關節相對于錄像中心的 3D 位置,那麼換一個方向或地點,可能同一套動作會擁有完全不同的數字表達形式。
是以,我們需要用新的方式來表達我們的資料,比如說放到一些本地參考系中(諸如跟人物的質心相關的一些),讓相似的動作有相似的數值表達。
How?
思考你的特征具體代表着什麼——你是否可以在它們上面做一些簡單的變換,來確定用來代表相似事物的資料點通常具有相似的數值表達?是否存在一個本地坐标系,能以一種不同的形式更自然地表達你的資料?比如說一個更好的色彩空間?
Why?
神經網絡隻對輸入的資料做一些最基本的假設,但是這些假設中有一條,是認為這些資料分布的空間是連續的,即對于空間中的大部分,兩個資料點間的點類似這兩個資料點的 “混合”,相鄰的資料點在某種意義上代表着相似的事情。
當資料空間中存在較大的不連續時,亦或者一大組分開的資料均代表着同一件事情時,将會使得學習任務的難度大大增加。
And?
了解資料預處理(preprocess)的另一種方式,是把它作為減少由排列組合導緻的資料激增的一種嘗試。
舉例來說,如果一個基于人物動作訓練過的神經網絡需要學習在該人物在各個地點、各個方向上的同一組動作,那麼将會耗費大量的資源,學習的過程将會是備援的。
正則化(regularization)方式是訓練神經網絡時另一個不可或缺的方面,通常以 Dropout 層、小噪聲或某種形式的随機過程等方式應用到網絡中。
即使在你看來目前資料規模遠大于參數規模,或是在某些情況下,不會出現過拟合效應,或者就算出現也不影響效果,你仍然應該加入 Dropout 層或一些其他形式的小噪聲。
向神經網絡添加正則化的一種最基本方法,是在網絡中的每個線性層(如卷積層或稠密層)前加入 Dropout 層。
在開始設定 Dropout 值時,可定義中等值到較低值,如 0.25 或 0.1。你可根據網絡的各項名額,來判斷過拟合程度并進行調整,若仍覺得不可能出現過拟合效應,可以将 Dropout 值設定到非常小,如 0.01。
正則化方式不僅僅是用來控制過拟合效應,它在訓練過程中引入了一些随機過程,在某種意義上 “平滑” 了代價格局。這種方式可加快訓練程序,有助于處理資料中的異常值,并防止網絡中出現極端權重結構。
跟 Dropout 層一樣,資料增強或者其他類型的噪聲也可作為正則化方式。
雖然 Dropout 層通常被認為是一種将許多随機子網絡的預測結果結合起來的技巧,但它也可看作是一種通過在訓練時産生多種輸入資料的相似變體來動态擴充訓練集大小的方法。
而且要知道,防止過拟合并提高網絡準确性的最佳方法是向神經網絡輸入大量且不重複的訓練資料。
設定了過大的批次(batch)大小,可能會對訓練時網絡的準确性産生負面影響,因為它降低了梯度下降的随機性。
要在可接受的訓練時間内,确定最小的批次大小。一個能合理利用 GPU 并行性能的批次大小可能不會達到最佳的準确率,因為在有些時候,較大的批次大小可能需要訓練更多疊代周期才能達到相同的正确率。
在開始時,要大膽地嘗試很小的批次大小,如 16、8,甚至是 1。
較小的批次大小能帶來有更多起伏、更随機的權重更新。這有兩個積極的作用,一是能幫助訓練 “跳出” 之前可能卡住它的局部最小值,二是能讓訓練在 “平坦” 的最小值結束,着通常會帶來更好的泛化性能。
資料中其他的一些要素有時也能起到批次大小的作用。
例如,以兩倍大小的先前分辨率來處理圖像,得到的效果與用四倍批次大小相似。
做個直覺的解釋,考慮在 CNN 網絡中,每個濾波器的權重更新值将根據輸入圖像的所有像素點和批次中的每張圖像來進行平均,将圖像分辨率提高兩倍,會産生一種四倍像素量同樣的平均效果,與将批次大小提高四倍的做法相似。
總體來說,最重要的是要考慮到,在每次疊代中有多少決定性的梯度更新值被平均,并確定平衡好這種不利影響與充分利用 GPU 并行性能的需求之間的關系。
學習率對網絡的訓練效果有着巨大的影響。如果你剛入門,使用了常用深度學習架構中給出的各種預設參數,那幾乎可以肯定,你的設定不對。
關閉梯度裁剪,找出學習率的最大值,也就是在訓練過程中不會讓誤差爆表的上限值。把學習率設定為比這小一點的值,很可能就非常接近最佳學習率了。
大多數深度學習架構會預設啟用梯度裁剪方式。這種方式通過限制在每個步驟中可以調整權重的數量,來防止訓練過程中優化政策出現崩潰。
當你的資料中包含許多異常值,會造成大幅度的梯度和權重更新,這種限制特别有用。但是在預設情況下,這種方式也會使使用者很難手動找到最佳學習率。
我發現,大多數深度學習新手會設定過高的學習率,并且通過梯度裁剪來緩解此問題,使得全局訓練過程變慢,并且改變學習率後的網絡效果不可預測。
如果你好好清洗了資料,删除了大多數異常值,并設定了合理的學習率,實際上并不需要梯度裁剪方式。如果關閉了梯度裁剪之後裡,你發現網絡偶爾會發生訓練錯誤,那就再打開它。
但是要記住,發生訓練錯誤通常表明你的資料還存在一些問題,梯度裁剪隻是一個暫時的解決方法。
在最後一層中,不合理的激活函數有時會導緻你的網絡無法輸出所需值的全部範圍。最常見的錯誤是,在最後一層使用 ReLU 函數,導緻網絡隻能産生正值輸出。
如果要實作回歸任務,那麼在最後一層通常不需要使用任何激活函數,除非你詳細地知道你想輸出哪一類值。
再次确認下你輸入資料的實際意義,以及歸一化後的具體範圍。
很可能出現的情況是,網絡的輸出區間是從負無窮大到正無窮大,在這種情況下,你不該在最後一層使用激活函數。
如果網絡輸出隻在某個區間内有意義,則需使用一些特殊的激活函數。比如,某網絡輸出為 [0, 1] 區間的機率值,根據這種情況可使用 S 形激活函數。
在選擇最後一層的激活函數時,有許多玄學。
在神經網絡産生輸出後,你也許會将其裁剪到 [-1, 1] 的區間。那将這個裁剪過程當作最後一層的激活函數,這似乎是有意義的,因為這将確定網絡中的誤差函數不會對不在 [-1, 1] 區間外的值進行懲罰。但是沒有誤差意味着區間外的這些值沒有對應梯度,這在某些情況下無法進行網絡訓練。
或者,你也可以在最後一層使用 tanh 函數,因為這個激活函數的輸出範圍是 [-1, 1]。但是這也可能出現問題,因為這個函數在 1 或 - 1 附近時斜率變得很大,可能會使權重大幅增加,最終隻産生 - 1 或 1 的輸出。
一般來說,最好的選擇通常是采用求穩政策,在最後一層不使用任何激活函數,而不是試圖使用一些機靈的技巧,可能會适得其反。
使用 ReLU 激活函數的深度神經網絡通常可能遭受由不良梯度引起的所謂 “死神經元”。這可能會對網絡的性能産生負面影響,或者在某些情況下導緻完全無法訓練。
如果發現在 epoch 到 epoch 之間,你的訓練誤差不會變化,就可能是由于 ReLU 激活函數導緻了所有的神經元已經死亡。
換一個激活函數試試,比如 leaky ReLU 或 ELU,看看是不是還會發生同樣的情況。
ReLU 激活函數的梯度對于正值為 1,對于負值為 0。這是因為對于小于 0 的輸入來說,輸入的很小變化不會影響輸出。
這可能看起來不是一個問題,因為正值的梯度很大。但是很多層疊在一起,而負權重可以将具有強梯度的大正值變為 0 梯度的負值。
你可能經常發現,無論輸入什麼,部分甚至全部隐藏單元對成本函數都是 0 梯度,這就是所謂的網絡 “已死”,所有權重都無法更新。
很多運算都具有 0 梯度,比如裁剪,舍入,或取最大 / 最小值,如果用它們來計算成本函數相對于權重的導數,都會産生不良梯度。
如果它們出現在你的符号圖的任何地方,要非常小心,因為它們常常會導緻意想不到的困難。
如果你沒有正确地初始化神經網絡權重,那麼神經網絡很可能根本就無法訓練。
神經網絡中有許多其他元件,會假設你的權重初始化是正确的,或者标準的,它們會将權重設定為 0,或者使用你自定義的随機初始化權重,于是将不會起作用。
“he”、“lecun” 或 “xavier” 權重初始化都是受歡迎的選擇,在幾乎任何情況下都應該很好地工作。隻要選一個(我最喜歡的是 “lecun”)就行了。
但是一旦神經網絡開始訓練了,你就可以自由的實驗,尋找最适合你任務的權重了。
你可能聽說過,可以使用 “小随機數” 初始化神經網絡權重,但并不那麼簡單。
所有上述初始化方法都是靠複雜、細緻的數學發現的,這也說明了為什麼它們是最佳的。
更重要的是,很多其他神經網絡元件都是圍繞這些初始化建構的,并根據經驗使用它們進行測試 ,自己進行初始化可能會導緻難以複現其他研究者的成果。
其他層可能也需要仔細地初始化。網絡偏移被初始化為零,而其他更複雜的層(如參數激活函數)可能會帶有自己的初始化,這與正确的同樣重要。
網絡越深越好?不一定。
當你對網絡進行基準測試,試着在一些任務上提高 1% 的準确度時,更深的網絡通常會表現得更好。
但是如果你設計的淺層(3 到 5 層)網絡沒有學習任何特征,那麼可以保證,你設計的超深(如 100 層)網絡也會沒有效果,甚至更加糟糕。
剛開始時,先試試淺層神經網絡的效果,通常是 3 到 8 層。隻有當你的網絡有一定效果,要開始着手提高準确率時,再去研究更深層網絡的結構。
看起來似乎是當有人決定堆一個幾百層的神經網絡時,神經網絡模型忽然得到了突破性的結果,但事實并非如此。
在過去十年中,神經網絡中所有改良技術所取得的微小進步,對淺層和深層網絡都同樣适用。如果你的網絡不起作用,這很可能不是深度問題,是其他方面出錯了。
從小型網絡開始訓練,也意味着能更快地訓練網絡、更快地完成模型推理及更快地完成不同結構和參數配置的疊代過程。首先,與僅堆疊更多網絡層相比,上面提到的所有方面将對模型準确率産生更大的影響。
某些情況下,隐藏單元太多或者太少,都會導緻網絡難以訓練。
隐藏單元太少,可能會沒有能力表達所需的任務;太多單元又會導緻網絡緩慢、難以訓練,殘留噪聲難以消除。
開始時的隐藏單元數量,最好在 256 到 1024 個之間。
然後,看一下研究類似應用的研究人員使用了多少個隐藏單元,找找靈感。如果你的同行所用的數量和上面給出的數字相差很遠,可能會有一些特殊的原因,這可能對你來說很重要。
當決定隐藏單元的數量時,關鍵在于考慮要表達你想通過網絡傳遞的資訊,所需的最小真實值是多少。
然後,考慮到 dropout、網絡使用備援的表示、以及為你的估計留一點餘地,可以将這個數字放大一點。
如果你正在做分類,可以使用類别數目的 5 到 10 倍,作為隐藏單元的數量;如果做回歸,可以使用輸入或輸出變量數目的 2 到 3 倍。
當然,所有這些都高度依賴于環境,沒有簡單的自動解決方案,決定隐藏單元數量時,最重要的依然是直覺。
實際上,與其他因素相比,隐藏單元的數量通常對神經網絡性能影響很小,而在許多情況下,高估所需隐藏機關的數量除了拖慢訓練速度之外,也不會有什麼負面影響。
一旦網絡開始正常工作,如果你還是擔心,可以嘗試各種不同數量的隐藏單元,并測量網絡精度,直到找到最合适的設定。
本文作者:AI研習社