第 3 講・互動版

訓練不起來怎麼辦?

一言以蔽之:train 不起來的時候,第一件事永遠是看 training loss。因為病有兩種,而且吃錯藥會更糟。

好啊,我們來開始上課吧。

今天這堂課跟前兩堂不太一樣。前兩堂我們在蓋房子——第 1 講講機器學習就是找一個函式,第 2 講講怎麼把 model 變得有彈性,可以畫出彎彎曲曲的線。

今天我們不蓋房子。今天是修水電。

因為你一定會遇到這件事:你照著前兩講做,程式碼一行不差,跑下去——然後 loss 就卡在那邊不動了。或者更慘,你看到一排 nan

這時候你會做什麼?我猜你會做一件很多人都會做的事:開始亂改。把 learning rate 調小一點試試看,沒用;把層數加深一點試試看,沒用;換個 optimizer,還是沒用。改到半夜三點,你也不知道自己在改什麼。

今天這堂課就是要讓你不要變成那樣。

今天的 Roadmap

我先把整堂課的骨架給你。如果你今天只記得一件事,請記這張圖。

你的 model train 不起來,第一件事不是看 testing loss,是先看 training loss——也就是它在「考古題」上考幾分。

model train 不起來 → 第一件事:先看 training loss
training loss 就很大
MODEL BIAS(model 太弱,畫不出來)
或 OPTIMIZATION(畫得出來,但走不到)
↓ 該吃的藥
把 model 變大(更多 feature/更深)
或換更好的 optimizer(lr、momentum、Adam…)
training loss 很小,
但 testing loss 很大
OVERFITTING(背考古題背過頭)
↓ 該吃的藥
加資料/early stopping/regularization
(課本還會叫你把 model 變小,但這一刀很鈍,等一下實測給你看)
同一個症狀,兩帖完全相反的藥 ← 診斷錯了會越治越糟

注意看那兩欄的「藥」。它們是相反的。一邊叫你把 model 變大,一邊叫你把 model 變小。

所以如果你診斷錯了——你明明是 optimization 走不到,卻以為自己 overfitting,然後把 model 砍小——那你不是沒治好,你是把病加重了

你可能會想說:「不對啊,我最後在意的不是 testing loss 嗎?我當然要先看 testing loss 啊。」

對,你最後在意的是 testing loss,這沒錯。但在意它跟拿它來診斷是兩回事。你想想看,testing loss 很大,這件事本身給你的資訊超級少——它可能是因為你的 model 太爛,也可能是因為你 overfitting。這兩件事的藥剛好相反。

所以 testing loss 大,只是告訴你「你生病了」。training loss 才是告訴你「你生什麼病」的那個。這其實就是先驗血再開藥而已,沒有什麼高深的東西。

那今天分成三段:

  1. 診斷:怎麼分辨是 model bias 還是 optimization。這裡有一個很漂亮的判準
  2. 左邊那條路:optimization 走不到怎麼辦。learning rate、saddle point、momentum、adaptive learning rate、batch size 都在這段
  3. 右邊那條路:overfitting 怎麼辦。以及一個大家常常做錯的事——你不可以用 testing set 來挑 model

需要的先備知識:第 1 講的 gradient descent、第 2 講的 network。中間有一點點數學,但都是加減乘除等級的。

這一版是互動的。每一個滑桿你都可以拉、每一個點都可以拖,圖會馬上重畫。而且這一講的每一個數字都是你的瀏覽器現場算出來的,不是預先錄好的表演。請務必動手。

先講一條線:放棄基準線

今天要 fit 的東西是一條彎彎曲曲的線:,加一點雜訊,總共 120 筆資料。故意選一條有點難的——它有大的起伏,也有小的抖動。

正式開始之前,我要你先記一個數字:這份資料的變異數

為什麼這個數字重要?因為那是一個完全放棄的 model 會拿到的分數

什麼叫完全放棄?就是不管你輸入什麼 ,我一律回答「平均值」。這種 model 完全沒有學到任何東西,而它的 mean squared error 剛好就是資料的變異數。

所以等一下如果你看到某個 model 的 training loss 停在 附近,那代表什麼?代表它根本什麼都沒學到,它只是在猜平均而已。

這條「放棄基準線」你自己做實驗的時候也應該算一下。很多人 train 半天,其實 model 從頭到尾都在猜平均,但因為沒有基準線可以比,就一直沒發現。等一下你會看到,這條線會抓到兩個兇手。

