特斯拉AI主管給你的33條深度學習訓練建議

AIBigbull2050發表於2019-12-10

精翻版Andrej Karpathy部落格

特斯拉AI主管給你的33條深度學習訓練建議

30行程式碼就能訓練神經網路?

幾周前,我在發了一條”最常見的神經網路錯誤”的微博(•̀ᴗ•́)و ̑̑ ,列舉了一些與訓練神經網路相關的常見錯誤,這條微博引發了大家熱烈的討論。相信很多人都曾親身經歷過”卷積層的工作原理”和”訓練的實際結果”之間的巨大差距。

所以我想,就這個話題寫一篇長文。不過,我不打算詳細列舉常見的錯誤,而是更深入地探討一下如何能夠完全避免這些錯誤(或快速修復它們)。這樣做的訣竅是遵循一個特定的 過程,據我所知,很少有文字記錄如何修復訓練錯誤的這個過程。

下面從兩個日常碰到的問題開始。

1) 神經網路訓練是一種”抽象洩漏”(leaky abstraction)

現在訓練神經網路很容易,許多庫和框架都以可以用30行程式碼解決你的資料問題而自豪,給人一種即插即用的( 錯誤)印象。常見的做法是:

>>> your_data = # plug your awesome dataset here

這些庫和示例類似我們之前熟悉的標準軟體的介面流程———抽象、簡潔的API,如http請求庫API所示:

>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))

這很酷!開發者勇敢地承擔了理解查詢字串、url、GET/POST請求、HTTP連線等重擔,並在很大程度上隱藏了幾行程式碼背後的複雜性。

不幸的是, 神經網路不是這樣的。如果你稍微偏離了訓練ImageNet分類器,那麼它們就不是”現成的”技術。

在我以前介紹反向傳播的博文中,我試圖通過使用反向傳播並將其稱為”抽象洩漏”來說明這一點,但不幸的是,實際情況要糟糕得多。Backprop + SGD並不會神奇地讓你的網路工作,Batch norm並不能神奇地使其更快地收斂,RNN不會神奇地讓你”插入”文字;你可以用RL表示你的問題,但僅僅是這樣並不意味著你應該這樣做。如果你堅持使用這種技術而不瞭解它的工作原理,那麼你很可能會失敗。

2) 神經網路訓練常常無聲無息地失敗

當你破壞或錯誤配置程式碼時,通常會遇到某種異常。比如你插入了一個包含預期字串的整數。這個函式只需要3個引數。但匯入失敗了。這個鍵根本不存在。這兩個列表中的元素數量不相等。此外,通常可以為某個特定功能建立單元測試。

這只是訓練神經網路的開端。從語法上來說,一切都是正確的,但整件事的安排並不妥善,這真的很難判斷。”可能的錯誤範圍”很大,符合邏輯(與語法相反),並且很難進行單元測試。例如,在資料增強過程中,當你從左到右翻轉影像時,可能忘記了翻轉標籤。你的網路仍然可以(令人震驚地)很好地工作,因為你的網路可以在內部學習檢測翻轉的影像,然後左右翻轉它的預測。或者你的自迴歸模型會因為一個off-by-one的bug而不小心將它試圖預測的東西作為輸入。或者你試著修剪梯度,但結果卻減少了損失,導致在訓練期間忽略了異常值的例子。或者你從一個預訓練的檢查點初始化你的權重,但沒有使用原始平均值。或者你只是搞砸了正則化強度、學習率、衰減率、模型大小等的設定。因此,只有在運氣好的情況下,錯誤配置的神經網路才會丟擲異常;大多數情況下,它會繼續訓練,但默默地使執行變糟。

因此, 用”快速而激烈”的方法來訓練神經網路是行不通的,只會導致痛苦。痛苦雖然是讓神經網路正常工作的一個非常自然的部分,但它是可以通過徹底的、防禦性的、偏執的,以及對幾乎所有可能的事情進行視覺化來減輕的。根據我的經驗,與深度學習成功最相關的品質是耐心和關注細節。

如何訓練一個神經網路

基於以上兩個事實,我為自己開發了一個特定的過程,當我將神經網路應用到一個新的問題時,都遵循這個過程。本文中將嘗試描述這個過程。

你會看到,它是非常重視上述兩個原則的。特別是,它是從簡單到複雜構建的,每一步我們都對將要發生的事情做出具體的假設,然後通過實驗驗證它們,或者進行調查,直到發現問題。需要極力避免的是同時引入大量”未經驗證”的複雜性,這必然會引入錯誤/錯誤配置,而這些錯誤/錯誤配置將永遠無法找到。如果像訓練一樣編寫神經網路程式碼,你會想要使用非常小的學習率並猜測,然後在每次迭代之後評估完整的測試集。

