特斯拉AI總監(jiān):我復現了LeCun 33年前神經網絡,和現在區(qū)別不大
最近,特斯拉 AI 高級總監(jiān) Andrej Karpathy 做了一件很有趣的事情,他把 Yann LeCun 等人 1989 年的一篇論文復現了一遍。一是為了好玩,二是為了看看這 33 年間,深度學習領域到底發(fā)生了哪些有趣的變化,當年的 Lecun 到底被什么卡了脖子。此外,他還展望了一下 2055 年的人將如何看待今天的深度學習研究。
1989 年,Yann Lecun 等人發(fā)表了一篇名為「Backpropagation Applied to Handwritten Zip Code Recognition」的論文。在我看來,這篇論文有一定的歷史意義,因為據我所知,它是使用反向傳播訓練的端到端神經網絡在現實世界中最早的應用。
論文鏈接:http://yann.lecun.com/exdb/publis/pdf/lecun-89e.pdf
盡管數據集和神經網絡都比較小(7291 張 16x16 灰度數字圖像,1000 個神經元),但這篇論文在 33 年后的今天讀起來依然沒感覺過時:它展示了一個數據集,描述了神經網絡的架構、損失函數、優(yōu)化,還給出了訓練集和測試集的實驗分類錯誤率。它是那么得有辨識度,完全可以被歸類為現代深度學習論文,但它卻來自 33 年前。所以我開始復現這篇論文,一是為了好玩,二是為了將此練習作為一個案例研究,探討深度學習進步的本質。
實現
我試著盡可能地接近論文,并在 PyTorch 中復現了每個細節(jié),參見以下 GitHub 庫:
復現鏈接:https://github.com/karpathy/lecun1989-repro
最初的網絡是用 Lisp 實現的,使用了 Bottou 和 LeCun 1988 年提出的反向傳播模擬器 SN(后來被命名為 Lush)。這篇論文是法語的,所以我讀不懂。但從句法來看,你可以使用高級 API 指定神經網絡,類似于今天在 PyTorch 中做的事情。
在當代軟件設計中,庫的設計分為以下 3 個部分:
1)一個快速 (C/CUDA) 通用 Tensor 庫,用來實現多維張量的基本數學運算;
2)一個 autograd 引擎,用來跟蹤前向計算圖并為 backward pass 生成運算;
3)一個可腳本化的(Python)深度學習、高級 API,包含常見的深度學習運算、層、架構、優(yōu)化器、損失函數等。
訓練
在訓練過程中,我們要對 7291 個樣本組成的訓練集進行 23 次 pass,總共有 167693 個樣本 / 標簽展示給神經網絡。最初的網絡在一個 SUN-4/260 工作站(發(fā)布于 1987 年)上訓練了 3 天?,F如今,我用我的 MacBook Air (M1) CPU 就能運行這個實現,而且只用了 90 秒(實現了大約 3000 倍的加速)。我的 conda 被設置為使用本機 amd64 構建,而不是 Rosetta 模擬。如果 PyTorch 能夠支持 M1 的所有功能(包括 GPU 和 NPU),那么加速效果可能會更加明顯。
我還嘗試單純地在一個 A100 GPU 上運行代碼,但訓練卻更慢,原因很可能是網絡太小(4 層 convnet,最多 12 個通道,總共 9760 個參數,64K MACs,1K 激活),并且 SGD 一次只使用一個示例。也就是說,如果一個人真的想用現代硬件(A100)和軟件基礎設施(CUDA,PyTorch)來解決這個問題,我們需要把 per-example SGD 換成 full-batch 訓練,以最大限度地提高 GPU 利用率。這樣一來,我們很有可能額外實現約 100 倍的訓練加速。
復現 1989 年的實驗結果
原始論文給出了以下實驗結果:
eval: split train. loss 2.5e-3. error 0.14%. misses: 10
eval: split test . loss 1.8e-2. error 5.00%. misses: 102
但在第 23 輪 pass 之后,我的訓練腳本 repro.py 打印出的結果卻是:
eval: split train. loss 4.073383e-03. error 0.62%. misses: 45
eval: split test . loss 2.838382e-02. error 4.09%. misses: 82
所以,我只是粗略復現了論文的結果,數字并不精確。想要得到和原論文一模一樣的結果似乎是不可能的,因為原始數據集已經隨著時間的流逝而丟失了。所以,我必須使用更大的 MNIST 數據集來模擬它,取它的 28x28 digits,用雙線性插值將它們縮小到 16x16 像素,并隨機而不替換地從中抽取正確數量的訓練和測試集示例。
但我確信還有其他影響精確復現的原因,如這篇論文對權重初始化方案的描述有點過于抽象;PDF 文件中可能存在一些格式錯誤(小數點、平方根符號被抹掉等等)。例如,論文告訴我們,權重初始化是從統(tǒng)一「2 4 / F」中提取的,其中 F 是 fan-in,但我猜這里實際是「2.4 / sqrt(F)」,其中的 sqrt 有助于保持輸出的標準差。H1 和 H2 層之間特定的稀疏連接結構也有問題,論文只說它是「根據一個方案選擇的,這個方案先按下不表」,所以我不得不用重疊的塊稀疏結構做一些合理的猜測。
該論文還聲稱使用了 tanh non-linearity,但是我擔心這可能實際上是映射 ntanh(1) = 1 的「normalized tanh」,并有可能添加了一個縮小的殘差連接,這在當時非常流行,以確保 tanh 的平尾至少有一點梯度。最后,該論文使用了「牛頓法的特殊版本,該版本使用了 Hessian 的正對角近似」。但我只用了 SGD,因為它明顯更簡單。而且,論文作者表示,「這種算法被認為不會帶來學習速度的巨大提升」。
用新方法試試
這是我最喜歡的部分。我們生活在 33 年后的今天,深度學習已經是一個非?;钴S的研究領域。利用我們現在對深度學習的理解以及這 33 年積累的研發(fā)經驗,我們能在原結果的基礎上做出多少改進呢?
我最初的結果是:
eval: split train. loss 4.073383e-03. error 0.62%. misses: 45
eval: split test . loss 2.838382e-02. error 4.09%. misses: 82
首先需要明確,我們正在進行 10 個類別的簡單分類。但當時,這被建模為 targets -1(針對負類)或 + 1(針對正類)的均方誤差(MSE)回歸,輸出神經元也具有 tanh non-linearity。因此,我刪除了輸出層上的 tanh 以獲得 class logits,并在標準(多類)交叉熵損失函數中進行交換。這一變化極大地提高了訓練錯誤,使訓練集完全過擬合:
eval: split train. loss 9.536698e-06. error 0.00%. misses: 0
eval: split test . loss 9.536698e-06. error 4.38%. misses: 87
我懷疑,如果你的輸出層有(飽和的)tanh non-linearity 和 MSE 誤差,你必須更加小心權重初始化的細節(jié)。其次,根據我的經驗,一個微調過的 SGD 可以運行得很好,但是當前 Adam 優(yōu)化器(3e-4 的學習率)幾乎總是一個很好的基線,幾乎不需要調整。
因此,為了讓我更加確信優(yōu)化不會影響性能,我切換到了 AdamW with,同時將學習率設置為 3e-4,并在訓練過程中將學習率降至 1e-4。結果如下:
eval: split train. loss 0.000000e+00. error 0.00%. misses: 0
eval: split test . loss 0.000000e+00. error 3.59%. misses: 72
這在 SGD 的基礎上給出了一個稍微改進的結果。不過,我們還需要記住,通過默認參數也有一點權重衰減,這有助于對抗過擬合的情況。由于過擬合仍然嚴重,接下來我引入了一個簡單的數據增強策略:將輸入圖像水平或垂直移動 1 個像素。但是,因為這模擬了數據集的增大,所以我還必須將通道數從 23 增加到 60(我驗證了在原始設置中簡單地增加通道數并不能顯著改善結果):
eval: split train. loss 8.780676e-04. error 1.70%. misses: 123
eval: split test . loss 8.780676e-04. error 2.19%. misses: 43
從測試錯誤中可以看出,上述方法很有幫助!在對抗過擬合方面,數據增強是一個相當簡單、標準的概念,但我沒有在 1989 年的論文中看到它,也許這是一個出現略晚的創(chuàng)新?由于過擬合仍然存在,我從工具箱里拿出了另一個新工具——Dropout。我在參數數量最多的層(H3)前添加了一個 0.25 的弱 dropout。因為 dropout 將激活設置為零,所以它與活動范圍為 [-1,1] 的 tanh 一起使用沒有多大意義,所以我也將所有 non-linearities 替換為更簡單的 ReLU 激活函數。因為 dropout 會在訓練中引入更多的噪聲,我們還必須訓練更長的時間,pass 數達到 80。最后得到的結果如下:
eval: split train. loss 2.601336e-03. error 1.47%. misses: 106
eval: split test . loss 2.601336e-03. error 1.59%. misses: 32
這使得我們在測試集上只有 32/2007 的錯誤!我驗證過,僅僅在原始網絡中將 tanh 換成 relu 并沒有帶來實質性的收益,所以這里的大部分改進來自于 dropout??偟膩碚f,如果我回到 1989 年,我將把錯誤率降低 60%(把錯誤數從 80 降到 30 個),測試集的總錯誤率僅為 1.5%。但這一收益并不是「免費的午餐」,因為我們幾乎增加了 3 倍的訓練時間(從 3 天增加到 12 天)。但是推理時間不會受到影響。剩下的錯誤如下:
再進一步
然而,在完成 MSE → Softmax、SGD → AdamW 的轉變,增加數據增強、dropout,以及將 tanh 換成 relu 之后,我開始逐漸放棄那些容易實現的想法,轉而嘗試更多的東西(如權重歸一化),但沒有得到實質上更好的結果。
我還試圖將一個 ViT 縮小為一個與參數和 flops 數量大致匹配的「微型 ViT」,但它無法與一個卷積網絡的性能相媲美。當然,在過去的 33 年里,我們還看到了許多其他的創(chuàng)新,但是其中的許多(例如殘差連接、層 / 批歸一化)只能在大模型中發(fā)揮作用,而且主要用于穩(wěn)定大模型的優(yōu)化。在這一點上,進一步的收益可能來自于網絡規(guī)模的擴大,但是這會增加測試時推理的延遲。
在數據上動手腳
提高性能的另一種方法是擴大數據集的規(guī)模,盡管這需要花費一美元的標簽成本。這里再貼一下我們的原始基線:
eval: split train. loss 4.073383e-03. error 0.62%. misses: 45
eval: split test . loss 2.838382e-02. error 4.09%. misses: 82
由于現在可以使用整個 MNIST,我們可以將訓練集規(guī)模擴大到原來的 7 倍(從 7291 擴大到 50000)。僅從增加的數據來看,讓基線訓練跑 100 pass 已經顯示出了一些改進:
eval: split train. loss 1.305315e-02. error 2.03%. misses: 60
eval: split test . loss 1.943992e-02. error 2.74%. misses: 54
進一步將其與現代知識的創(chuàng)新相結合(如前一節(jié)所述),將獲得最佳性能:
eval: split train. loss 3.238392e-04. error 1.07%. misses: 31
eval: split test . loss 3.238392e-04. error 1.25%. misses: 24
總之,在 1989 年,簡單地擴展數據集將是提高系統(tǒng)性能的一種有效方法,而且不會影響推理延遲。
反思
讓我們總結一下,作為一個來自 2022 年的時間旅行者,我們從 1989 年的深度學習 SOTA 技術中學到了什么:
- 首先,33 年來的宏觀層面沒有太大變化。我們仍然在建立由神經元層構成的可微神經網絡體系架構,并使用反向傳播和隨機梯度下降對它們進行端到端優(yōu)化。一切讀起來都非常熟悉,只是 1989 年的網絡更小。
- 以今天的標準來看,1989 年的數據集還是個「嬰兒」: 訓練集只有 7291 張 16x16 的灰度圖像。今天的視覺數據集通常包含來自網絡的幾億張高分辨率彩色圖像(谷歌有 JFT-300M,OpenAI CLIP 是在 400M 張圖上訓練的),而且會增長到幾十億張的規(guī)模。每張圖像包含的像素信息增長了 1000 倍(384 * 384 * 3/(16 * 16)),圖像數量增長了 100,000 倍(1e9/1e4) ,粗略計算的話,像素數據輸入增長了 100,000,000 倍以上。
- 那時的神經網絡也是一個「嬰兒」:它大約有 9760 個參數、64K MACs 和 1K activations。當前(視覺)神經網絡的規(guī)模達到了幾十億參數,而自然語言模型可以達到數萬億參數。
- 當年,一個 SOTA 分類器在工作站上訓練需要 3 天,現在如果是在無風扇筆記本電腦上訓練只需要 90 秒(3000 倍加速),如果切換到 full-batch 優(yōu)化并使用 GPU,速度還能提升百倍以上。
- 事實上,我能夠通過微調模型、增強、損失函數,以及基于現代創(chuàng)新的優(yōu)化,將錯誤率降低 60% ,同時保持數據集和模型測試時間不變。
- 僅僅通過擴大數據集就可以獲得適度的收益。
- 進一步的重大收益可能必須來自一個更大的模型,這將需要更多的計算和額外的研究與開發(fā),以幫助穩(wěn)定規(guī)模不斷擴大的訓練。如果我被傳送到 1989 年,而且沒有一臺更大的計算機,我將無法進一步改進系統(tǒng)。
假設這個練習課程時間上保持不變,這對 2022 年的深度學習意味著什么?一個來自 2055 年的時間旅行者會如何看待當前網絡的表現?
- 2055 年的神經網絡在宏觀層面上基本上與 2022 年的神經網絡相同,只是規(guī)模更大。
- 我們今天的數據集和模型看起來像個笑話,2055 年的二者規(guī)模都大約有 10,000,000 倍。
- 一個人可以在一分鐘內訓練 2022 個 SOTA 模型,而且是在他們的個人電腦上作為一個周末娛樂項目來訓練。
- 今天的模型并不是最優(yōu)化的,只是改變了模型的一些細節(jié)、損失函數、增強或者可以將誤差降低一半的優(yōu)化器。
- 我們的數據集太小了,通過擴大數據集可以獲得適度的收益。
- 如果不擴大計算機基礎設施和投資相應規(guī)模的高效訓練模式的研發(fā),就不可能取得進一步的收益。
但我想要表達的最重要的趨勢是,隨著 GPT 這種基礎模型的出現,根據某些目標任務(比如數字識別)從零開始訓練整個神經網絡的設置,會由于「微調」而變得落伍。這些基礎模型由那些擁有大量計算資源的少數機構進行訓練,大多數應用是通過對網絡的一部分進行輕量級微調、prompt engineering,或是通過數據和模型蒸餾到更小的專用推理網絡的 optional step 來實現的。
我認為,這一趨勢在未來將十分活躍。大膽假設一下,你根本不會再想訓練一個神經網絡。在 2055 年,你可以用說話的方式去要求一個 10,000,000 倍大小的神經網絡大腦去執(zhí)行一些任務。如果你的要求足夠明確,它就會答應你。
當然,你也可以自己訓練一個神經網絡,但你為什么要這么做呢?