祕籍在手,訓練不愁!特斯拉AI負責人Karpathy的超全神經網路訓練套路

大資料文摘發表於2019-04-26

祕籍在手,訓練不愁!特斯拉AI負責人Karpathy的超全神經網路訓練套路

大資料文摘出品

編譯:周素雲、宋欣儀、熊琰、ZoeY、顧晨波

訓練神經網路到底有訣竅和套路嗎?

Andrej Karpathy認為,還的確有。

這位特斯拉的人工智慧研究負責人、李飛飛的史丹佛高徒剛剛難得更新了部落格,推出了一篇長文《神經網路的訓練祕籍》,詳細講述了我們在訓練神經網路時候可以遵循的套路。

據Andrej Karpathy推特說,他本來是在推特上寫了一些自己訓練神經網路的經驗教訓,結果網友們反響強烈,所以他決定把相關內容更完整的在一篇文章中呈現給大家。

祕籍在手,訓練不愁!特斯拉AI負責人Karpathy的超全神經網路訓練套路

在這篇長文中,Andrej Karpathy像一個操心的老父親一樣,詳細且循循善誘地對所有機器學習從業者講述了構建神經網路的難處,以及如何才能循序漸進地構造神經網路。講述細緻,邏輯清晰,非常值得一看。

文摘菌也在第一時間對文章做了翻譯,以下是翻譯原文,enjoy~

以下內容翻譯至Andrej Karpathy的部落格:A Recipe for Training Neural Networks。

神經網路訓練是一個漏洞百出的抽象概念

都說萬事開頭難,但隨著訓練神經網路的一些即插即用的工具的出現,很多30行程式碼解決問題的案例讓人誤以為訓練神經網路很簡單,就像這樣:

>>> your_data = # plug your awesome dataset here
>>> model = SuperCrossValidator(SuperDuper.fit, your_data, ResNet50, SGDOptimizer)
# conquer world here

這些庫和例子是不是對你來說很熟悉?比如Request庫。

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

這些分享非常酷炫, 一些開發人員提供了理解查詢字串,URL,GET / POST請求,HTTP連線等等,並且在很大程度上隱藏了幾行程式碼背後的複雜性。

但不幸的是,神經網路並不是這樣的。它們不是“現成的”技術,這個可以在我之前寫的“你該知道backprop"一文中有介紹。

文章連結

https://medium.com/@karpathy/yes-you-should-understand-backprop-e2f06eab496b

Backprop + SGD並沒有神奇地讓你的網路運作,批量規範也不會神奇地使其收斂得更快。RNN也不會地讓輕而易舉地你“插入”文字。你可以用RL制定問題也不意味你應該這麼做。

如果你堅持使用該神經網路訓練而不瞭解其工作原理,就很容易會失敗。

訓練失敗的神經網路

當你錯誤配置程式碼時,通常會遇到某種異常。比如你在一個預期字串的位置插入了整數。因為該函式只需要3個引數,所以輸入失敗。對此我們通常可以為特定功能建立一個單元測試。

這只是訓練神經網路的一個開始。但可能出現所有語法正確,整個事情就是不對的情況,而且很難說清楚哪裡不對。

“可能的錯誤“的覆蓋面非常大,而且是邏輯性的(與語法相反),這很難通過單元測試判斷出來。例如,在資料增強期間需要左右翻轉影像時,你可能忘記翻轉標籤。你的網路仍然可以繼續工作得非常好,因為它可以在內部學習檢測翻轉的影像,然後左右翻轉其預測。

這之後,或許你的自迴歸模型會因為一個錯誤的錯誤而將它想要預測的東西作為輸入。或者你希望裁剪你的梯度但是模型裁剪了缺失值,導致模型忽略異常值。或者你會從預訓練檢查點初始化權重,但沒有使用原始均值。或者你只是搞砸了正則化強度,學習率,衰減率,模型大小等設定。因此,錯誤配置的神經網路只有在你運氣好的時候才會讓你發現異常,大部分時間它會自己訓練,默默工作,然後越來越糟糕。

