A Recipe for Training Neural Networks [中文翻譯, part 1]

最近拜讀大神Karpathy的經驗之談 A Recipe for Training Neural Networks  https://karpathy.github.io/2019/04/25/recipe/,這個祕籍對不少深度學習算法訓練過程當中遇到的各自問題進行了總結,並提出了不少很好的建議,翻譯於此,但願可以幫助更多人學到這些內容。python

譯文以下:git

幾周前,我發佈了一條關於「最多見的神經網絡錯誤」的推文,列出了一些與訓練神經網絡相關的常見問題。這條推文獲得了比我預期的要多得多的認同(包括網絡研討會:))。顯然,不少人我的遇到了「卷積層是這樣工做的」和「咱們的卷積網絡達到最早進結果」之間的巨大差距。github

因此我認爲若是清空我塵土飛揚的博客並將這個推文擴展到這個主題應該獲得的長篇形式應該是有趣的事情。然而,與其列舉常見的錯誤或深刻分析它們,我更想深刻挖掘並討論如何避免出現這些錯誤(或者很是快速地修復它們)。這樣作的關鍵是遵循某個過程,據我所知,這個過程並無文檔記錄下來。讓咱們從促使我作這個的兩個重要發現開始吧。算法

1. 神經網絡訓練的困難是一種假象

據稱很容易開始訓練神經網絡。許多圖書和框架都以展現了若是採用30行代碼解決您的數據問題,並以此而自豪。這給你們一個很是錯誤的印象,即這些東西是即插即用。常見的示例代碼以下:api

>>> your_data = # 導入數據
>>> model = SuperCrossValidator(SuperDuper.fit, your_data, ResNet50, SGDOptimizer)
# 由此征服世界這些

這些庫和示例激活了咱們大腦裏面熟悉標準軟件的部分 - 一般它們認爲乾淨和抽象的API是能夠輕易得到的。 好比,後面的功能能夠採用以下調用:網絡

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

  這很酷! 一個勇敢的開發人員已經承擔了理解查詢字符串,URL,GET / POST請求,HTTP鏈接等等的負擔,而且在很大程度上隱藏了幾行代碼背後的複雜性。 這偏偏是咱們熟悉和期待的。 不幸的是,神經網不是那樣的。 它們不是「現成的」技術,第二個與訓練ImageNet分類器略有不一樣。 我試圖在個人帖子「是的你應該理解反向傳播」中經過反向傳播的例子說明這一點,並將其稱爲「漏洞抽象」,但不幸的是狀況更加可怕。 Backprop + SGD並無神奇地讓你的網絡正常工做。 Batch Norm也不會神奇地使其收斂得更快。RNN不會神奇地讓你「插入」文本。 只是由於你能夠將你的問題採用加強學習來建模,並不意味着你應該如此。 若是您堅持使用該技術而不瞭解其工做原理,則可能會失敗。 這讓我想到......架構

2. 神經網絡訓練無聲地失敗

當您寫錯或錯誤配置代碼時,您一般會遇到某種異常。你輸入了一個整數,但這原本應該是一個字符串的!某函數只須要3個參數!導入失敗!鍵值不存在!兩個列表中的元素個數不相等。此外,一般不會爲特定功能建立單元測試。框架

解決了這些問題,也只是訓練神經網絡的開始。一切均可以在語法上正確,但整個訓練沒有妥善安排,並且很難說清楚。 「可能的錯誤面」是很大的,邏輯(與語法相反)層面的問題,而且對單元測試很是棘手。例如,在數據加強期間左右翻轉圖像時,您可能忘記翻轉數據標籤。您的網絡仍然能夠(使人震驚地)工做得很是好,由於您的網絡能夠在內部學習檢測翻轉的圖像,而後左右翻轉其預測。或許你的自迴歸模型會由於一個偶然錯誤而意外地將它想要預測的東西做爲輸入。或者你想要修剪你的梯度可是修剪了損失,致使在訓練期間異常樣本被忽略。或者您從預訓練模型初始化了您的權重,但沒有使用原始均值。或者你只是用錯了正則化權重,學習率,衰減率,模型大小等。所以,錯誤設置的神經網絡只有在你運氣好的時候纔會拋出異常;大部分時間它會訓練,但默默地輸出看起來有點糟糕的結果。函數