第一段:到底是 model bias,還是 optimization?

好,假設你 train 下去,training loss 很大。看流程圖,你知道有兩種可能:model bias(model 太爛,它連在訓練資料上都畫不出那條線)或 optimization(model 其實畫得出來,但 gradient descent 走不到那組參數)。

這兩個要吃的藥不一樣。model bias 你要把 model 加大;optimization 你把 model 加大是沒用的——加大只會讓它更難走。

那怎麼分辨呢?

這裡有一個判準,我覺得是整個 deep learning 裡面最漂亮的推理之一。做法很簡單:拿一個比較淺的 model 來當對照組,然後看那個比較深的 model,它的 training loss 有沒有比淺的低。

為什麼這樣就分得出來呢?你想想看。假設我有一個 2 層的 network,跟一個 6 層的 network。

6 層的表達力一定包含 2 層的。為什麼?因為你只要讓多出來的那 4 層什麼事都不做(原封不動地把輸入複製到輸出),那 6 層就變成 2 層了。所以 2 層畫得出來的每一條線,6 層一定也畫得出來。這是數學上的事實,不是我猜的。

所以:如果 6 層的 training loss 比 2 層還高,那絕對不可能是 model bias。因為 model bias 的意思是「這個 model 的能力範圍裡面根本沒有好答案」,但我們剛剛證明了 6 層的能力範圍完全包含 2 層的。那它為什麼考得比較差?只有一個解釋:它找不到

好,那我們來實際跑一次。同樣的資料、同樣的 learning rate、同樣的步數,只有深度不一樣。下面每一根長條都是在你的瀏覽器裡當場 train 出來的。

動手玩 ①・深的一定比淺的好嗎
hidden layer:
learning curve(縱軸是對數刻度)。紅色虛線就是放棄基準線
每種深度最後的 training loss。灰色=有學到,紅色=貼在放棄基準線上
訓練中……

好,你看到了。0 層(就是一條直線,沒有 hidden layer)loss 是 ,最高。這個很合理——它就是 model bias。一條直線本來就畫不出那個彎彎曲曲的東西,這是 model 能力的問題,你把它 train 到天荒地老也沒用。

然後 1 到 4 層,全部掉到 這一帶。比直線好一大截,加 hidden layer 確實有用。

眼睛很利的同學會發現:1、2、3、4 層彼此並沒有一路往下,它們大概都在同一個高度上下抖。這也是實話——今天這個問題其實不難,一層 10 個 neuron 就差不多夠用了,再加下去也榨不出什麼。「加深一定會更好」本來就不是真的,我們現在要看的是另一件更嚴重的事。

那件更嚴重的事就在右邊:6 層跟 8 層,直接彈回那條紅色的放棄基準線上。

欸你知道嗎,這代表 8 層的 network 從頭到尾什麼都沒學到,它就只是在輸出平均值而已。它比一條直線還爛。

那我問你,這是 model bias 嗎?

不可能。8 層的表達力包含 4 層的,4 層做得到的事,8 層一定做得到。所以這一定是 optimization 的問題——它走不到。你切到 6 層或 8 層看看左邊那張圖:那條線一開始掉了一點點,然後就整條平掉了。那不是它在慢慢學,那是它完全停住了

這件事在歷史上是真的發生過的喔。深度學習在 2010 年之前一直做不起來,很大一部分原因就是這個——不是大家的 model 不夠大,是根本 train 不動

這邊有一個大家最常搞錯的地方

很多人看到上面那個實驗,第一個反應是:「所以深的 network 比較差嘛,那我用淺的就好了啊。」

千萬不要這樣想。這是把「走不到」跟「不存在」搞混了。那個好答案是存在的,8 層的 network 裡面一定有一組參數比 3 層更好,只是 gradient descent 沒有走到而已。

你可能還會想說:「那我把 sigmoid 換掉是不是就好了?」對,有一部分就是這樣。sigmoid 的微分最大只有 0.25,一層一層往回乘,8 層下來 gradient 就變成 0.25 的 8 次方,也就是六萬五千分之一——這叫 vanishing gradient(梯度消失),白話文就是訊號傳到前面幾層的時候已經沒聲音了

但我要你注意的是:換 activation 這件事,是在修 optimization,不是在加大 model。

這就是為什麼診斷要做對。診斷對了,你才知道自己該去修哪一邊;診斷錯了,你會在錯的那一邊改到天亮。

