Neural-Networks

當我的神經網絡不學習時我該怎麼辦?

  • June 19, 2018

我正在訓練一個神經網絡,但訓練損失並沒有減少。我怎樣才能解決這個問題?

我不是在問過度擬合或正則化。我在問如何解決我的網絡在訓練集上的性能沒有提高的問題。


這個問題是有意籠統的,因此關於如何訓練神經網絡的其他問題可以作為這個問題的副本來關閉,其態度是“如果你給一個人一條魚,你就可以餵他一天,但如果你教一個人人釣魚,你可以養他一輩子。” 請參閱此 Meta 線程進行討論:回答“我的神經網絡不起作用,請修復”問題的最佳方法是什麼?

如果你的神經網絡不能很好地泛化,請參閱:當我的神經網絡不能很好地泛化時我該怎麼辦?

驗證您的代碼是否沒有錯誤

作家之間有句俗話,“所有的寫作都是重寫”——也就是說,大部分的寫作都是在修改。對於程序員(或至少是數據科學家)來說,這個表達可以重新表述為“所有編碼都是調試”。

每當您編寫代碼時,您都需要驗證它是否按預期工作。我發現驗證正確性的最佳方法是將代碼分成小段,並驗證每個段是否有效。這可以通過將段輸出與您知道的正確答案進行比較來完成。這稱為單元測試。編寫好的單元測試是成為一名優秀的統計學家/數據科學家/機器學習專家/神經網絡從業者的關鍵。根本沒有替代品。

**在調整網絡性能之前,您必須檢查您的代碼是否沒有錯誤!**否則,您不妨在RMS Titanic上重新佈置躺椅。

神經網絡有兩個特徵使得驗證比其他類型的機器學習或統計模型更重要。

  1. 神經網絡不像隨機森林或邏輯回歸那樣是“現成的”算法。即使對於簡單的前饋網絡,用戶也有責任就如何配置、連接、初始化和優化網絡做出大量決定。這意味著編寫代碼,編寫代碼意味著調試。
  2. *即使神經網絡代碼在沒有引發異常的情況下執行,網絡仍然可能存在錯誤!*這些錯誤甚至可能是網絡將要訓練的陰險類型,但會陷入次優解決方案,或者生成的網絡沒有所需的架構。(這是句法和語義錯誤之間區別的一個例子。)

這篇由 Chase Roberts 撰寫的Medium帖子“如何對機器學習代碼進行單元測試”更詳細地討論了機器學習模型的單元測試。我從文章中藉用了這個錯誤代碼的例子:

def make_convnet(input_image):
   net = slim.conv2d(input_image, 32, [11, 11], scope="conv1_11x11")
   net = slim.conv2d(input_image, 64, [5, 5], scope="conv2_5x5")
   net = slim.max_pool2d(net, [4, 4], stride=4, scope='pool1')
   net = slim.conv2d(input_image, 64, [5, 5], scope="conv3_5x5")
   net = slim.conv2d(input_image, 128, [3, 3], scope="conv4_3x3")
   net = slim.max_pool2d(net, [2, 2], scope='pool2')
   net = slim.conv2d(input_image, 128, [3, 3], scope="conv5_3x3")
   net = slim.max_pool2d(net, [2, 2], scope='pool3')
   net = slim.conv2d(input_image, 32, [1, 1], scope="conv6_1x1")
   return net

你看到錯誤了嗎?許多不同的操作實際上並沒有使用,因為以前的結果被新變量覆蓋了。在網絡中使用這段代碼仍然可以訓練,權重會更新,損失甚至可能會減少——但代碼肯定沒有達到預期的效果。(作者對使用單引號或雙引號也不一致,但這純粹是文體。)

與神經網絡有關的最常見的編程錯誤是

單元測試不僅限於神經網絡本身。您需要測試生成或轉換數據並輸入網絡的所有步驟。這裡的一些常見錯誤是

  • NAor NaNorInf數據中的值在輸出中創建NAor NaNorInf值,因此在損失函數中。
  • 獨立於樣本打亂標籤(例如,分別為標籤和样本創建訓練/測試拆分);
  • 不小心將訓練數據分配為測試數據;
  • 使用訓練/測試拆分時,模型引用原始的非拆分數據,而不是訓練分區或測試分區。
  • 忘記縮放測試數據;
  • 使用測試分區的統計數據而不是訓練分區來縮放測試數據;
  • 忘記取消縮放預測(例如像素值在 [0,1] 而不是 [0, 255] 中)。
  • 這是一個問題的示例,其中問題似乎是模型配置或超參數選擇之一,但實際上問題是如何計算梯度的一個細微錯誤。訓練準確性的下降是由於統計或編程錯誤嗎?