1. 梳理資料

訓練神經網路的第一步是完全不用接觸任何神經網路程式碼,而是 從徹底檢查資料開始。這一步至關重要,花大量時間(以小時為單位)瀏覽數千個示例,瞭解它們的分佈並尋找模式。幸運的是,人類的大腦非常擅長做這個。有一次,我發現資料中包含重複的例子,還有一次發現了損壞的影像/標籤。我會尋找資料中的不平衡和偏差,也會關注自己是如何對資料進行分類的,這個過程會提醒了最終要探索怎樣的網路網路結構型別。

舉個例子,只有區域性的特性是否足夠,還是需要全域性上下文?有多少變數,它們以什麼形式出現?哪些變數是假的,可以預先處理的?空間位置是否重要,是否想要將其平均池化?細節有多重要,能在多大程度上對影像進行降取樣?標籤有多少噪音?

此外,由於神經網路實際上是資料集的壓縮版本,因此你能夠檢視你的網路預測並瞭解它們可能出錯來自何處。如果你的網路給你的預測與你在資料中看到的不一致,那就是有什麼地方錯了。

一旦獲得了一種定性的感覺,那麼寫一些簡單的程式碼來搜尋/過濾/排序也是一個好主意,不管你能想到什麼(例如標籤的型別,註釋的大小,註釋的數量,等等),並將它們的分佈和沿著任何軸的異常值視覺化,通常異常值幾乎總是能揭示資料質量或預處理中的一些bug。

2. 建立端到端的訓練/評估框架+獲取簡單的基線

現在我們已經對資料有了一定了解,那麼就可以實現炫酷的多尺度ASPP FPN ResNet,並開始訓練超棒的模型了嗎?肯定不是,首先應該建立一個完整的訓練+評估的框架,通過一系列的實驗評估模型的正確性。在這個階段,最好選擇一些你不可能搞砸的簡單模型———例如線性分類器,或者一個非常小的卷積網路。通過訓練這個網路,視覺化損失值及其他指標(例如準確性),並在此過程中執行一系列帶有明確假設的消融實驗。

這個階段的提示和技巧:

- 固定隨機種子(random seed)。使用固定的隨機seed來保證當你執行同一程式碼兩次時,能得到相同的結果。消除了變數因素,將有助於對模型的判斷。

- 簡化。在這個階段一定要關閉所有資料增強,資料增強是一種正則化策略,我們稍後可能會將其合併進來,但在初始階段它只是會引入一些愚蠢的bug。

- 評估整個測試集。當繪製測試損失時,在整個測試集上進行評估,不要只在batches上繪製測試損失,然後依賴於在Tensorboard中平滑它們。為了要追求正確,需要犧牲一定的時間。

- 在初始化時驗證損失。驗證你的損失是否以正確的損失值開始。例如,如果你正確地初始化了最後一層,你應該在初始化時測量softmax的-log(1/n_classes)。同樣的預設值可以用於L2迴歸、Huber損失等。

- 正確地初始化。正確初地始化最後一層權重。例如,如果你要回歸一些平均值為50的值,那麼要將最終偏差初始化為50。如果你有一個不平衡的資料集,其正負比為1:10,那麼在你的日誌上設定偏差,使你的網路預測概率在初始化時為0.1。正確設定這些引數將加快收斂速度,並消除”hockey stick”損失曲線,在最初幾次迭代中,你的網路基本上只是學習偏差。

- human baseline。監控人類可解釋和可檢查的損失以外的指標(例如準確性)。儘可能地評估你自己(人類)的準確性,並與之進行比較。或者,對測試資料進行兩次註釋,對於每個示例,將一個註釋作為預測,將第二個註釋作為ground truth。

- input-indepent baseline。訓練一個獨立於輸入的基線(例如,最簡單的方法就是將所有輸入設定為零)。這應該比實際插入資料而不將其歸零的情況更糟。也就是說,可以知道你的模型是否學會從輸入中提取任何資訊?

- 在單批資料上過擬合。過擬合包含少量例子(例如只有兩個)的一個batch。為此,我們需要增加模型的容量(例如新增層或過濾器),並驗證我們可以達到的最低損失值(例如0)。我也喜歡在同一個圖中視覺化標籤和預測,並確保一旦達到最小損失,它們最終會完美地對齊。如果沒有完美對齊,那麼在某個地方就有一個bug,無法繼續到下一個階段。

- 驗證訓練損失的下降。在這個階段,你可能希望在資料集上實現欠擬合,因為該階段的模型是一個玩具模型。試著增加一點它的容量,在看看訓練損失是否下降了。