第二段:optimization 走不到,怎麼辦?

先講最基本的:learning rate

Learning rate 就是「一步跨多大」,第 1 講講過。大家都知道它很重要,但是「太大」跟「太小」到底長什麼樣子,這件事你一定要親眼看過一次。看過一次以後,你在螢幕上瞄一眼就認得出來了。

下面這個 2 層的 network,什麼都不改,只換 learning rate。你把滑桿從最左邊拉到最右邊,看它經歷什麼。

上面那排 optimizer 按鈕先不要碰,我們等一下會回來。現在先固定在 plain GD。

動手玩 ②・learning rate 的四種長相
optimizer:
learning curve(對數刻度)。灰色虛線是放棄基準線
train 完之後,它畫出來的線長這樣
0.30
計算中……

四種命運,我們一條一條看。你自己拉到對應的位置對一下:

這幾種長相你記起來:爆炸、看起來活著但其實死了、漂亮下降、龜速。

順帶一提,第二種給了我們一個很重要的教訓:learning rate 太大不一定會報錯。它可能只是安靜地把你的 model 弄壞,然後給你一條「看起來有在收斂」的平線。這就是為什麼前面要先算放棄基準線——沒有那條線,你根本抓不到這個兇手。

那它到底卡在哪裡?把地形攤開來看

好,我們知道 learning rate 要調對了。但為什麼會這樣呢?

要看清楚,我們得把 error surface 畫出來。可是剛剛那個 network 有幾百個參數,畫不出來。所以我們換一個只有兩個參數的問題——兩個參數才畫得出等高線圖。

這個問題我動了一點手腳:我讓兩個 feature 的尺度差很多。第一個 feature 的數值大概在 ±3 這個範圍,第二個大概在 ±0.2。比如說你在預測房價,一個 feature 是「坪數」(幾十),一個 feature 是「屋齡佔比」(0 到 1)。這種事情超常發生的。

算出來的結果是這樣:這個 error surface 兩個方向的陡峭程度是 差了 。一個是懸崖,一個是平原。數學上還可以直接算出來,learning rate 只要超過 ,就一定會發散。

你等一下在「動手玩 ④」的下半部就會看到這個地形。它根本不像一個碗,它像一條窄縫

而這就帶出一個很根本的麻煩:你只有一個 learning rate,但你要同時應付懸崖跟平原。你把步伐調到在懸崖上剛好不會摔下去,那你在平原上就是龜速前進;你把步伐調到在平原上走得夠快,那你在懸崖上一步就飛出去了。一個 learning rate 沒辦法同時滿足兩邊。

而且我要提醒你:這裡的「兩個方向」只是為了畫圖才簡化成兩個。真正的 network 有幾百萬個方向,每一個方向的陡峭程度都不一樣。你要用一個數字去應付幾百萬種地形——你就知道這有多不合理了。

這件事很重要,我們等一下會回來解決它。先記著。現在我們先處理另一個更根本的問題:它到底是卡在什麼東西上面?

卡住的到底是什麼?破除一個大迷思

好,我們來想一件事。什麼時候 gradient descent 會停下來?答案很簡單:gradient 等於 0 的時候。因為更新量是 gradient,gradient 是 0,你乘什麼都是 0,它就不動了。那 gradient 等於 0 的地方叫什麼?我們統稱它 critical point(臨界點)

現在問題來了:你被卡在一個 critical point,那是什麼地方?

大部分人第一個想到的是 local minima(區域最小值)——一個小山谷,四面八方都往上,你被困在裡面出不去。大家講到 deep learning 的困難,十個有九個會講這個。

但其實,在高維度的空間裡,local minima 是很稀有的東西。你更常卡到的是另一種,叫 saddle point(鞍點)——就是有些方向往上,有些方向往下的地方。名字來自馬鞍:你坐上去,前後是往下的,左右是往上的。

這個東西 gradient 也是 0,所以 gradient descent 一樣會在那邊發呆。但它跟 local minima 有一個天大的差別saddle point 是有路可以走的。你只要往對的方向走,loss 就會繼續下降。它不是死路。

下面這個地形有三個 critical point:兩個 local minimum、一個 saddle point。拖那顆白色的球去逛逛,右邊那朵花會告訴你「站在這個位置,往四面八方走分別會上坡還是下坡」——藍色是往上,紅色是往下

動手玩 ③・卡住的地方長什麼樣子
error surface(越藍越低)・★ 是 critical point・白球可以拖
站在白球那裡,往各個方向走的坡度
拖拖看那顆球。