所以,「快速和激烈」方法訓練的神經網絡並不能發揮其做用,咱們只會遭受其痛苦。 如今,痛苦是讓神經網絡運做良好的一個很是天然的過程,但能夠經過對訓練過程當中的全部細節瞭然於胸來減輕訓練過程的折磨。 在個人經驗中,與深度學習成功最相關的品質是耐心和對細節的關注。單元測試

祕方

鑑於上述兩個事實,我已經爲本身開發了一個特定的過程,使我可以將神經網絡應用於新問題。稍後我會竭力描述如何作到的。 你會發現它很是重視上述兩個原則。 特別是,它遵循從簡單到複雜的規律,而且在每一步咱們對將要發生的事情作出具體假設,而後經過實驗驗證它們或進行檢查直到咱們發現了問題。 咱們試圖防止的是同時引入了許多複雜「未經驗證的」問題,這必然會致使須要花不少時間去查找的錯誤/錯誤配置。 若是編寫您的神經網絡代碼就像訓練同樣,您須要使用很是小的學習速率並猜想,而後在每次迭代後評估整個的測試集。

1.瞭解你的數據

訓練神經網絡的第一步是根本不是接觸任何神經網絡代碼,而是從完全檢查數據開始。這一步相當重要。我喜歡花費大量時間(以小時爲單位)掃描數千個示例,瞭解它們的分佈並尋找模式。幸運的是,你的大腦很是擅長這一點。有一次我發現了數據中包含重複的例子。另外一次我發現了錯誤的圖像/標籤對。我一般會尋找不均衡的數據,也會關注本身對數據的分類過程,這些過程暗示了咱們最終會嘗試的各類架構。例如 -咱們須要局部特徵仍是全局上下文?數據有多少變化,這些變化採起什麼形式?什麼變化是假的,是能夠預處理的?空間位置是否重要,或者咱們是否想要將其平均化?細節有多重要,咱們能夠在多大程度上對圖像進行下采樣?標籤有多少噪聲?

此外,因爲神經網絡實際上能夠看做壓縮/編譯的數據集,所以您將可以查看網絡的(錯誤)預測並瞭解它們的來源。若是你的網絡給你的預測看起來與你在數據中看到的內容不一致,那麼就會有所收穫。

一旦得到定性的感知,編寫一些簡單的代碼來搜索/過濾/排序也是一個好主意,不管你能想到什麼(例如標籤的類型、大小、數量等),並可視化它們的分佈,和沿任何座標軸的異常值。異常值尤爲能揭示數據質量或預處理中的一些錯誤。

2.設置端到端的訓練/評估框架+得到基準

當咱們瞭解了數據以後,咱們能夠採用咱們超級精彩的多尺度ASPP FPN ResNet訓練牛X的模型嗎? 答案是不。 這是一條充滿痛苦的道路。 咱們的下一步是創建一個完整的訓練+評估框架,並經過一系列實驗驗證其正確性。在這個階段,最好選擇一些你能正確使用的簡單模型 - 例如 線性分類器,或很是小的ConvNet。 咱們但願對其進行訓練,可視化損失,任何其餘指標(例如準確度),模型預測,並在此過程當中使用明確的假設進行一系列實驗。