過猶不及,訓練神經網路的“快速和大強度”的方法不起作用,只能帶來一系列麻煩,這在過去是對的。但現在,這些麻煩可以成為讓神經網路運作良好的一個部分,主要通過視覺化來達到。深度學習要想成功,最需要的品質是耐心和對細節的關注。

祕籍在手,訓練不愁

基於上文講述的兩個問題,我為自己開發了一套神經網路訓練”套路”。本文中我將嘗試描述這個套路。這個套路非常重視上述兩個原則,並且從簡到繁,在每一步都對將要發生的事情做出具體假設,然後通過實驗驗證或進行檢查,直到問題出現。


避免一次性地引入多個“未經驗證的”複雜因素,這會導致你長時間的查詢錯誤配置(如果有的話)。如果編寫神經網路程式碼就像訓練一樣,最好控制學習速率,作出猜測,然後在每次迭代後評估完整的測試集。

1.開始訓練前,先對資料了熟於心

訓練神經網路的第一步不是研究神經網路程式碼,而是從徹底檢查資料開始。這一步至關重要。我喜歡花費大量時間(以小時為單位)瀏覽數千個示例,瞭解它們的分佈並尋找規律。幸運的是,我們的大腦非常擅長這一點。

有一次我發現資料集中包含重複的例子,還有一次我發現了損壞的影像/標籤。我會嘗試尋找資料的不平衡和偏見。我通常也會關注我自己的資料分類過程,從中可以看到我們最終要探索的各種架構。

這個資料集的背景是什麼?有多少變化,它採取什麼形式?什麼變化是假的,可以預處理?空間位置是否重要,或者我們是否想要將其平均化?細節有多重要,我們可以在多大程度上對影像進行縮減取樣?標籤有多少?

此外,由於神經網路實際上是資料集的壓縮/編譯版本,因此你將能夠檢視網路(錯誤)預測並瞭解它們的來源。如果你的網路給你的預測看起來與你在資料中看到的內容不一致,那麼就會有所收穫。

一旦獲得定性意義,編寫一些簡單的程式碼來搜尋/過濾/排序也是一個好主意(例如標籤的型別,註釋的大小,註釋的數量等),你可以視覺化它們的分佈,發現沿任何軸的異常值,注意,異常值幾乎總能揭示資料質量或預處理中的一些錯誤。

2. 設定端到端的評估框架

當你瞭解資料就可以利用多尺度ASPP FPN ResNet並開始訓練模型了麼?那你真是想多了。

下一步應該做的是建立一個完整的訓練模型+評估框架,並通過一系列實驗獲得對其正確性的信任。在這個階段, 你最好選擇一種有把握的簡單模型,例如線性分類器或非常小的ConvNet。訓練的內容通常包括視覺化損失、準確度、模型預測等,並在此過程中使用伴有明確假設的一系列消融實驗。

固定隨機種子

始終使用固定的隨機種子來確保當你執行程式碼兩次時,還可以獲得相同的結果。這種方法可以消除差異因素的影響。

簡化

不要野心太大加入過多資料, 這個階段一定要關閉其他資料庫的擴充,在我們以後的正規訓練中可能會嘗試擴充資料, 但現在加入無疑是給自己找麻煩。

在評估中新增有效數字

當你在整個大的測試集進行評估並出現失敗時, 不要繼續進行批量的測試然後指望在Tensorboard進行平滑處理。我們需要追求準確,但也需要在適當的時候保持理智的放棄。

驗證損失函式

用正確的損失值來驗證損失函式, 例如,如果要保證初始化最後一層的正確, 你需要在softmax初始化時測試log(1/n_classes), 相同的預設值可以是L2 迴歸、Huber losses等。

初始化正確

確定初始化最終圖層權重正確。例如,如果你迴歸一些平均值為50的值,則將最終偏差初始化為50。如果你有一個比例為1:10的不平衡資料集:正數:負數,請設定對數的偏差,以便你的網路預測概率在初始化時為0.1。在最初的幾次迭代中,你的網路只是基本地學習偏差,正確設定這些將加速收斂並消除“曲棍球棒”損失曲線。