出於對一切美好事物的熱愛,擴展您的數據

數據的規模可以對訓練產生巨大的影響。有時,如果數據沒有擴展,網絡根本不會減少損失。其他網絡會減少損失,但速度很慢。縮放輸入(以及某些時候,目標)可以顯著改善網絡的訓練。

在你走之前爬行; 跑前走

廣泛而深入的神經網絡,以及具有奇異佈線的神經網絡,是目前機器學習中的熱門事物。但是這些網絡並沒有完全形成。他們的設計師從較小的單位建立起來。首先,構建一個具有單個隱藏層的小型網絡並驗證它是否正常工作。然後逐漸增加額外的模型複雜性,並驗證其中的每一個是否也能正常工作。

  • 一層中的神經元太少會限製網絡學習的表示,導致擬合 不足。太多的神經元會導致過度擬合,因為網絡會“記住”訓練數據。

即使您可以證明在數學上只需要少量神經元來建模問題,通常情況下,擁有“更多”神經元會使優化器更容易找到“好的”配置。(但我認為沒有人完全理解為什麼會這樣。)我在此處的 XOR 問題的背景下提供了一個示例:難道我的迭代不需要訓練 NN 的 MSE < 0.001 的 XOR 太高了嗎?.

  • 選擇隱藏層的數量可以讓網絡從原始數據中學習抽象。深度學習如今風靡一時,具有大量層的網絡已經顯示出令人印象深刻的結果。但是添加太多的隱藏層可能會導致過度擬合的風險,或者使優化網絡變得非常困難。
  • 選擇一個聰明的網絡佈線可以為你做很多工作。您的數據源是否適合專門的網絡架構?卷積神經網絡可以在“結構化”數據源、圖像或音頻數據上取得令人印象深刻的結果。遞歸神經網絡可以很好地處理順序數據類型,例如自然語言或時間序列數據。殘差連接可以改善深度前饋網絡。

神經網絡訓練就像開鎖

要獲得最先進的結果,甚至僅僅是好的結果,您必須設置所有配置為可以很好地協同工作的部分。設置一個實際學習的神經網絡配置很像撬鎖:所有部分都必須正確排列*。*就像在正確的位置有一個 tumbler 是不夠的一樣,僅僅正確設置架構或僅優化器也是不夠的。

調整配置選擇並不像說一種配置選擇(例如學習率)比另一種配置選擇(例如單元數)或多或少那麼簡單,因為所有這些選擇都與所有其他選擇相互作用,所以一個選擇 可以與 在 其他 地方 做出 的 另一個 選擇 結合 起來做得 很好.

這是配置選項的非詳盡列表,它們也不是正則化選項或數值優化選項。