那為什麼說「高維度空間裡 local minima 很稀有」呢?你想想 local minima 的定義:每一個方向都往上。一維你只要顧左右兩個方向,二維你要東西南北都往上,那一千萬個參數呢?你要一千萬個方向全部都往上——這就像丟一千萬個銅板,要求全部都是正面。下面直接算給你看:隨機生 3000 個臨界點,看有幾個是「四面八方全都往上」。

5 維
計算中……

你沒有看錯。一維的時候大概一半的臨界點是 local minima;到了 3 維只剩百分之二點多;到了 5 維以上,三千次裡面一次都沒有出現過。

而我們現在隨便一個 network 都有幾萬、幾百萬、甚至幾千億個參數。

所以結論是:你以為你卡在 local minima,其實你八成是卡在 saddle point。

這是個好消息喔。因為 local minima 是死路,saddle point 不是。saddle point 一定有往下的方向,你只是需要一點力氣、一點運氣,或者一點慣性,把自己推過去而已。

那要怎麼推過去呢?怎麼辦呢?

Momentum:讓球有慣性

好,這邊我們換個角度想。

現在的 gradient descent 是這樣:我站在山上,看一下腳下哪邊低,往那邊走一步。停下來,再看一次,再走一步。

你有沒有覺得這個行為很奇怪?它每一步都是從靜止開始的。

這不像球在滾啊。真的球從斜坡滾下來,它會越滾越快;而且滾到一個小坑的時候,它不會就停在裡面——它有慣性,會衝過去

那我們能不能讓參數也有慣性呢?可以,而且改起來出乎意料地簡單:

就這樣。多記一個「上一步移動了多少」,然後把它的一部分留下來。寫成式子:

就是速度, 通常設 0.9。這個東西叫 momentum(動量)——其實就是把上一步的方向留一點下來而已。那它為什麼能衝過小坑呢?因為在坑底 gradient 是 0,但 不是 0,它還帶著之前累積的速度,所以它會繼續往前跑。

下面是一個有兩個坑的地形,右邊的坑淺,左邊的坑深。球從右邊放下去。你來調那個 ,從 0 慢慢往上加,看它什麼時候才翻得過去。

動手玩 ④・給它一點慣性
快速切換:
雙坑地形。灰色=純 GD(β=0),綠色=目前的 β
同一批路徑畫成 loss 對步數
0.90
1.60
計算中……

下面是剛剛講的那條窄縫——就是兩個方向差了 倍的那個地形。同一個起點、同樣走 250 步,看誰真的走到星星上。上面那個 β 滑桿也管這裡的橘色路徑。

窄縫地形。★ 是正確答案 (w1=2, w2=5)
計算中……

先看雙坑那組。普通的 gradient descent(灰色)滑到右邊那個淺坑就停了。它沒有做錯任何事——那裡的 gradient 確實是 0,它就是該停。

但 β 調上去以後就不一樣了。綠球衝進淺坑沒有停,帶著慣性爬上小丘,翻過去,然後掉進左邊那個真正的谷底。

右邊那張圖更有意思:綠色那條線中間往上跑了一段。這件事很反直覺喔——我們不是在最小化 loss 嗎?怎麼會允許它變大?

但這正是重點。有時候你要先讓 loss 變糟,才能到更好的地方。一路只走下坡的人,走到第一個坑就結束了。

而且你會發現 β 有個門檻:慣性不夠大就是翻不過去,中間沒有模糊地帶。你可以自己拉滑桿找那個臨界點在哪。

Adaptive Learning Rate:每個參數自己一個步伐

好,我們現在回來處理那個懸而未決的問題。

還記得嗎?窄縫地形,一個方向陡、一個方向平,差了 倍。結論是一個 learning rate 沒辦法同時滿足兩邊

你去看上面那張窄縫圖:紅色那條(純 GD)一開始「唰」一下衝到谷底,然後就沿著谷底慢慢爬,250 步爬到一半就沒了。它不是壞掉,它就是慢

那怎麼辦呢?這時候你會想到一個很自然的想法:那不要用同一個 learning rate 不就好了?陡的方向用小步伐,平的方向用大步伐,每一個參數都有它自己的 learning rate。

好,那新的問題來了:機器怎麼知道哪個方向陡、哪個方向平?沒有人會告訴它。但其實它手上早就有答案了——看 gradient 的大小就知道了嘛。

