0
雷鋒網(wǎng)按:本文經(jīng)AI新媒體量子位(公眾號 ID: QbitAI)授權(quán)轉(zhuǎn)載,轉(zhuǎn)載請聯(lián)系出處。王瀚宸 @王小新 編譯自 TheOrangeDuck。
每個人在調(diào)試神經(jīng)網(wǎng)絡(luò)的時候,大概都遇到過這樣一個時刻:
什么鬼!我的神經(jīng)網(wǎng)絡(luò)就是不 work!到底該怎么辦!
機器學習博客 TheOrangeDuck 的作者,育碧蒙特利爾實驗室的機器學習研究員 Daniel Holden,也就是這個人:
根據(jù)自己工作中失敗的教訓,整理了一份神經(jīng)網(wǎng)絡(luò)出錯原因清單,一共 11 條。量子位搬運過來,各位被神經(jīng)網(wǎng)絡(luò)虐待的時候,可以按圖索驥。
當然,也祝你們看了這 11 條之后,功力大進,煉丹順利。
What?
在使用神經(jīng)網(wǎng)絡(luò)的過程中,非常重要的一點是要考慮好怎樣規(guī)范化(normalize)你的數(shù)據(jù)。
這一步不能馬虎,不正確、仔細完成規(guī)范化的話,你的網(wǎng)絡(luò)將會不能正常工作。
因為規(guī)范化數(shù)據(jù)這個重要的步驟在深度學習圈中早已被大家熟知,所以論文中很少提到,因此常會成為初學者的阻礙。
How?
大體上說,規(guī)范化是指從數(shù)據(jù)中減去平均值,然后再除以標準差的操作。
通常這個操作對每個輸入和輸出特征是分別完成的,但你可能會想同時對一整組的特征進行規(guī)范化,再挑出其中一些特殊處理。
Why?
我們需要規(guī)范化數(shù)據(jù)的主要原因是,在神經(jīng)網(wǎng)絡(luò)中幾乎所有的數(shù)據(jù)傳輸途徑中,都是假設(shè)輸入和輸出的數(shù)據(jù)結(jié)構(gòu)滿足標準差接近于 1,平均值幾乎為 0。這個假設(shè)在深度學習中的每個地方都會出現(xiàn),從權(quán)重因子的初始化,到活化函數(shù),再到訓練網(wǎng)絡(luò)的優(yōu)化算法。
And?
一個未訓練的神經(jīng)網(wǎng)絡(luò)通常輸出的結(jié)果范圍從 - 1 到 1。如果你希望它的輸出值在其它的范圍,比如說 RGB 圖片表示顏色的值域就是 0 到 255,你將會遇到麻煩。
當期望的輸出值是 255,神經(jīng)網(wǎng)絡(luò)開始訓練時情況會極不穩(wěn)定,因為實際產(chǎn)生的值為 - 1 或者 1,對大多數(shù)用來訓練神經(jīng)網(wǎng)絡(luò)的優(yōu)化算法來說,這和 255 相比都有巨大的誤差。這將會產(chǎn)生巨大的梯度,你的訓練誤差很可能會爆表。
就算碰巧在你訓練的起始階段,誤差沒有爆表,這個過程仍然是沒有意義的,因為神經(jīng)網(wǎng)絡(luò)在向錯誤的方向?qū)W習和發(fā)展。
如果你先將你的數(shù)據(jù)規(guī)范化(在這個例子中你可以將 RGB 值除以 128 然后減去 1),那么這些情況就都不會發(fā)生。
總體來說,神經(jīng)網(wǎng)絡(luò)中各種特征的值域決定了他們的重要性。
如果輸出中的一項特征的值域很大,那么意味著與其他特征相比,它將會產(chǎn)生更大的誤差。同樣地,輸入中值域大的特征也會支配著網(wǎng)絡(luò),在下游中引起更大的變化。
因此,僅僅依靠許多神經(jīng)網(wǎng)絡(luò)庫中的自動規(guī)范化,盲目地減去平均值后再除以方差,并不總是合適的做法??赡苡羞@樣一個輸入特征,取值范圍通常在 0 到 0.001 之間,它的值域這么小是因為這個特征不重要,還是因為它與其他特征相比有著更小的單位呢?這決定了你要不要將它規(guī)范化。
類似地,還要謹慎對待那些值域較小的特征,因為它們的標準差可能很小,接近或者嚴格等于 0。如果你對它們進行規(guī)范化,可能會產(chǎn)生 NaN(Not a Number) 的錯誤。
這種情況需要謹慎地對待,要仔細琢磨你的這些特征真正代表著什么,以及考慮規(guī)范化的過程是為了將所有輸入的特征等價。
這是少數(shù)幾個我認為在深度學習中需要人類完成的任務(wù)。
What?
當你訓練網(wǎng)絡(luò)經(jīng)過了幾個 epoch 之后,誤差(error)開始下降了——成功!
但這是否意味著你完成了訓練呢?博士能畢業(yè)了嗎?很不幸,答案是否定的。
你的代碼中,基本上還肯定還存在一些錯誤。這個 bug 可能存在于數(shù)據(jù)預(yù)處理,訓練網(wǎng)絡(luò)甚至是最后給出推斷結(jié)果的過程中。
只是誤差開始下降,并不意味著你的網(wǎng)絡(luò)學到了 “真功夫”。
How?
毋庸置疑,在數(shù)據(jù)傳輸過程中的每個階段檢查數(shù)據(jù)正確性都很重要,通常這意味著要通過一些方法來對結(jié)果進行可視化。
如果你的數(shù)據(jù)是圖像,那么情況就很簡單,相應(yīng)的動畫數(shù)據(jù)很好生成。但如果你的數(shù)據(jù)比較奇葩,也要找出一種合適的方法,能夠在預(yù)處理、網(wǎng)絡(luò)訓練和數(shù)據(jù)傳遞的每個階段來檢查數(shù)據(jù)的正確性,將其與原始的真實數(shù)據(jù)比較。
Why?
跟傳統(tǒng)的編程過程不同,機器學習系統(tǒng)失敗時都不出聲。
在傳統(tǒng)編程中,我們習慣了當遭遇狀況時計算機報錯,隨后我們可以結(jié)合報錯內(nèi)容來 debug。不幸的是,這個過程并不適用于機器學習應(yīng)用。
所以,我們需要極其小心地在每個階段檢查我們的過程是否有問題,從而能夠察覺到 bug 的產(chǎn)生,以及在需要回頭仔細檢查代碼的時候及時發(fā)現(xiàn)。
And?
有許多種方法來檢查你的網(wǎng)絡(luò)是否有效。其中之一是要明確訓練誤差的意義。將在訓練集上運行的神經(jīng)網(wǎng)絡(luò)的輸出結(jié)果進行可視化——輸出結(jié)果跟實際情況相比怎樣?
你可能看到在訓練過程中誤差從 100 下降到 1,但最終結(jié)果仍然是不可用的,因為在實際場景中誤差為 1 仍然是不可接受的結(jié)果。如果網(wǎng)絡(luò)在訓練集上有效,那么再在驗證集上測試——它是否同樣適用于之前沒有見過的數(shù)據(jù)呢?
我的建議是從一開始就可視化所有過程,不要等網(wǎng)絡(luò)不奏效時再開始做,在你開始嘗試不同的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)之前,你要確保整個流程沒有一絲差錯。這是你能夠正確評估不同網(wǎng)絡(luò)模型的唯一方式。
What?
絕大部分數(shù)據(jù)都很 tricky。我們認為非常相似的事物,從數(shù)據(jù)上看可能擁有完全不同的數(shù)值表達形式。
就拿視頻中的人物動作來說,如果我們數(shù)據(jù)是在一個特定地點或是特點方向上,記錄人物的關(guān)節(jié)相對于錄像中心的 3D 位置,那么換一個方向或地點,可能同一套動作會擁有完全不同的數(shù)字表達形式。
因此,我們需要用新的方式來表達我們的數(shù)據(jù),比如說放到一些本地參考系中(諸如跟人物的質(zhì)心相關(guān)的一些),讓相似的動作有相似的數(shù)值表達。
How?
思考你的特征具體代表著什么——你是否可以在它們上面做一些簡單的變換,來確保用來代表相似事物的數(shù)據(jù)點通常具有相似的數(shù)值表達?是否存在一個本地坐標系,能以一種不同的形式更自然地表達你的數(shù)據(jù)?比如說一個更好的色彩空間?
Why?
神經(jīng)網(wǎng)絡(luò)只對輸入的數(shù)據(jù)做一些最基本的假設(shè),但是這些假設(shè)中有一條,是認為這些數(shù)據(jù)分布的空間是連續(xù)的,即對于空間中的大部分,兩個數(shù)據(jù)點間的點類似這兩個數(shù)據(jù)點的 “混合”,相鄰的數(shù)據(jù)點在某種意義上代表著相似的事情。
當數(shù)據(jù)空間中存在較大的不連續(xù)時,亦或者一大組分開的數(shù)據(jù)均代表著同一件事情時,將會使得學習任務(wù)的難度大大增加。
And?
理解數(shù)據(jù)預(yù)處理(preprocess)的另一種方式,是把它作為減少由排列組合導(dǎo)致的數(shù)據(jù)激增的一種嘗試。
舉例來說,如果一個基于人物動作訓練過的神經(jīng)網(wǎng)絡(luò)需要學習在該人物在各個地點、各個方向上的同一組動作,那么將會耗費大量的資源,學習的過程將會是冗余的。
What?
正則化(regularization)方式是訓練神經(jīng)網(wǎng)絡(luò)時另一個不可或缺的方面,通常以 Dropout 層、小噪聲或某種形式的隨機過程等方式應(yīng)用到網(wǎng)絡(luò)中。
即使在你看來當前數(shù)據(jù)規(guī)模遠大于參數(shù)規(guī)模,或是在某些情況下,不會出現(xiàn)過擬合效應(yīng),或者就算出現(xiàn)也不影響效果,你仍然應(yīng)該加入 Dropout 層或一些其他形式的小噪聲。
How?
向神經(jīng)網(wǎng)絡(luò)添加正則化的一種最基本方法,是在網(wǎng)絡(luò)中的每個線性層(如卷積層或稠密層)前加入 Dropout 層。
在開始設(shè)置 Dropout 值時,可定義中等值到較低值,如 0.25 或 0.1。你可根據(jù)網(wǎng)絡(luò)的各項指標,來判斷過擬合程度并進行調(diào)整,若仍覺得不可能出現(xiàn)過擬合效應(yīng),可以將 Dropout 值設(shè)置到非常小,如 0.01。
Why?
正則化方式不僅僅是用來控制過擬合效應(yīng),它在訓練過程中引入了一些隨機過程,在某種意義上 “平滑” 了代價格局。這種方式可加快訓練進程,有助于處理數(shù)據(jù)中的異常值,并防止網(wǎng)絡(luò)中出現(xiàn)極端權(quán)重結(jié)構(gòu)。
And?
跟 Dropout 層一樣,數(shù)據(jù)增強或者其他類型的噪聲也可作為正則化方式。
雖然 Dropout 層通常被認為是一種將許多隨機子網(wǎng)絡(luò)的預(yù)測結(jié)果結(jié)合起來的技巧,但它也可看作是一種通過在訓練時產(chǎn)生多種輸入數(shù)據(jù)的相似變體來動態(tài)擴展訓練集大小的方法。
而且要知道,防止過擬合并提高網(wǎng)絡(luò)準確性的最佳方法是向神經(jīng)網(wǎng)絡(luò)輸入大量且不重復(fù)的訓練數(shù)據(jù)。
What?
設(shè)置了過大的批次(batch)大小,可能會對訓練時網(wǎng)絡(luò)的準確性產(chǎn)生負面影響,因為它降低了梯度下降的隨機性。
How?
要在可接受的訓練時間內(nèi),確定最小的批次大小。一個能合理利用 GPU 并行性能的批次大小可能不會達到最佳的準確率,因為在有些時候,較大的批次大小可能需要訓練更多迭代周期才能達到相同的正確率。
在開始時,要大膽地嘗試很小的批次大小,如 16、8,甚至是 1。
Why?
較小的批次大小能帶來有更多起伏、更隨機的權(quán)重更新。這有兩個積極的作用,一是能幫助訓練 “跳出” 之前可能卡住它的局部最小值,二是能讓訓練在 “平坦” 的最小值結(jié)束,著通常會帶來更好的泛化性能。
And?
數(shù)據(jù)中其他的一些要素有時也能起到批次大小的作用。
例如,以兩倍大小的先前分辨率來處理圖像,得到的效果與用四倍批次大小相似。
做個直觀的解釋,考慮在 CNN 網(wǎng)絡(luò)中,每個濾波器的權(quán)重更新值將根據(jù)輸入圖像的所有像素點和批次中的每張圖像來進行平均,將圖像分辨率提高兩倍,會產(chǎn)生一種四倍像素量同樣的平均效果,與將批次大小提高四倍的做法相似。
總體來說,最重要的是要考慮到,在每次迭代中有多少決定性的梯度更新值被平均,并確保平衡好這種不利影響與充分利用 GPU 并行性能的需求之間的關(guān)系。
What?
學習率對網(wǎng)絡(luò)的訓練效果有著巨大的影響。如果你剛?cè)腴T,使用了常用深度學習框架中給出的各種默認參數(shù),那幾乎可以肯定,你的設(shè)置不對。
How?
關(guān)閉梯度裁剪,找出學習率的最大值,也就是在訓練過程中不會讓誤差爆表的上限值。把學習率設(shè)置為比這小一點的值,很可能就非常接近最佳學習率了。
Why?
大多數(shù)深度學習框架會默認啟用梯度裁剪方式。這種方式通過限制在每個步驟中可以調(diào)整權(quán)重的數(shù)量,來防止訓練過程中優(yōu)化策略出現(xiàn)崩潰。
當你的數(shù)據(jù)中包含許多異常值,會造成大幅度的梯度和權(quán)重更新,這種限制特別有用。但是在默認情況下,這種方式也會使用戶很難手動找到最佳學習率。
我發(fā)現(xiàn),大多數(shù)深度學習新手會設(shè)置過高的學習率,并且通過梯度裁剪來緩解此問題,使得全局訓練過程變慢,并且改變學習率后的網(wǎng)絡(luò)效果不可預(yù)測。
And?
如果你好好清洗了數(shù)據(jù),刪除了大多數(shù)異常值,并設(shè)置了合理的學習率,實際上并不需要梯度裁剪方式。如果關(guān)閉了梯度裁剪之后里,你發(fā)現(xiàn)網(wǎng)絡(luò)偶爾會發(fā)生訓練錯誤,那就再打開它。
但是要記住,發(fā)生訓練錯誤通常表明你的數(shù)據(jù)還存在一些問題,梯度裁剪只是一個暫時的解決方法。
What?
在最后一層中,不合理的激活函數(shù)有時會導(dǎo)致你的網(wǎng)絡(luò)無法輸出所需值的全部范圍。最常見的錯誤是,在最后一層使用 ReLU 函數(shù),導(dǎo)致網(wǎng)絡(luò)只能產(chǎn)生正值輸出。
How?
如果要實現(xiàn)回歸任務(wù),那么在最后一層通常不需要使用任何激活函數(shù),除非你詳細地知道你想輸出哪一類值。
Why?
再次確認下你輸入數(shù)據(jù)的實際意義,以及歸一化后的具體范圍。
很可能出現(xiàn)的情況是,網(wǎng)絡(luò)的輸出區(qū)間是從負無窮大到正無窮大,在這種情況下,你不該在最后一層使用激活函數(shù)。
如果網(wǎng)絡(luò)輸出只在某個區(qū)間內(nèi)有意義,則需使用一些特殊的激活函數(shù)。比如,某網(wǎng)絡(luò)輸出為 [0, 1] 區(qū)間的概率值,根據(jù)這種情況可使用 S 形激活函數(shù)。
And?
在選擇最后一層的激活函數(shù)時,有許多玄學。
在神經(jīng)網(wǎng)絡(luò)產(chǎn)生輸出后,你也許會將其裁剪到 [-1, 1] 的區(qū)間。那將這個裁剪過程當作最后一層的激活函數(shù),這似乎是有意義的,因為這將確保網(wǎng)絡(luò)中的誤差函數(shù)不會對不在 [-1, 1] 區(qū)間外的值進行懲罰。但是沒有誤差意味著區(qū)間外的這些值沒有對應(yīng)梯度,這在某些情況下無法進行網(wǎng)絡(luò)訓練。
或者,你也可以在最后一層使用 tanh 函數(shù),因為這個激活函數(shù)的輸出范圍是 [-1, 1]。但是這也可能出現(xiàn)問題,因為這個函數(shù)在 1 或 - 1 附近時斜率變得很大,可能會使權(quán)重大幅增加,最終只產(chǎn)生 - 1 或 1 的輸出。
一般來說,最好的選擇通常是采用求穩(wěn)策略,在最后一層不使用任何激活函數(shù),而不是試圖使用一些機靈的技巧,可能會適得其反。
What?
使用 ReLU 激活函數(shù)的深度神經(jīng)網(wǎng)絡(luò)通??赡茉馐苡刹涣继荻纫鸬乃^ “死神經(jīng)元”。這可能會對網(wǎng)絡(luò)的性能產(chǎn)生負面影響,或者在某些情況下導(dǎo)致完全無法訓練。
How?
如果發(fā)現(xiàn)在 epoch 到 epoch 之間,你的訓練誤差不會變化,就可能是由于 ReLU 激活函數(shù)導(dǎo)致了所有的神經(jīng)元已經(jīng)死亡。
換一個激活函數(shù)試試,比如 leaky ReLU 或 ELU,看看是不是還會發(fā)生同樣的情況。
Why?
ReLU 激活函數(shù)的梯度對于正值為 1,對于負值為 0。這是因為對于小于 0 的輸入來說,輸入的很小變化不會影響輸出。
這可能看起來不是一個問題,因為正值的梯度很大。但是很多層疊在一起,而負權(quán)重可以將具有強梯度的大正值變?yōu)?0 梯度的負值。
你可能經(jīng)常發(fā)現(xiàn),無論輸入什么,部分甚至全部隱藏單元對成本函數(shù)都是 0 梯度,這就是所謂的網(wǎng)絡(luò) “已死”,所有權(quán)重都無法更新。
And?
很多運算都具有 0 梯度,比如裁剪,舍入,或取最大 / 最小值,如果用它們來計算成本函數(shù)相對于權(quán)重的導(dǎo)數(shù),都會產(chǎn)生不良梯度。
如果它們出現(xiàn)在你的符號圖的任何地方,要非常小心,因為它們常常會導(dǎo)致意想不到的困難。
What?
如果你沒有正確地初始化神經(jīng)網(wǎng)絡(luò)權(quán)重,那么神經(jīng)網(wǎng)絡(luò)很可能根本就無法訓練。
神經(jīng)網(wǎng)絡(luò)中有許多其他組件,會假設(shè)你的權(quán)重初始化是正確的,或者標準的,它們會將權(quán)重設(shè)置為 0,或者使用你自定義的隨機初始化權(quán)重,于是將不會起作用。
How?
“he”、“l(fā)ecun” 或 “xavier” 權(quán)重初始化都是受歡迎的選擇,在幾乎任何情況下都應(yīng)該很好地工作。只要選一個(我最喜歡的是 “l(fā)ecun”)就行了。
但是一旦神經(jīng)網(wǎng)絡(luò)開始訓練了,你就可以自由的實驗,尋找最適合你任務(wù)的權(quán)重了。
Why?
你可能聽說過,可以使用 “小隨機數(shù)” 初始化神經(jīng)網(wǎng)絡(luò)權(quán)重,但并不那么簡單。
所有上述初始化方法都是靠復(fù)雜、細致的數(shù)學發(fā)現(xiàn)的,這也說明了為什么它們是最佳的。
更重要的是,很多其他神經(jīng)網(wǎng)絡(luò)組件都是圍繞這些初始化構(gòu)建的,并根據(jù)經(jīng)驗使用它們進行測試 ,自己進行初始化可能會導(dǎo)致難以復(fù)現(xiàn)其他研究者的成果。
And?
其他層可能也需要仔細地初始化。網(wǎng)絡(luò)偏移被初始化為零,而其他更復(fù)雜的層(如參數(shù)激活函數(shù))可能會帶有自己的初始化,這與正確的同樣重要。
What?
網(wǎng)絡(luò)越深越好?不一定。
當你對網(wǎng)絡(luò)進行基準測試,試著在一些任務(wù)上提高 1% 的準確度時,更深的網(wǎng)絡(luò)通常會表現(xiàn)得更好。
但是如果你設(shè)計的淺層(3 到 5 層)網(wǎng)絡(luò)沒有學習任何特征,那么可以保證,你設(shè)計的超深(如 100 層)網(wǎng)絡(luò)也會沒有效果,甚至更加糟糕。
How?
剛開始時,先試試淺層神經(jīng)網(wǎng)絡(luò)的效果,通常是 3 到 8 層。只有當你的網(wǎng)絡(luò)有一定效果,要開始著手提高準確率時,再去研究更深層網(wǎng)絡(luò)的結(jié)構(gòu)。
Why?
看起來似乎是當有人決定堆一個幾百層的神經(jīng)網(wǎng)絡(luò)時,神經(jīng)網(wǎng)絡(luò)模型忽然得到了突破性的結(jié)果,但事實并非如此。
在過去十年中,神經(jīng)網(wǎng)絡(luò)中所有改良技術(shù)所取得的微小進步,對淺層和深層網(wǎng)絡(luò)都同樣適用。如果你的網(wǎng)絡(luò)不起作用,這很可能不是深度問題,是其他方面出錯了。
And?
從小型網(wǎng)絡(luò)開始訓練,也意味著能更快地訓練網(wǎng)絡(luò)、更快地完成模型推理及更快地完成不同結(jié)構(gòu)和參數(shù)配置的迭代過程。首先,與僅堆疊更多網(wǎng)絡(luò)層相比,上面提到的所有方面將對模型準確率產(chǎn)生更大的影響。
What?
某些情況下,隱藏單元太多或者太少,都會導(dǎo)致網(wǎng)絡(luò)難以訓練。
隱藏單元太少,可能會沒有能力表達所需的任務(wù);太多單元又會導(dǎo)致網(wǎng)絡(luò)緩慢、難以訓練,殘留噪聲難以消除。
How?
開始時的隱藏單元數(shù)量,最好在 256 到 1024 個之間。
然后,看一下研究類似應(yīng)用的研究人員使用了多少個隱藏單元,找找靈感。如果你的同行所用的數(shù)量和上面給出的數(shù)字相差很遠,可能會有一些特殊的原因,這可能對你來說很重要。
Why?
當決定隱藏單元的數(shù)量時,關(guān)鍵在于考慮要表達你想通過網(wǎng)絡(luò)傳遞的信息,所需的最小真實值是多少。
然后,考慮到 dropout、網(wǎng)絡(luò)使用冗余的表示、以及為你的估計留一點余地,可以將這個數(shù)字放大一點。
如果你正在做分類,可以使用類別數(shù)目的 5 到 10 倍,作為隱藏單元的數(shù)量;如果做回歸,可以使用輸入或輸出變量數(shù)目的 2 到 3 倍。
當然,所有這些都高度依賴于環(huán)境,沒有簡單的自動解決方案,決定隱藏單元數(shù)量時,最重要的依然是直覺。
And?
實際上,與其他因素相比,隱藏單元的數(shù)量通常對神經(jīng)網(wǎng)絡(luò)性能影響很小,而在許多情況下,高估所需隱藏單位的數(shù)量除了拖慢訓練速度之外,也不會有什么負面影響。
一旦網(wǎng)絡(luò)開始正常工作,如果你還是擔心,可以嘗試各種不同數(shù)量的隱藏單元,并測量網(wǎng)絡(luò)精度,直到找到最合適的設(shè)置。
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。