人為設定基準

監控除人為可解釋和可檢查的損失之外的指標,例如準確性。儘可能評估你自己的準確性並與之進行比較。或者,對測試資料進行兩次註解,將一個視為預測,將第二個作為基礎事實。

輸入-獨立基準

訓練一個輸入-獨立的基準,最簡單的方法是將所有輸入設定為零。如果不清零,當你插入資料時就變得很糟糕,因為你的模型可能會從輸入中提取資訊。

先過擬合一部分資料

我們可以增加模型的容量(例如新增層或過濾器)以驗證我們可以達到可實現的最低損失(例如零)。然後可以在同一個圖中同時顯示標籤和預測,並確保一旦達到最小損失,它們就會完美對齊。如果沒有對齊,那麼就意味著哪裡有一個錯誤,我們將無法進入下一個階段。

自我驗證

在使用玩具模型的階段,資料集和你的模型越不合適越好。嘗試稍微增加容量,然後看看你的訓練損失是否隨之下降了。

提前視覺化資料

在執行y_hat = model(x)sess.run在tf指令之前,最好先明確資料的位置,也就是說視覺化網路中的內容,將原始的大量資料和標籤視覺化。使它成為唯一的事實來源”。這個步驟無數次地節省了我的時間,並且為我揭示了資料預處理和擴充中的問題。

視覺化預測動態

我喜歡在訓練模型過程中對固定測試批次上的模型預測進行視覺化。這些預測的“動態”可以讓你直觀地瞭解到模型訓練的進展情況。如果你看到網路劇烈擺動,顯示出不穩定性,那就可能是你選擇的模型不適合這套資料。學習率非常低或非常高地情況下抖動量也會很明顯。

使用反向傳播來繪製依賴關係