所以做法就是:把每個參數的 gradient 平方累積起來,然後拿步伐去除以它的平方根。

其中 是 gradient 平方的移動平均, 是 gradient 的移動平均。那個 是什麼?就是剛剛的 momentum 啊。

所以你把 momentum(記得方向)跟 adaptive learning rate(記得陡不陡)這兩個東西加在一起,會得到什麼?

你會得到 Adam

對,就是那個你在每一份程式碼裡面都看到的 optim.Adam它其實就是「有慣性」加上「每個參數自己一個步伐」而已。這一頁的 Adam 是我們自己刻的,總共七行,沒有別的東西。

回去看那張窄縫圖的綠線:Adam 一路走過去,直接踩在星星上。為什麼?因為它發現 w2 這個方向 gradient 一直很小,就自動把 w2 的步伐放大了。沒有人教它,它自己從 gradient 裡面看出來的。

好,那現在請你回到「動手玩 ②」,把 optimizer 切成 momentum 跟 Adam,再把 learning rate 滑桿從頭拉到尾一次。你會看到三件事:

  1. 能用的範圍整個往左邊延伸了。把滑桿拉到最小的那格(0.002):plain GD 停在 0.5862,幾乎等於沒學;同一個 lr 換成 Adam 是 0.1494,早就開始 fit 了。plain GD 要到 0.017 才勉強上路,Adam 從滑桿的最左邊就能用。
  2. 同樣 800 步,Adam 掉得低一個數量級以上。plain GD 最好的一格是 0.1044,Adam 最好的一格是 0.0024——差了四十幾倍。右邊那條線也是到這時候才真的貼上去。
  3. 但 Adam 一樣有上限,而且它死得比較安靜。lr 拉到 0.48 以上,Adam 從頭到尾沒有出現半個 nan,它就是直接躺在放棄基準線 0.6053 上。沒有報錯,不代表有在學。(momentum 更妙:0.65 到 1.6 是躺著,再往上才爆成 nan。)

所以實務上你就先用 Adam。真的,不用想太多,先 Adam 下去。如果 Adam 都 train 不起來,那通常不是 optimizer 的問題,你要回去看資料、看 model 架構、看 loss 有沒有寫錯。

還有一個:Batch Size

我們前面算 gradient 的時候,都是一次拿全部資料去算,這個叫 full batch。這樣算出來的 gradient 是最準的,它是「真正的」下坡方向。

但你想想看,如果你有一千萬筆資料呢?你每走一步都要把一千萬筆算過一遍,那你一天走不了幾步。所以實務上大家的做法是每次隨機抓一小把出來算,這叫 mini-batch

代價當然有——你抓的那一小把不見得能代表全部,所以你算出來的方向是歪的,它會抖。我們實際測了一下(同一個 network、同樣 800 步):

計算中……

full batch 那條線最平滑,也走得最低,這符合直覺。但你看最後一欄「每步成本」:mini-batch 4 每一步只算 的資料量。同樣的秒數,它可以多走幾十倍的步數。所以「哪個比較好」這個問題,要看你問的是每一步還是每一秒

但接下來這件事才有趣。那個「吵」,我們一直把它當缺點在講。

其實它是優點。

你想想看,full batch 算出來的 gradient 是固定的。它走到一個 critical point,gradient 是 0,那就是 0,它永遠不會動了。但 mini-batch 不一樣——你換一把資料,error surface 就長得不太一樣,剛剛那個 gradient 是 0 的地方,換一把資料以後可能就不是 0 了。它會被自己的雜訊震出去。

我們用剛剛那個雙坑地形驗證一下。這次不用 momentum,只用最原始的 gradient descent,但在 gradient 上加雜訊——這就是在模擬 mini-batch 的效果。每個雜訊大小放 200 顆球,全部從淺坑那邊出發:

計算中……

完全沒有雜訊的時候,一顆球都逃不出來,200 顆全部卡在淺坑。雜訊加大以後,逃出去的比例一路往上。

所以那個「吵」不是純粹的壞事,它是一種免費的探索。這就是為什麼實務上幾乎沒有人用 full batch——不是只因為它慢,也是因為它太乖了。太乖的人走不出第一個坑。

好,第二段到這邊結束。左邊那條路(optimization)我們走完了。

第三段:另外一半的病 —— Overfitting

好,現在假設你把 optimization 的問題都修好了,你的 training loss 掉到很低很低。你很開心,然後你去看 testing loss。