這個階段的須要注意的地方和建議主要包括:

  • 設置固定的隨機種子。始終使用固定的隨機種子,以保證當您運行兩遍代碼兩次時,能夠得到相同的結果。這消除了隨機因素,並將幫助您保持理智。
  • 簡化。確保禁用任何沒必要要的嘗試。例如,在此階段確定要關閉任何數據擴展。數據擴充是一種咱們可能在之後合併的正規化策略,可是此刻它也可能引入一些愚蠢錯誤。
  • 在評估時儘可能準確。在繪製測試損失時,對整個(大)測試集進行評估。不要只是在批量上繪製測試損失,而後依靠在Tensorboard中平滑它們。咱們追求正確,而且很是願意放棄保持理智的時間。
  • 驗證初始的損失。驗證您的損失函數是否以正確的損失值開始。例如。若是正確初始化最後一層,則應在初始化時查看softmax上的-log(1 / n_classes),這默認值能夠爲L2迴歸,Huber損失等。
  • 正確初始化。正確初始化每層權重。例如,若是你正在迴歸一些平均值爲50的值,那麼將誤差初始化爲50。若是你有一個正負樣本比例爲1:10的不平衡數據集,請在你的數據上設置誤差,使網絡初始化時就能夠預測爲0.1。正確設置這些將加速收斂並消除「曲棍球棒」損失曲線,由於在最初的幾回迭代中,您的網絡基本上只是學習誤差。
  • 人工評測。設置損失以外,人工能夠查驗和解釋的的指標(例如準確性)。儘量評估您本身(人類)的對該問題的準確性並與之進行比較。或者,對測試數據進行兩次標註,而且對於每一個示例,分別標註預測值和真實值。
  • 指定和數據無關的標準。訓練一個獨立於輸入數據的標準,(例如,最簡單的方法是將全部輸入設置爲零)。這應該比實際插入數據時更糟糕,而不會將其清零。這能夠嗎?即你的模型是否學會從輸入中提取任何信息?
  • 在少許數據上過擬合。看看模型是否可以在僅僅少數幾個數據(例如少至兩個)上過擬合。爲此,咱們增長了模型的容量(例如添加層或過濾器)並驗證咱們能夠達到可實現的最低損失(例如零)。我還想在同一個圖中同時顯示標籤和預測,並確保一旦達到最小損失,它們就會完美重合。若是他們不這樣作,那麼某個地方就會出現一個錯誤,咱們沒法繼續下一個階段。
  • 驗證減小訓練損失。在這個階段,你但願模型在數據集上欠擬合,由於你正在使用玩具模型。嘗試稍微增長容量,看看您的訓練損失是否應該降低?
  • 在訓練模型前再次查看數據。在您y_hat = model(x)(或tf中的sess.run)以前,查看數據是否正確。也就是說 - 您但願確切地瞭解將要網絡中的內容,將原始張量的數據和標籤解碼並將其可視化。這是惟一的「事實來源」。我沒法計算這個多少次節省了個人時間,並揭示了數據預處理和擴充中的問題。
  • 預測過程動態可視化。我喜歡在訓練過程當中對固定測試數據上的預測結果進行可視化。這些預測如何動態發生的,將爲您提供使人難以置信的直覺,使您可以瞭解訓練的進展狀況。不少時候,若是網絡以某種方式擺動太多,顯示出不穩定性,就有可能感受網絡難以擬合您的數據。學習率很是低或很是高,抖動量也很明顯。
  • 使用反向傳遞來繪製依賴關係。您的深度學習代碼一般包含複雜的,矢量化的和廣播的操做。我遇到的一個相對常見的錯誤是人們弄錯了(例如他們使用視圖而不是某處的轉置/置換)而且無心中混合了batch的維度信息。不巧的是,您的網絡一般仍然能夠正常訓練,由於它會學會忽略其餘示例中的數據。調試此問題(以及其餘相關問題)的一種方法是將某個示例的損失設置爲1.0,將反向傳遞一直運行到輸入,並確保僅在該樣本上得到非零梯度。更通常地說,梯度爲您提供了網絡中數據的依賴關係信息,這對調試頗有用。
  • 設置一個特例。這是一個更通用的編碼技巧,但我常常看到人們由於貪心,從頭開始編寫相對通常的功能,而導入bug。我喜歡爲我如今正在作的事情編寫一個專門的函數,讓它工做,而後在確保我獲得相同的結果再將其重構成通常功能的函數。這一般適用於矢量化代碼,其中我幾乎老是首先寫出徹底循環的版本,而後一次一個循環地將它轉換爲矢量化代碼。
相關文章
相關標籤/搜索