深度學習程式碼通常包含複雜的,向量化的和工作量巨大的操作。一個相對常見的錯誤是人們弄錯指令(例如在應該使用transpose permute到地方使用了view並且無意中在不同維度上混合資訊。

令人沮喪的是,機器學習模型仍然可以正常訓練,因為它會學習忽略其他示例中的資料。除錯此問題(以及其他相關問題)的一種方法是將一個案例的的缺失值設定為1.0,然後反向傳遞一直執行到輸入,確保在這個案例到其他案例上獲得一個非零梯度值。梯度值可以提供網路中關鍵內容的資訊,這對除錯很有用。

使用特例

編寫特例是一個通用的編碼技巧,但我經常看到人們寫下一個非常複雜的例子。我建議先從相對一般的功能開始。我喜歡為我現在正在做的事情編寫一個非常具體的函式,讓它執行,之後概括它得出的的結果。這非常適用於向量化程式碼,我一般都是先寫出一個完全迴圈的版本,然後一次一個迴圈地將它轉換為向量化程式碼。

3.過擬合

到這個階段,我們應該對資料集有了很好的理解,同時我們必須保證我們的模型能夠滿足訓練與驗證結果的要求。對於任意模型,我們能夠計算得出一個我們足以信任的指標。同時,我們也對我們的模型效能提出不基於輸入的效能指標(模型效能不應受輸入影響),我們模型的效能應當勝過傻瓜模型(比如隨機分類)的效能,我們也應當對於人工的效能有一定的瞭解(我們希望我們的模型能夠達到人類智慧的層次)。到了處理擬合的這個階段,我們將對模型進行迭代,從而提高模型的質量。

我用來尋找高質量模型的方法就是兩步:首先找一個過擬合的模型(比如說,過擬合的判定標準可以是訓練損失),然後我對這個模型進行規範化(regularize)處理從而使這個過擬合的模型變成一個高質量的模型(以降低部分訓練損失的代價提高驗證損失的質量——即以提高一點訓練誤差的代價降低較多的驗證誤差)。我喜歡用兩步法的原因很簡單,如果我們不能在第一步中根本不能使用任何模型取得較低的誤差,這意味著我們的機器學習存在著一些問題,或者bug,或者錯誤配置。

這一步的一些提示與技巧:

  • 挑選模型。為了取得較好的訓練損失(較低的訓練誤差),你應當根據資料選取合適的網路結構。在選擇模型時,我的第一條建議是:不要想著一口吃能胖子。我看到了好多人,如同堆樂高玩具一般,使用神經網路工具箱瘋狂調整網路結構,狂熱地妄想著創造詭異的神經網路,一步取得高質量的模型。在你專案的初期階段,千萬要抑制自己產生這樣的想法。我經常建議人們就簡簡單單地查查和自己專案相關的論文,然後把他們模型的簡化版應用在自己的專案來取得較好的效能。比如說,你想對圖片分類,那就別先急著建立自己的神經網路,簡簡單單把ResNet-50抄過來試一試。在這之後,你就可以在這個網路上做一些自己的調整,並且用調整後的新網路,告訴ResNet-50誰才是真的爸爸。

  • Adam優化是靠譜的。在最初的訓練階段,我會使用學習速率為3e-4的Adam作為網路引數的優化/迭代方法。對於ConvNets(卷積神經網路)而言,精準調參的隨機梯度下降(Stochastic Gradient Descent)會比Adam擁有更好地效能,但是最優學習速率的區間會更窄,對於不同的問題會有不同的最優學習速率區間。(注意:如果你在使用遞迴神經網路或者相關的序列模型,Adam的使用就更加廣泛了。在你專案的初期,我再強調一次,不要想著一步登天,跟著論文依樣畫瓢。

  • 提高模型複雜程度時,每次只改動一處。如果你有多個想要加入到分類器裡的東西,我建議你依次加入它們(比如先加入dropout再加入batch normalization),來保證你得到你期待的效能。不要把所有東西都一股腦地一下塞到自己的模型裡。先用較少的資料集進行訓練,然後再加入更多的資料集,循序漸進,一步步提高模型的效能。

  • 不要相信預設的學習速率衰減率。如果你將原有的訓練程式碼運用在一些新的領域時,你應該對學習速率衰減率萬分警惕。對於不同的問題,你不僅僅應該用不同的學習速率衰減方法,更應該注意的是,對於特定的問題,衰減速率應當基於當前的epoch數(epoch number),這會基於你當前資料集的尺寸。比如說,ImageNet的學習速率在第30個epoch的時候,會減少10。如果你不在訓練ImageNet,那你最好就不要那麼做。如果你改程式碼調參的時候不小心,然後讓你模型引數的學習速率下降過快,你的模型引數可能會不收斂。在我自己的工作中,我會完全地取消學習速率的衰減率(我就用常值學習速率),然後在最後的最後來調節學習速率衰減率。

4.正則化

理想情況下,我們現在已經擁有一個有效的模型,至少對於訓練集來說是有效的。現在是時候放棄一些訓練精度,使它更規範並且具有更高的測算精度了。下面是一些提示和技巧:

  • 獲取更多資料。首先,目前為止,在任何實際環境中規範模型的最佳和首選方法是新增更多真實的訓練資料。一個很常見的錯誤是,當你可以收集更多的資料時,你卻絞盡腦汁花大量的工程週期在一個小資料集中提高效率。據我所知,新增更多的資料幾乎是唯一能夠保證提高配置良好的神經網路效能的方法。另一種方法則是整合學習器(如果你能負擔得起的話),但它只有在整合5個個體學習器以上才能展現比較好的效果。

  • 資料擴增。除了真正資料,你還可以使用半真半假的資料-嘗試更具有挑戰性的資料增強。

  • 創意性地造資料。如果半真半假的資料不起作用,那麼可以嘗試假資料。人們正在尋找擴充套件資料集的創造性方法;例如,域隨機化、模擬、甚至是GAN。

  • 預訓練。即使你有足夠的資料,如果可以的話,建議使用一個經過預先訓練的網路。

  • 堅持有監督學習。不要太執著於無監督的預先訓練。據我所知,與2008年的部落格文章所告訴你的不同,目前為止還沒有任何一個現代計算機視覺領域的無監督學習網路呈現了好的效果(儘管近幾天BERT模型表現不俗,但這很可能是由於文字的謹慎性和更高的訊雜比)。

  • 減小輸入維度。刪除可能包含虛假訊號的輸入。如果資料集很小,任何的偽輸入都將是一個過擬合的機會。同樣,如果低層次的細節不太重要,嘗試輸入較小的影像。

  • 縮減模型大小。在許多情況下,你可以使用域知識來約束並縮減模型大小。例如,過去流行在ImageNet的主幹網頂部使用完全連線層,但是這些層後來被簡單的平均池化所取代,這個過程消除了大量的引數使用。

  • 減小batch大小。由於batch內部的規範化,較小的batch在一定程度上對應著較強的規範化。這是因為相對於完全平均值-標準差比,batch的經驗平均值-標準差比更加有效,所以比例和偏移對於你的batch影響更大

  • 防止過擬合。新增dropout。對ConvNets使用dropout2d(一種dropout方法)。當然,請謹慎地使用,因為dropout似乎不能很好地處理批處理規範化。

  • 權重衰減。增加權重衰減懲罰力度。

  • 及時停止訓練。基於已測量驗證的損失,及時停止訓練,防止模型過擬合。

  • 嘗試大一點的模型。我在最後,並且是在“及時停止”之後提到這一點,是因為我在過去發現過幾次,更大的模型最終會有更大程度的過擬合,但它們的“及時停止”效能往往比較小的模型好得多。

最後,為了讓你更確信自己的神經網路已經是一個合理的分類器了,我建議你視覺化網路的第一層權重,並確保你的結果是有意義的。如果你的第一層過濾器看起來像噪音,那麼也許哪裡是有問題的。同樣,網路內的啟用函式有時會顯示奇怪的效應,你可以利用這些資訊去追蹤問題所在。

5.開始調參

你現在應該把你的資料集放在“迴圈迭代中”,為模型探索更寬泛的空間,以實現低驗證成本的體系結構。以下是關於這一步的一些提示和技巧:

  • 隨機網格搜尋。為了同時調整多個超引數,使用網格搜尋來確保能夠覆蓋所有設定引數,這顯然聽起來很誘人,但請記住,最好使用隨機搜尋。直觀地說,這是因為神經網路通常對某些引數比其他引數更敏感。在極限情況下,如果一個引數很重要但是改變引數b並沒有效果。那麼你應該多次取樣,因為這比簡單取樣幾個固定點更好。

  • 超級引數優化。目前有很多貝葉斯超引數優化工具箱可以供我們參考使用,當然,我的一些朋友也有成功使用他們的案例,但我的個人經驗是,探索一個非常好的、應用廣泛的模型以及高階別的訓練方法是使用實習生:)。哈哈哈…只是開玩笑。

6.再“榨”點東西出來

一旦你找到體系結構和超引數的最佳方法,你仍然可以使用一些技巧,從系統中提煉出一些精髓和方法:

  • 集合/合併。模型集合是一種非常有保證非常靠譜的方法,可以在任何事情上提升2%的精度。如果你在測試時無法承受計算的成本,請考慮使用“黑匣子”進行整體提升。

  • 自行訓練。把模型放在一邊然後讓它自己一直訓練。有些人在模型的驗證損失幾乎趨於平穩時就直接停止了訓練。這是不對的,根據我的經驗,模型的網路可以長時間不間斷地進行訓練並不斷優化提升。有一次我在寒假期間,在訓練模型的時候不小心離開了,然後模型自己一直訓練,當我1月份回來時,它是SOTA(達到了最好的狀態)

結論

一旦你做到了以上所有這些,你會對技術,資料集和問題有更加深刻的理解,因為你已經建立了整個神經網路訓練的邏輯,並瞭解了提高準確性的信心與把握,而且你已經探索了越來越複雜的模型,模型可以每一步都能按照你預測的方法途徑進行訓練優化並且得到相應的進步。

現在你可以閱讀大量的論文,嘗試大量實驗,並獲得你的SOTA結果。

祝好運!

相關報導:

http://karpathy.github.io/2019/04/25/recipe/

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

相關文章