然後你就笑不出來了。

這就是 overfitting。第 1 講我們提過一次:training loss 低只代表 model 把考古題背熟了。今天我們要真的做出來給你看

怎麼做出 overfitting 呢?食譜很簡單:資料給得很少,model 給得很大。我們只給 20 個點,但給它一個兩層、每層 24 個 neuron 的 network,參數量遠遠超過資料量。

下面那個滑桿是訓練步數。你從 0 慢慢往右拉,就等於在看這個 model 一步一步被 train 壞的過程。

動手玩 ⑤・把 model 越訓練越糟
訓練資料: weight decay:
那條經典的交叉(對數刻度)。綠色虛線=testing 最低點
這一步的 model 畫出來長什麼樣
2000
計算中……

左圖就是那條經典的交叉圖,你之後會看到一輩子。

藍色的 training loss 一路往下,掉到幾乎是 0。從 training loss 來看,這個 model 完美無缺。但紅色的 testing loss 呢?它先跟著往下,到了某一點——它掉頭往上了。那個轉折點(綠色虛線)之後,你每多 train 一步,model 就變爛一點。

右圖告訴你為什麼。把滑桿拉到最右邊,看那條紅線:它穿過了每一個藍點,一個都沒漏。為了穿過每一個點,它在點跟點之間扭出各種奇怪的形狀。

但那些點上面是有雜訊的啊。它不只學了規律,它把雜訊也一起背下來了。這就是 overfitting——說穿了就是把考古題連印刷錯誤都一起背起來

那怎麼辦呢?我們有四帖藥。上面那排按鈕已經有三帖了。

藥方一:加資料(最好的一帖)

按「60 筆」跟「120 筆」看看。你會發現一件很有趣的事:資料越多,training loss 反而越高。

這不是壞事喔。20 筆資料的時候 training loss 低到 0.00 幾,但那是因為它在背答案。資料變多以後它背不動了,被迫去學真正的規律,所以 training loss 上升——但 testing loss 下降,而且兩者的差距一路縮小:

切到不同資料量,這裡會累積比較表。

這就是為什麼加資料是最好的藥。它不是在壓制 model,它是在讓「背答案」這條路變得不划算。

藥方二:把 model 變小(課本這樣寫,但我們實測給你看)

這帖是課本上最常寫的第二帖。我們也測了:資料固定 20 筆,只改 hidden layer 的寬度,每個設定換三組不同的隨機初始值重跑,取平均,而且把誤差也畫出來

這裡有一個做實驗的重要習慣,我要特別提一下:一次實驗不能說明任何事。隨機初始值不一樣,結果就會不一樣。你等一下就會知道為什麼這件事非做不可。

藥方二實測・把 model 砍小,overfitting 會改善嗎
計算中……

好,這裡我要跟你講一件老實話,因為實驗結果跟課本講的不一樣

我們原本的預期是什麼?把 model 變小,overfitting 應該要改善才對。結果你看那排橘色的 testing loss,從最寬一路往左砍——它不但沒有變好,反而一路變糟。最大的那個 model,testing loss 反而是最低的。

那寬度 1 跟寬度 2 為什麼那麼慘?看綠色的 training loss 就知道了——它們連訓練資料都 fit 不起來。回去看流程圖:training loss 很大,那就不是 overfitting 了,那是 model bias

所以「把 model 變小」這一刀砍下去,發生了什麼事?我們沒有治好 overfitting,我們是把病換成了另外一種

而且你看那個誤差棒。中間那幾根,光是換一組隨機初始值,結果就可以差 ——比很多人論文裡宣稱的「改進」還大。如果我只跑一次就下結論,我可以「證明」出任何我想要的結果。

這件事你要記住:在 network 上,「把 model 變小」是一把很鈍的刀。它是傳統機器學習留下來的直覺,但在深度學習裡它常常不成立——反而是「model 放大,然後用別的方法管住它」比較有效。為什麼會這樣,到今天都還是個活躍的研究題目。

所以你之後如果聽到有人說「overfitting 喔?那你把 model 改小一點」,你可以在心裡打一個問號,然後問他:那你的 training loss 有跟著變高嗎?

那真正好用的是什麼?就是接下來這兩帖。

藥方三、四:Early Stopping 跟 Regularization

這兩帖才是實務上天天在用的,而且它們有一個共同的好處:你不用動資料,也不用動架構。

