🚧 為初學者構建高階神經網路的簡單注意事項

照片由 Henry & Co. 拍攝於 Unsplash
隨著機器學習不斷滲透到行業的各個方面,神經網路從未如此被熱議。例如,像 GPT-3 這樣的模型在過去幾周內席捲了社交媒體,並繼續以聳人聽聞的標題出現在科技新聞之外的媒體頭條。
與此同時,深度學習框架、工具和專門的庫透過使最先進的研究比以往任何時候都更容易使用,從而推動了機器學習研究的普及。我們經常看到那些幾乎神奇/即插即用的 5 行程式碼,它們承諾(接近)最先進的結果。作為 Hugging Face 🤗 的一員,我承認我對此負有部分責任。😅 這可能會給沒有經驗的使用者帶來誤導,讓他們以為神經網路現在是一項成熟的技術,而實際上,這個領域仍在不斷發展中。
實際上,構建和訓練神經網路通常是一種極其令人沮喪的體驗
- 有時很難判斷你的效能不佳是由於模型/程式碼中的錯誤,還是僅僅受限於模型的表達能力。
- 在流程的每一步,你都可能犯下無數微小的錯誤,而起初並未察覺,但你的模型仍然會訓練並給出不錯的效能。
在這篇文章中,我將嘗試重點介紹我在構建和除錯神經網路時的一些心路歷程。 我所說的“除錯”,是指確保你構建的東西和你腦海中的想法是一致的。我還會指出一些在你不知道下一步該怎麼做時可以檢視的東西,並列出我通常會問自己的問題。
這些想法很多源於我在自然語言處理領域的研究經驗,但其中大部分原則也適用於機器學習的其他領域。
1. 🙈 從擱置機器學習開始
這可能聽起來有悖常理,但構建神經網路的第一步是將機器學習放在一邊,專注於你的資料。檢視樣本、它們的標籤、如果你處理的是文字,則看看詞彙的多樣性、長度分佈等等。你應該深入研究資料,初步瞭解你正在處理的原始產品,並專注於提取模型可能能夠捕捉到的通用模式。希望透過檢視幾百個樣本,你將能夠識別出高層次的模式。你可以問自己一些標準問題:
- 標籤是否均衡?
- 是否有你不同意的黃金標籤?
- 資料是如何獲得的?這個過程中可能有哪些噪聲來源?
- 有沒有看起來很自然的預處理步驟(如分詞、移除 URL 或話題標籤等)?
- 樣本的多樣性如何?
- 什麼樣的基於規則的演算法可以在這個問題上表現得不錯?
對你的資料集有一個宏觀的(定性的)感覺和微觀的(定量的)分析同樣重要。如果你正在使用一個公開的資料集,可能已經有人深入研究過資料並報告了他們的分析(例如,這在 Kaggle 競賽中很常見),所以你絕對應該去看看這些分析!
2. 📚 像機器學習新手一樣繼續
一旦你對資料有了深入而廣泛的瞭解,我總是建議把自己想象成剛開始學習機器學習時的樣子,那時你還在 Coursera 上看 Andrew Ng 的入門課程。從最簡單的方法開始,以瞭解你的任務的難度以及標準基線模型的表現如何。 例如,如果你處理文字,二元文字分類的標準基線可以包括一個在 word2vec 或 fastText 嵌入之上訓練的邏輯迴歸。使用當前的工具,執行這些基線與執行 BERT 一樣容易(如果不是更容易的話),而 BERT 可以說被認為是許多自然語言處理問題的標準工具之一。如果還有其他基線可用,執行(或實現)其中一些。這將幫助你更加熟悉資料。
作為開發者,我們很容易在構建一些花哨的東西時感到滿足,但如果它只比簡單的基線高出幾個百分點,有時就很難從理性上證明其合理性,所以確保你有合理的比較點是至關重要的。
- 隨機預測器的表現會怎樣(尤其是在分類問題中)?資料集可能是不平衡的……
- 隨機預測器的損失會是什麼樣子?
- 衡量我的任務進展的最佳指標是什麼?
- 這個指標的侷限性是什麼?如果它完美無缺,我能得出什麼結論?我不能得出什麼結論?
- “簡單方法”中缺少了什麼才能達到完美的分數?
- 在我的神經網路工具箱中,是否有適合模擬資料歸納偏置的架構?
3. 🦸♀️ 不要害怕深入瞭解這些 5 行程式碼模板的背後
接下來,你可以根據之前獲得的見解和理解開始構建你的模型。如前所述,實現神經網路很快就會變得相當棘手:有很多相互配合的活動部件(最佳化器、模型、輸入處理管道等),在實現這些部件並將它們相互連線時,很多小事情都可能出錯。挑戰在於,你可能會犯這些錯誤,訓練一個模型而它永遠不會崩潰,並且仍然能得到不錯的效能……
然而,當你認為你已經完成了實現時,一個好習慣是對一小批樣本進行過擬合(例如 16 個)。如果你的實現是(基本)正確的,你的模型將能夠過擬合併記住這些樣本,顯示出 0 損失(確保你移除了任何形式的正則化,如權重衰減)。如果不能,很可能你在實現中做錯了什麼。在少數情況下,這意味著你的模型表達能力不足或容量不夠。再次強調,從一個小規模的模型開始(例如更少的層):你的目標是除錯模型,所以你需要快速的反饋迴圈,而不是高效能。
專家提示:根據我使用預訓練語言模型的經驗,將嵌入模組凍結為其預訓練值對微調任務的效能影響不大,但可以顯著加快訓練速度。
一些常見的錯誤包括:
- 錯誤的索引……(這些真的是最糟糕的 😅)。例如,確保你在正確的維度上收集張量……
- 在評估模式下忘記呼叫 `model.eval()`(在 PyTorch 中)或 `model.zero_grad()` 來清除梯度
- 輸入預處理中出了問題
- 損失函式收到了錯誤的引數(例如,在需要 logits 時傳遞了機率)
- 初始化沒有打破對稱性(通常發生在你用單個常量值初始化整個矩陣時)
- 一些引數在前向傳播過程中從未被呼叫(因此沒有收到梯度)
- 學習率一直取到像 0 這樣的奇怪值
- 你的輸入被以次優的方式截斷了
專家提示:當你處理語言時,要認真檢查分詞器的輸出。我數不清有多少個小時是因為分詞出了問題,而我卻在努力復現結果(有時是我自己以前的結果)上浪費了。🤦♂️
另一個有用的工具是深入研究訓練動態並在(例如 Tensorboard 中)繪製多個標量在訓練過程中的演變。至少,你應該關注你的損失、引數及其梯度的動態。
隨著損失的減少,你還想檢視模型的預測:要麼在你的開發集上進行評估,要麼,我個人最喜歡的是,打印出幾個模型的輸出。例如,如果你正在訓練一個機器翻譯模型,看到生成結果在訓練過程中變得越來越令人信服是相當令人滿意的。你需要特別注意過擬合:你的訓練損失持續下降,而你的評估損失卻在飛速上升。💫
4. 👀 調優但不要盲目調優
一旦你的一切都準備就緒,你可能想調整你的超引數,為你的設定找到最佳配置。我通常堅持使用隨機網格搜尋,因為事實證明它在實踐中相當有效。
有些人報告說使用像貝葉斯最佳化這樣的高階超引數調整方法取得了成功,但根據我的經驗,在一個合理的手動定義網格上進行隨機搜尋仍然是一個難以超越的基線。
最重要的是,用不同的超引數(或像啟用函式這樣的架構調整)啟動 1000 次執行是沒有意義的:比較幾次不同超引數的執行,以瞭解哪些超引數影響最大,但總的來說,期望透過簡單地調整幾個值來獲得最大的效能提升是不切實際的。例如,如果你效能最好的模型是用 4e2 的學習率訓練的,那麼你的神經網路內部可能發生了更根本的事情,你需要識別和理解這種行為,以便在當前特定情境之外重用這些知識。
平均而言,專家們使用更少的資源來找到更好的解決方案。
總而言之,一個幫助我更好地構建神經網路的通用建議是,儘可能地深入理解神經網路的每個元件,而不是盲目地(更不用說神奇地)調整架構。保持簡單,避免那些即使經過努力嘗試也無法合理解釋的小調整。顯然,在“試錯法”和“分析法”之間需要找到適當的平衡,但隨著你積累實踐經驗,很多這些直覺會變得更加自然。你也在訓練你自己的內部模型。 🤯
一些相關的閱讀資料以補充你的閱讀
- Joel Grus 的 (機器學習中的)可復現性作為工程最佳實踐的載體
- Cecelia Shao 的 除錯神經網路的清單
- Chase Roberts 的 如何對機器學習程式碼進行單元測試
- Andrej Karpathy 的 訓練神經網路的秘訣