所有這些主題都是活躍的研究領域。

  • 網絡初始化經常被忽視為神經網絡錯誤的來源。過大的間隔初始化可能會設置太大的初始權重,這意味著單個神經元對網絡行為有很大的影響。
  • 神經網絡和回歸模型之間的主要區別在於,神經網絡是許多非線性函數的組合,稱為激活函數。(參見:神經網絡和線性回歸的本質區別是什麼

經典神經網絡結果集中在 sigmoidal 激活函數(邏輯或 $ \tanh $ 職能)。最近的一個結果發現,ReLU(或類似的)單元往往工作得更好,因為它們具有更陡峭的梯度,因此可以快速應用更新。(請參閱:我們為什麼在神經網絡中使用 ReLU 以及如何使用它?)關於 ReLU 的一個警告是“死神經元”現象,它會阻礙學習;洩漏的 relus 和類似的變體避免了這個問題。看

還有許多其他選項。請參閱:具有優點/缺點的神經網絡中激活函數的綜合列表

非凸優化很難

神經網絡的目標函數只有在沒有隱藏單元、所有激活都是線性的、設計矩陣是滿秩時才是凸的——因為這種配置同樣是一個普通的回歸問題。

在所有其他情況下,優化問題都是非凸的,非凸優化是困難的。訓練神經網絡的挑戰是眾所周知的(請參閱:為什麼很難訓練深度神經網絡?)。此外,神經網絡有非常多的參數,這限制了我們只能使用一階方法(請參閱:為什麼牛頓法在機器學習中沒有廣泛使用?)。這是一個非常活躍的研究領域。

  • 學習率設置得太大會導致優化發散,因為你會從“峽谷”的一側跳到另一側。將此值設置得太小將阻止您取得任何實際進展,並可能使 SGD 中固有的噪聲壓倒您的梯度估計。看:

  • 如果梯度高於某個閾值,則梯度裁剪會重新縮放梯度的範數。我曾經認為這是一個設置後忘記的參數,通常為 1.0,但我發現通過將其設置為 0.25 可以顯著改善 LSTM 語言模型。我不知道為什麼會這樣。

  • 學習率調度可以在訓練過程中降低學習率。以我的經驗,嘗試使用調度很像正則表達式:它將一個問題(“我如何在某個時期之後繼續學習?”)替換為兩個問題(“我如何在某個時期之後繼續學習? ?”和“我如何選擇一個好的時間表?”)。其他人堅持認為日程安排是必不可少的。我讓你決定。

  • 選擇一個好的minibatch 大小可以間接影響學習過程,因為較大的 minibatch 往往具有較小的方差 (大數定律) 比更小的 mini-batch。您希望 mini-batch 足夠大以提供有關梯度方向的信息,但又足夠小以使 SGD 可以規範您的網絡。

  • 隨機梯度下降有許多變體,它們使用動量、自適應學習率、Nesterov 更新等來改進普通 SGD。設計一個更好的優化器是一個非常活躍的研究領域。一些例子:

  • Adam 優化器剛問世時就引起了極大的興趣。但最近的一些研究發現,具有動量的 SGD 可以勝過神經網絡的自適應梯度方法。“機器學習中自適應梯度方法的邊際價值”,作者:Ashia C. Wilson、Rebecca Roelofs、Mitchell Stern、Nathan Srebro、Benjamin Recht

  • 但另一方面,這篇最近的論文提出了一種新的自適應學習率優化器,據稱它縮小了自適應率方法和具有動量的 SGD 之間的差距。“在訓練深度神經網絡中縮小自適應梯度方法的泛化差距”,作者:Jinghui Chen,Quanquan Gu

採用歷史梯度信息自動調整學習率的自適應梯度方法在訓練深度神經網絡時被觀察到比具有動量的隨機梯度下降 (SGD) 更差的泛化能力。這使得如何縮小自適應梯度方法的泛化差距成為一個懸而未決的問題。在這項工作中,我們展示了 Adam、Amsgrad 等自適應梯度方法有時會“過度適應”。我們設計了一種新算法,稱為部分自適應動量估計方法 (Padam),它將 Adam/Amsgrad 與 SGD 統一起來,以實現兩全其美。在標準基准上的實驗表明,Padam 在訓練深度神經網絡時可以保持與 Adam/Amsgrad 一樣的快速收斂速度,同時泛化和 SGD 一樣。

正則化

選擇和調整網絡正則化是構建泛化良好的模型(即不會過度擬合訓練數據的模型)的關鍵部分。但是,當您的網絡正在努力減少訓練數據的損失時——當網絡沒有學習時——正則化可能會掩蓋問題所在。

當我的網絡不學習時,我關閉所有正則化並驗證非正則化網絡是否正常工作。然後我將每個正則化部分添加回去,並驗證其中的每一個在整個過程中是否有效。

這種策略可以查明某些正則化可能設置不當的地方。一些例子是

保留實驗日誌

當我建立一個神經網絡時,我不會對任何參數設置進行硬編碼。相反,我在一個配置文件(例如 JSON)中執行此操作,該文件被讀取並用於在運行時填充網絡配置詳細信息。我保留所有這些配置文件。如果我進行任何參數修改,我會創建一個新的配置文件。最後,我將所有訓練和驗證的每個時期的損失作為註釋附加。

我對保留舊結果如此著迷的原因是,這樣可以很容易地回顧以前的實驗。它還可以防止錯誤地重複相同的死胡同實驗。在心理上,它還可以讓你回顧並觀察“嗯,這個項目可能不是我今天想要的,但與我之前相比,我正在取得進步 $ k $ 幾週前。”

舉個例子,我想了解 LSTM 語言模型,所以我決定製作一個 Twitter 機器人來寫新的推文以回應其他 Twitter 用戶。我在空閒時間從事這方面的工作,在研究生院和工作之間。花了大約一年的時間,我迭代了大約 150 種不同的模型,然後才找到了一個我想要的模型:生成(有點)有意義的新英語文本。(一個關鍵的癥結,也是它進行瞭如此多嘗試的部分原因是,僅僅獲得較低的樣本外損失是不夠的,因為早期的低損失模型已經設法記住了訓練數據,所以它只是在回复提示時逐字複製密切相關的文本塊——需要進行一些調整以使模型更加自發並且仍然具有低損失。)

引用自:https://stats.stackexchange.com/questions/352036

comments powered by Disqus