Early stopping 最直覺:既然 testing loss 是先降後升,那我就在它開始升之前停下來不就好了。回到「動手玩 ⑤」,把資料切回 20 筆、weight decay 切回 0,然後把滑桿拉到那條綠色虛線上——你會看到 testing loss 從 變成 好了 。什麼都沒改,只是早點收手而已。

但這裡有一個超級重要的細節:你絕對不可以看著 testing loss 決定什麼時候停。為什麼不行?下一段就是專門在講這件事。實務上要用驗證集(validation set),一份我們自己另外切出來、專門用來做決定的資料。

Regularization 換個角度想。剛剛那條紅線為什麼那麼扭?因為它的參數很大——參數大,函式才能急轉彎。那我們就在 loss 裡面多加一項懲罰,懲罰參數的大小:

這樣機器在降低 loss 的時候,就會順便把參數壓小,函式就平滑了。微分之後,這一項的效果就是每次更新都把參數乘上一個略小於 1 的數,所以它也叫 weight decay(權重衰減)——其實就是每一步都把參數縮水一點點而已。程式碼就一行。

回到「動手玩 ⑤」,把 weight decay 依序切成 0 → 1e-4 → 3e-3,看 testing loss 怎麼變:

切過那三個 weight decay 按鈕,這裡會累積比較表。

你有沒有發現一個模式?今天講的每一帖藥,都是有劑量的。learning rate 有劑量、model 大小有劑量、regularization 也有劑量。太少沒效,太多有毒——weight decay 開到 3e-3,training loss 自己就爛掉了,那又變回 model bias 了。

那劑量怎麼決定?這就是最後一段了,而且這一段是今天最容易做錯的地方。

最後一段:你不可以用 testing set 挑 model

好,我們現在有一堆選擇要做:幾層?每層幾個 neuron?learning rate 多少?weight decay 多少?什麼時候停?

你會怎麼決定?很自然的想法是:都試試看,哪個 testing loss 最低就用哪個。

這個想法非常直覺。它也非常危險。

我先講結論:你用 testing set 挑了 100 次,那 testing set 就變成 training set 了。

為什麼呢?我用一個實驗讓你看。這個實驗我把它設計得很極端:我做 500 個 model,但這 500 個 model 實力完全一樣,而且全部都是亂猜的。每一題都是丟銅板,正確率就是 50%,一個都不例外。然後我拿一份 40 題的 testing set 給它們考,考完挑分數最高的那個。

你覺得會發生什麼事?

動手玩 ⑥・冠軍換一份考卷就現形
500 個實力一模一樣的擲銅板 model,考同一份 40 題考卷
橫軸=挑他出來的考卷,縱軸=一份新考卷
計算中……

右邊那張散布圖是重點。

如果 testing set 的分數真的代表實力,那這張圖上的點應該要沿著對角線排——在這份考卷考得好的,在那份考卷也該考得好。但你看,那就是一團雲,相關係數大概是 0。

那顆紅星星就是我們挑出來的「冠軍」。它在左邊那份考卷上是第一名,在新考卷上就是一個平凡人。

你多按幾次「重跑」。你會發現偶爾那位冠軍在新考卷上還是考得不錯——但那也只是運氣,就跟他第一次考好一樣。單獨一次實驗永遠說明不了什麼(這句話今天已經出現第二次了)。真正的證據是讀數最後那一行:整套流程重複 300 次,冠軍在「挑他出來的那份考卷」上平均七十幾分,換一張新考卷就掉回 50 分附近。中間那二十幾分,完全是「挑選」這個動作自己變出來的

所以你想想看這件事的可怕之處。你每挑一次,就等於用掉了一點 testing set 的公信力。挑一次還好,挑 500 次,你挑出來的就不是「最強的 model」,而是「最會在這 40 題上碰運氣的 model」。那個 model 在 testing set 上的分數,已經不能拿來預測它在真實世界的表現了

這件事有一個很好記的說法:你不能拿考卷來練習,然後又拿同一張考卷來評分。

那正確的做法是什麼?

很簡單:把資料切成三份,而不是兩份。

所有的挑選、比較、調參,全部在 validation set 上做。testing set 你把它鎖起來,做完全部的事情以後打開來測一次,然後那就是答案。如果你測完覺得不滿意,回去改,再測一次——恭喜你,你的 testing set 已經被污染了。