- 在輸入網路前對資料進行視覺化。在執行y_hat = model(x)(或tf中執行sess.run)之前,是視覺化資料的正確時間。我們需要準確地視覺化輸入給你的網路的東西,把原始的資料張量和標籤解碼並視覺化。這個過程為我節省了大量時間,並揭示了資料預處理和增強方面的問題。

- 視覺化預測動態。在訓練過程中,我喜歡視覺化固定測試batch模型的預測結構。這些預測動態移動將為你提供關於訓練進展的良好直覺。很多時候,如果網路以某種方式太過波動,顯示出不穩定性,你可能會覺得網路”難以”擬合你的資料。非常低或非常高的學習率在波動的數量中也很容易被注意到。

- 使用反向傳播來繪製依賴關係圖。深度學習程式碼通常包含複雜、向量化和broadcast操作。我遇到過的一個比較常見的bug是,使用view而不是進行transpose/permute,無意中混淆了批處理的維度資訊。然而,你的網路通常仍然訓練良好,因為它將學會忽略來自其他樣本的資料。一種debug的方法是將某個樣本i的損失設定為1.0,然後執行反向傳播一直到輸入,並確保只在i-thexample上得到一個非零梯度。也就是說,梯度提供了網路中的依賴關係資訊,這對於debug非常有用。

- 程式碼通用化。這是一種更為通用的編碼技巧,但是我經常看到人們在使用這個技巧時產生bug,尤其是從頭編寫一個相對通用的函式時。我喜歡為我正在做的事情寫非常具體的函式,讓這個函式能work,然後再一般化,確保得到相同的結果。這通常適用於向量化程式碼,我會先寫出完整的迴圈版本,然後再將它轉換為向量化程式碼。

3.過擬合

在這個階段,我們應該對資料集有一個很好的理解,已經有完整的訓練和測試pipeline。對於任何給定的模型,可以重複地計算出一個可信的量度。這一階段的任務是對一個效能不錯的模型進行迭代。

找到一個好模型可以分為兩個極端:首先得到一個足夠大的模型,它可以是過擬合的,然後在適當調整(放棄一些訓練損失,以改善驗證損失)。

關於這個階段的一些提示和技巧:

- 挑選模型。 為了獲得良好的訓練損失,需要為資料選擇合適的網路結構。我的建議是:不要逞英雄。我見過很多人瘋狂的人,將神經網路工具箱的各種工具像堆樂高積木一樣堆疊在各種奇異的網路結構中,應該在專案早期階段盡力避免這樣做。

我總是建議先找到最相關的一些論文,先把其中最簡單的網路結構原樣照搬過來,以獲得良好的效能。比如你做得是影像分類,不要想著當英雄,先把ResNet-50拿過來用。後面可以做一些更自定義設定和改進,並實現比它更好的效能。

- 選Adam總沒錯。 在設定baseline方法的早期階段,我喜歡使用學習率為3e-4的Adam網路結構。根據我的經驗,Adam對超引數更加寬容,包括不良的學習率。對於ConvNets來說,一個經過適當調整的SGD幾乎總會略優於Adam,但前者最佳學習率區域要窄得多,而且受到具體問題所限。(如果使用的是RNN和相關的序列模型,那麼在專案早期階段更常用Adam。再說一遍,不要逞英雄,多參考相關論文。)

- 一次只針對一個物件進行復雜化。 如果有多個訊號進入分類器,建議逐個引入,確保每次訊號的引入都獲得預期的效能提升。一開始不要一股腦地把全部訊號都餵給模型。增加複雜度還有別的方法,比如可以嘗試先插入較小的影像,然後再放大,諸如此類。

- 不要過於相信預設的學習率衰減。 如果是重用來自其他領域的程式碼,那麼在處理學習速度衰減時一定要非常小心。您不僅希望針對不同的問題使用不同的衰減計劃,而且 - 更糟糕的是 - 在典型的實施中,計劃將基於當前的epoch數,該值會根據資料集的大小而廣泛變化。

比如,ImageNet將在30個epoch上衰減10倍。如果你沒有訓練ImageNet那麼這肯定不是想要的結果。如果你不小心,程式碼可能會過早地將學習率趨零,導致模型無法收斂。在我自己的研究中總是完全禁用學習率衰減(學習速率為常數)並在最後進行調整。

4.正則化

理想情況下,我們要處理的是大型模型,至少能夠擬合訓練集。現在是時候通過放棄一些訓練準確性,進行一些正則化處理,以獲得一些驗證準確性。以下是這方面一些提示和技巧:

- 獲取更多的資料。首先,在任何實際環境中,最佳和首選方法是新增更多真實的訓練資料。當可以收集更多資料時,就不要再花費大量時間嘗試從小型資料集中擠效能了。據我所知,新增更多資料幾乎是唯一無限改善配置良好的神經網路效能的方式。

- 資料增強。 僅次於真實資料的半真實的資料,需要嘗試更積極的資料增強。

- 創造性資料增強。 如果資料增強不能做到這一點,假資料也許可以。人們正在尋找擴充套件資料集的創新方法;例如,域隨機化,模擬的使用,巧妙的混合,比如將(可能模擬的)資料引入場景,甚至是GAN中。

- 預訓練。 即使有足夠的資料,如果可以,使用預訓練網路也幾乎沒壞處。

- 堅持監督式學習。 不要對無監督的預訓練抱有過分的信心。據我所知,沒有任何一種無監督學習在現代計算機視覺任務上有很強的表現(雖然現在NLP領域誕生了BERT等優秀模型,但這很可能是因為文字資料更成熟的形式,以及更高的訊雜比)。

- 更小的輸入維度。 去除可能包含虛假訊號的特徵。如果資料集很小,任何新增的虛假輸入都可能造成過擬合。同樣,如果低階細節無關緊要,請嘗試輸入更小的影像。

- 較小的模型。 很多時候可以在網路上使用先驗知識來減小模型大小。例如,過去在ImageNet骨幹網頂部使用全連線層很流行,但現在已改用簡單的平均池化,從而去掉了過程中的大量引數。

- 減小批大小。 由於歸一化是基於批大小的,更小的batch size會具有更明顯的正則化效果。這是因為批量經驗均值/標準是完整均值/標準的更近似版本,因此標度和偏移量”擺動”您的批次更多。

- Dropout。新增dropout。對ConvNets使用dropout2d。建議謹慎使用dropout,因為似乎不太不適合批歸一化。

- 權重衰減。 增加權重衰減懲罰。

- “早停”:提早停止訓練。 根據驗證損失停止訓練,在出現過擬合之前獲得模型。

- 嘗試更大的模型。 我最後提到這一點,並且只是在提前停止之後,但我在過去曾經發現過幾次大模型最終過擬合,但是它們的”早停”效能往往比小模型的效能要好得多。

最後,為了進一步確保網路是一個合理的分類器,我喜歡對網路第一層權重進行視覺化,並確保獲得有意義的邊緣。如果第一層濾波器看起來像噪音,那麼可能需要去掉一些東西。類似地,網路中的啟用函式有時也會有異常出現,暴露出一些問題。

5.精細調整

現在應該使用資料集探索寬泛的模型空間,以獲得低驗證損失的體系結構。下面是一些提示和技巧:

- 隨機網格搜尋。為了同時調整多個超引數,使用網格搜尋確保覆蓋所有設定,這聽起來很誘人,但請記住,最好使用隨機搜尋。直觀地說,這是因為神經網路通常對某些引數比其他引數更敏感。在極限情況下,如果一個引數很重要,但是改變另一個引數沒有效果,那還不如對第一個引數進行徹底取樣。

- 超引數優化。目前有很多花哨的貝葉斯超引數優化工具箱,也有一些成功應用的例項,但我個人的經驗是,探索優質大寬度模型和超引數空間的最好方式是找個實習生。哈哈,開句玩笑。

6.效能壓榨

在確定了最佳型別的體系結構和超引數後,仍然可以利用一些技巧最後”壓榨”一下系統的效能:

- 整合。模型是一種非常有保證的方法,可以在任何事情上獲得2%的準確率。如果您在測試時無法負擔計算,請考慮使用黑暗知識將您的整體提升到網路中。

- 保持訓練。我經常看到人們在驗證損失趨於平穩時就想停止模型訓練。根據我的經驗,網路會長時間不間斷地進行訓練。有一次我在冬假期間不小心沒有停止訓練,當我第二1月份回來時,發現模型效能達到了SOTA水平。

最後

走到這一步,相信你已經獲得了神經網路訓練成功的要素:對技術,資料集和問題定位有了更加深刻的理解,建立了模型的訓練/測試基礎框架,並對其準確性有了很高的信心,且進一步探索了更加複雜的模型,以預測每一步的除錯獲得效能改進。現在是時候開始閱讀大量論文,嘗試大量實驗,準備好獲得SOTA結果吧,祝你好運!

https://blog.csdn.net/weixin_42137700/article/details/100150120

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946223/viewspace-2655498/,如需轉載,請註明出處,否則將追究法律責任。

相關文章