搜尋課程與筆記

搜尋筆記

輸入主題、關鍵字或單元編號,例如 Flow Matching、U4。

    按 Esc 關閉 · ⌘ / Ctrl K 開啟搜尋

    L2.4 16 分鐘閱讀 2026年10月

    L2.4 同一張地圖從不同地方出發:損失曲面與初始化

    本篇重用2 篇

    同一張地圖,兩個登山口

    前幾篇用二次損失算清楚了步長、momentum 與逐方向縮放。但那些公式不能原封不動套到一般網路上:若損失像一張有許多山谷的地圖,同樣沿下坡走,換個登山口會不會走到不同地方?

    線性模型的平方損失是凸的;設計矩陣滿秩時才有唯一最小點。非線性網路則一般不保證凸,所以初始化可能影響走到的解,也會影響一開始的訊號與梯度。

    這在實務上表現成一件每個人都遇過的事:同一份程式、只換 random seed,跑出來的分數不一樣。

    問題是這件事有多嚴重。常見的說法是「高維空間裡到處都是局部極小」,聽起來像是每次都在賭運氣。這一篇要把它量出來——結果比那個說法溫和得多,而真正該花時間調的是另一個東西。

    同一個架構、同一份資料,只換隨機起點,
    會下到同一個谷底嗎?
    如果不會,那我該調的是哪個設定?

    課堂提問Q1

    把「同一張地圖、不同登山口」翻成數學:非凸到底讓哪一個保證失效了?初始化在這件事裡扮演什麼角色——它只是「隨機起點」,還是一個要調的超參數?

    先想一想,再展開看整理後的答案

    失效的是「局部訊息足以判斷全域」這個保證。 凸函數有一個很強的性質:∇L(θ)=0\nabla L(\theta)=0 就是全域最小。梯度是局部的量(霧中下山:Gradient 與方向),而凸性讓這個局部的量帶著全域的資訊。非凸之後這個橋斷了——∇L=0\nabla L=0 只告訴你「這裡是平的」,它可能是局部極小、鞍點、或一片平原。

    在光滑、步長夠小且梯度非零時,Taylor 的局部下降論證仍成立;但局部下降不保證走到全域最小,也不保證有限時間內收斂。同一個方法還要考慮起點、步長與停止時間。

    初始化不只是「隨機起點」,它至少決定三件事。

    1. 落在哪個盆地。 這是上面說的那件事,它給了 seed 之間的散佈。
    2. 一開始的梯度有多大。 權重的尺度直接決定前向訊號的尺度,而前向訊號的尺度又決定反向梯度的尺度。太小 → 幾乎沒有梯度可走;太大 → 落在飽和區(tanh⁡\tanh 的導數趨近零)或直接越過穩定門檻。
    3. 每一層拿到的梯度比例。 反向傳播會逐層乘上 Jacobian。尺度、激發函數與深度共同決定這個乘積是否變小或變大;不能把原因只歸給其中一項。

    所以 gain(初始化的標準差乘數)是一個和學習率同等地位的超參數,而不是一個實作細節。

    兩件要分開的事:散佈與尺度

    散佈是「同一個設定跑很多次會差多少」。它決定你該怎麼報告結果(同一個實驗跑三次結果都不同,該報哪一個? 會處理報告的部分),以及「A 比 B 好 2%2\%」這句話有沒有意義。

    尺度是「初始化的權重該多大」。它決定訓練能不能開始。這是一個可以先驗算出來的東西,而經典的答案是讓每一層的前向訊號變異數大致守恆:權重取標準差 ∝1/fan-in\propto 1/\sqrt{\text{fan-in}}(Glorot/He 初始化,參考文獻 1、2)。本篇用的 gain 就是在那個基準上再乘一個倍數,gain=1\text{gain}=1 表示照基準。

    兩件事的診斷方式不同:散佈要跑很多個 seed 看分布;尺度要在初始化時量前向與反向的統計量,不必訓練。

    散佈與尺度:兩張不同的圖

    圖說: seed 散布決定該報哪些統計量;初始化尺度則決定訊號與梯度能否進入可訓練區間。

    深度會不會讓梯度消失

    先把「梯度消失」寫成一個可以量的東西。深層網路裡第一層的梯度是一串因子的乘積(鏈式法則):

    ∇h0L=J1⊤J2⊤⋯JD⊤∇hDL,Jℓ=diag⁡(σ′(zℓ))Wℓ.\nabla_{h_0}L=J_1^\top J_2^\top\cdots J_D^\top\nabla_{h_D}L, \qquad J_\ell=\operatorname{diag}(\sigma'(z_\ell))W_\ell.

    這裡 hℓ=σ(Wℓhℓ−1+bℓ)h_\ell=\sigma(W_\ell h_{\ell-1}+b_\ell),JℓJ_\ell 描述這一層如何改變輸入;參數梯度還會乘上對應層的輸入。如果每層在梯度方向上的放大比例都大約是 cc,乘積就可能像 cDc^D:

    • c<1c<1:指數衰減 → 梯度消失,深層的前段學不動。
    • c>1c>1:指數放大 → 梯度爆炸。
    • c≈1c\approx1:比較有機會維持量級,但矩陣的方向與奇異值仍重要。

    關鍵是 cc 由什麼決定。 它是「權重的尺度」乘上「激發函數導數的典型值」——兩個都由初始化的 gain 控制。1/fan-in1/\sqrt{\text{fan-in}} 這個選擇的整個目的就是把 cc 調到 11 附近。

    深度讓逐層相乘的效應累積;初始化是在調整每一層的因子,不是消除深度的影響。

    固定 gain 掃深度,是檢查這件事的一種方法。但除了第一層/最後一層的比值,也要量兩者的絕對範數:兩邊一起變小時,比值仍可能不變。

    注意殘差連接把問題換成了另一個

    殘差連接 hℓ+1=hℓ+σ(Wℓhℓ)h_{\ell+1}=h_\ell+\sigma(W_\ell h_\ell) 讓局部 Jacobian 多出 identity:I+JFℓI+J_{F_\ell}。這提供直接傳遞的分支,但總梯度仍由所有分支共同決定;若 JFℓJ_{F_\ell} 抵消 identity,梯度仍可能縮小,不能把殘差當成不消失的保證。

    但那條通路也不會縮小:每一層都把訊號加上一份自己的輸出,所以前向與反向的量級都隨深度成長。本篇最後量到的比值是 0.23→0.52→1.36→13.0→3280.23\to0.52\to1.36\to13.0\to328(深度 22 到 3232)——從「平的」變成「指數爆炸」。

    修法是把那一份縮小:hℓ+1=hℓ+α σ(Wℓhℓ)h_{\ell+1}=h_\ell+\alpha\,\sigma(W_\ell h_\ell) 取 α=1/D\alpha=1/\sqrt{D} 或 α\alpha 可學(初始化為 00),或者在每個 block 裡放一個正規化層把量級拉回來。這就是為什麼殘差幾乎總是和正規化層一起出現——它們修的是相反方向的兩個病。一張圖裡同時有紋理、物件和整個場景,一個固定大小的核怎麼辦? 會回到這件事。

    兩個設定各怎麼調

    尺度先調,而且不用訓練就能調。 做法:初始化之後跑一個 batch 的前向,看每一層輸出的標準差。理想是各層大致相同(不隨深度衰減或放大);同時反向一次,看第一層與最後一層的梯度大小比。這兩個數字五分鐘就量得到,而它們決定訓練能不能開始。

    散佈用來決定報告方式,不是用來挑 seed。 跑 5–10 個 seed,報中位數與範圍。挑最好的那個 seed 來報告,就是 老師用考古題教學又出題:為什麼要切資料 講的選擇偏差再來一次——只是這次選的是隨機性而不是超參數。

    這一切依賴什麼。 一,本篇的網路很小(兩層 32 單元)。散佈的大小隨規模改變,而且方向不是單調的:更大的網路在很多任務上散佈更小(過參數化讓不同盆地的品質趨近),但更深的網路對初始化更敏感。二,本篇量的是訓練損失的散佈;泛化的散佈是另一個量,通常更大。三,用 Adam 訓練會壓掉一部分初始化的影響(它的逐座標尺度會自我調整),所以同一組實驗用 SGD 跑,尺度那條曲線會更陡。

    把散佈與尺度量出來

    模型是一個 1→32→32→11\to32\to32\to1 的 tanh⁡\tanh 網路,Adam η=0.01\eta=0.01 跑 4000 步,資料是共用 toy 的 30 個點。

    第一段:200 個隨機起點。

    import numpy as np, mlp                      # mlp.py:三十行的 numpy MLP,見本節末
    from ml_toy import toy_1d
    x, y = toy_1d(n=30, seed=0); X = x[:,None]
    
    fin = []
    for s in range(200):
        P = mlp.init([1,32,32,1], gain=1.0, rng=np.random.default_rng(s))
        P, L = mlp.adam_train(P, X, y, T=4000, eta=0.01)
        fin.append(L)
    fin = np.array(fin)
    print(f"最小 {fin.min():.4f}  中位數 {np.median(fin):.4f}  第90百分位 {np.percentile(fin,90):.4f}  最大 {fin.max():.4f}")
    print(f"比中位數差兩倍以上的比例:{(fin > 2*np.median(fin)).mean()*100:.1f}%")
    最小 0.0023  中位數 0.0034  第90百分位 0.0046  最大 0.0085
    比中位數差兩倍以上的比例:3.0%

    散佈存在,但它不是「每次都在賭運氣」。 中位數 0.00340.0034、第 90 百分位 0.00460.0046——九成的 seed 落在中位數的 1.351.35 倍以內。最大值 0.00850.0085 是中位數的 2.52.5 倍,而落在那個尾巴裡的只有 3%3\%。

    這提醒我們不要只報一個 seed;但第 90 百分位相對中位數的比例,不是統計顯著性的門檻。比較兩種方法時,要直接看它們的多次結果與配對差異。

    這些是訓練 4000 步後的損失,不一定是局部極小值。損失數字散在一段範圍,也不能證明參數空間中的低損失區域連通;要談地形還需要另外的分析。

    第二段:初始化尺度的掃描。 同一個架構,只改 gain(每個 gain 跑 12 個 seed 取中位數):

      gain=0.01   最終訓練 MSE 中位數 0.0064   最差 0.0135
      gain=0.1    最終訓練 MSE 中位數 0.0057   最差 0.0059
      gain=1      最終訓練 MSE 中位數 0.0031   最差 0.0061
      gain=3      最終訓練 MSE 中位數 0.0066   最差 0.0089
      gain=10     最終訓練 MSE 中位數 0.0108   最差 0.0133
      gain=30     最終訓練 MSE 中位數 0.0156   最差 0.0523

    又是一個 U 形,谷底正好在 gain=1=1(也就是 1/fan-in1/\sqrt{\text{fan-in}} 那個基準)。兩端壞得方式不同:gain=0.01=0.01 時權重太小,前向訊號幾乎是零、反向梯度也幾乎是零,4000 步還沒走到(0.00640.0064,而且最差的那個 seed 是 0.01350.0135);gain=30=30 時大部分單元一開始就落在 tanh⁡\tanh 的飽和區(導數趨近零),0.01560.0156,而且最差的那個 seed 是 0.05230.0523——尺度太大時 seed 之間的散佈也跟著變大。

    注意這個掃描的範圍:從谷底往兩邊各差 3030 倍,損失只變差 22 到 55 倍。它比學習率溫和(霧中下山,一步該走多大:梯度下降的一頁 的 η\eta 跨過門檻是 1012210^{122}),但它是一個實實在在的設定,而且因為它在訓練開始前就決定,調錯了不會給你任何錯誤訊息。

    第三段:深度與梯度的比值。 量初始化時 ∥∂L/∂W1∥\lVert\partial L/\partial W_1\rVert 與 ∥∂L/∂Wlast∥\lVert\partial L/\partial W_{\text{last}}\rVert 的比(8 個 seed 的中位數):

    (tanh,每層 32 單元,無殘差)
     depth     gain=0.5       gain=1       gain=2
         2     1.61e-01     1.64e-01     1.90e-01
         8     1.75e-01     2.26e-01     4.41e-01
        16     1.87e-01     2.28e-01     7.99e-01
        32     1.86e-01     2.19e-01     7.63e+00

    gain=0.5=0.5 與 11 的梯度比值在這個實驗中沒有明顯下降。但只看比例還不能排除兩端一起變小,應把各層的絕對梯度與更新量一起畫出來。

    而 gain=2=2 那一欄從 0.190.19 爬到 7.637.63——同一個深度、只把初始化放大兩倍,就從「平的」變成「放大 40 倍」。第三個結論因此是:

    這組結果顯示 gain 很重要;它不是「深度無關」的證明。

    刻意違反:加上未縮放的殘差連接。

    (同上,加 h_{ℓ+1} = h_ℓ + tanh(W_ℓ h_ℓ))
     depth      無殘差       有殘差
         2    1.64e-01    2.26e-01
         4    1.93e-01    5.20e-01
         8    2.26e-01    1.36e+00
        16    2.28e-01    1.30e+01
        32    2.19e-01    3.28e+02

    殘差把「平的」變成「指數爆炸」:深度 32 時比值是 328328,而無殘差是 0.220.22。原因寫在上面的 <Remark> 裡——每一層都把訊號加上一份自己的輸出,所以量級隨深度累積。

    這個未縮放的殘差例子提醒我們檢查量級,不代表所有殘差一定爆炸。縮放、零初始化的殘差分支與正規化都是可能的做法;要由具體架構決定,而不是宣稱殘差與正規化必須成對出現。

    補充本篇用的三十行 numpy MLP

    為了讓上面的實驗可以逐行檢查(而不是呼叫一個框架),mlp.py 只做三件事:init(sizes, gain, rng) 用 gain/fan-in\text{gain}/\sqrt{\text{fan-in}} 初始化每一層;grads(P, X, y, skip) 手寫前向與反向(tanh⁡\tanh 的導數是 1−tanh⁡21-\tanh^2,殘差那一支在反向時多加一個恆等項);adam_train(P, X, y, T, eta) 是 一個 η 不夠用:動量與 Adam 各買到什麼 那個更新式,含偏差修正。

    刻意沒有做的事:沒有 minibatch(全批次,30 筆資料不需要)、沒有正規化層、沒有 weight decay。這樣一來每一個實驗只有一個變數在動——這是能把 gain 的效果和其他東西分開的原因,也是為什麼這裡的數字比在一個完整框架上跑出來的乾淨。

    換 seed、換 gain、換深度

    互動 demo:散佈與尺度是兩件事:seed 的散佈量的是前者,梯度比量的是後者。

    檢查理解

    想一想

    一個團隊比較兩個架構,各跑一次:A 得到訓練 MSE 0.00320.0032、B 得到 0.00410.0041,結論是「A 比較好」。根據本篇的散佈實測,最合理的評論是:

    想一想

    一個 32 層的網路訓練不動,前幾層的權重幾乎不變。同事說「深層網路本來就會梯度消失,要加殘差」。根據本篇的實測,更準確的診斷順序是:

    想一想

    在 gain 的掃描裡,gain=30=30 的中位數是 0.01560.0156 而最差的 seed 是 0.05230.0523;gain=0.1=0.1 的中位數是 0.00570.0057 而最差是 0.00590.0059。這個對比說明:

    想一想

    下列哪一句不對?

    參考文獻

    1. Glorot, X., Bengio, Y. Understanding the difficulty of training deep feedforward neural networks. AISTATS 2010.(把「讓每層訊號變異數守恆」寫成初始化條件,1/fan-in1/\sqrt{\text{fan-in}} 的來源。)
    2. He, K. et al. Delving Deep into Rectifiers. ICCV 2015.(ReLU 下的對應版本,以及深度與初始化尺度如何一起決定訓練成不成立。)
    3. Li, H. et al. Visualizing the Loss Landscape of Neural Nets. NeurIPS 2018.(損失曲面的視覺化方法,以及殘差與寬度如何改變曲面的樣子。)