這也是為什麼你看到某個模型「號稱」在某個 benchmark 上拿到很高的分數時,心裡可以留一個問號。不是說那個數字是假的,而是你要問:這個分數是第一次測出來的,還是調了三百次以後的最好一次?這兩件事在數字上長得一模一樣,但意義差很多。

而且更麻煩的是,一個 benchmark 被整個領域用了幾年之後,就算每個團隊都只測一次,整個領域加起來也已經對它挑了幾千次了。這時候那個 benchmark 的分數,就開始跟真實能力脫鉤。

我自己是覺得,這是「評估」這件事最難、也最沒有完美解的地方。唯一的解法其實只有一個:不斷做新的考卷。

隨堂小測驗

三題,選了馬上告訴你對不對。

換你動手:三個練習

光看是學不會的,你一定要自己改改看。

  1. 練習 1(暖身):回到「動手玩 ①」,把 6 層跟 8 層看清楚——它們卡在放棄基準線上。然後打開 notebooks/03_訓練不起來怎麼辦_你的調參急救手冊.ipynb,把深度實驗的 opt="gd" 改成 opt="adam"lr 改成 0.01。那些卡住的 network 救得回來嗎?如果救得回來,這件事本身證明了什麼?(提示:回去看流程圖左邊那兩個框)
  2. 練習 2(核心):在「動手玩 ②」裡,對 plain GD、momentum、Adam 各找出「能用的 learning rate 範圍」——從哪裡開始不爆炸、到哪裡開始就沒學到東西。把三個範圍寫下來比一比。為什麼 Adam 的範圍寬那麼多?回想一下它多做了哪兩件事。
  3. 練習 3(想一想,不用寫程式):我們今天的 model,輸入永遠是一個數字 x。前兩講也是——第 1 講輸入是過去 7 天的觀看數,也是固定 7 個數字。現在假設輸入是一個句子,你要判斷它是正面還是負面。「還不錯」是 3 個字,「這部電影我看到一半就想睡覺了但是配樂真的很好聽」是 24 個字。你的 network 第一層那個 W 矩陣,形狀是固定的。那輸入長度會變,怎麼辦?

練習 3 就是下一堂課的入口。想不出來很正常,那正是重點。

好,我們複習一下

今天講了很多工具,但如果你只帶走三件事,請帶走這三件:

  1. 先看 training loss,不要先看 testing loss。testing loss 大只告訴你「你生病了」,training loss 才告訴你「你生什麼病」。而這兩種病的藥是相反的——一個叫你把 model 變大,一個叫你把 model 變小。診斷錯了,你會越治越糟。
  2. 深的比淺的差,一定是 optimization 的問題。因為深的表達力包含淺的,它做得到,只是走不到。而走不到的地方通常不是 local minima——高維空間裡那東西稀有得很,你八成是卡在 saddle point,而 saddle point 是有路可以走的。momentum 給你慣性,adaptive learning rate 給你合適的步伐,mini-batch 的雜訊給你免費的探索。
  3. testing set 只能看一次。你用它挑了幾百次,它就從考卷變成考古題了。所有的決定都在 validation set 上做。

然後還有一件事,是今天的實驗自己告訴我們的:課本上寫的藥方,也要拿數據去驗。「overfitting 就把 model 變小」聽起來很有道理,但我們實際測下去,它在這個問題上根本不成立,而且雜訊比效果還大。寫論文、看論文、自己做實驗,都要記得問一句:這個結論跑了幾次?誤差有多大?

然後,今天所有東西都是自己刻的。Adam 就是七行,momentum 就是一行,regularization 也是一行。裡面真的沒有魔法。

最後留一個問題

你回頭看今天的每一個實驗,還有前面兩堂課的每一個實驗。我們的輸入是什麼?是一個固定長度的數字組合——一個數字、七個數字、兩個 feature,長度永遠是固定的。因為 network 第一層那個 W 矩陣,形狀是寫死的。

可是這個世界上最有意思的資料,長度都是會變的啊。

一個句子可以是 3 個字,也可以是 300 個字。一段語音可以是 2 秒,也可以是 20 分鐘。一段 DNA、一個分子、一張社群網路的圖——長度全部都不固定。

而且更麻煩的是,順序有意義。「我不喜歡你」跟「你不喜歡我」,字一模一樣,意思完全不同。

所以我們需要一個 model,它要能吃長度會變的一整排東西,而且它要知道誰跟誰有關係

那,怎麼辦呢?

這個東西叫 self-attention,我們下一堂課再跟大家講。

以上就是我今天想跟大家分享的內容。