U2.5 11 分鐘閱讀 2026年9月

U2.5 實作:同一個 toy,只換訓練目標

本篇重用M2.3導航每 30 秒更新一次:Euler 法與它的誤差

設定:只動兩行

沿用 U1.5toy.py——同一組月牙資料、同一個 MLP、同一組 random seed。唯一的改動是訓練目標與取樣器,這樣圖上的差異才歸因得回框架,而不是「這次剛好訓得比較好」。

先確認符號沒有搞反:這個單元 x0噪聲x1資料t 從 0 走到 1,跟 U1 的 notebook 剛好對調。

步驟 1:訓練

for step in range(n_steps):
    x1 = sample_data(batch)                    # 資料
    x0 = torch.randn_like(x1)                  # 噪聲
    t  = torch.rand(batch, 1)
    xt = t * x1 + (1 - t) * x0
    loss = ((model(xt, t) - (x1 - x0)) ** 2).mean()
    loss.backward(); opt.step(); opt.zero_grad()

U1.5 的訓練迴圈逐行對照,真的只有 xt 與目標那兩行不同。

換框架在程式裡只是換兩行;換掉的是那兩行背後的設計自由度。

會看到的事情是:loss 一樣不會掉到 0,而且停住的數字比上一個單元大。這不是壞事——U2.2 說過 LCFM\mathcal L_{\text{CFM}}LFM\mathcal L_{\text{FM}} 多一個常數,就是條件速度繞著邊際速度的變異數。真正要看的是它停在哪裡不動之後還會不會慢慢往下

步驟 2:取樣與軌跡圖(圖 a)

def euler_sample(model, x0, n_steps):
    x, h, traj = x0, 1.0 / n_steps, [x0]
    for i in range(n_steps):
        t = torch.full((x.shape[0], 1), i * h)
        x = x + h * model(x, t)
        traj.append(x)
    return torch.stack(traj)

固定和 U1.5 相同的 32 個起點(注意 FM 的起點是 t=0t=0 的噪聲,對應 DDPM 的 xTx_T),步數取 {10,50,1000}\lbrace 10,50,1000 \rbrace,把上一個單元的 DDIM 軌跡和這裡的 FM 軌跡並排。

會看到的事情是:兩邊的軌跡形狀明顯不同,而且在兩個月牙中間那塊區域都特別彎——那裡正是條件直線交叉最密的地方(U2.3 Q1)。

那到底哪一條路徑的軌跡比較好走?
這件事能不能量出來,而不是看圖猜?

步驟 3:步數對誤差(圖 b)

對步數 {2,5,10,20,50,100,1000}\lbrace 2,5,10,20,50,100,1000 \rbrace 各取 2000 個樣本,算終點與資料的 W2W_2(用 POT 的精確 OT),DDIM 與 FM 各一條線,畫在 log-log 座標上。

這張圖有兩個獨立的訊息:

  • 斜率=數值方法的階數。Euler 是一階,斜率應該接近 1-1;換成 Heun(二階)會變成接近 2-2
  • 高度=軌跡有多彎。同樣是 Euler,兩條線平行但高低不同,高的那條就是彎的那條。

先寫下你的猜測(哪條高?),再跑出來對。

互動 demo:步數對誤差的 log-log 圖。 這是圖 b 的「標準答案版」——速度場用資料精確算,所以看到的完全是離散化誤差,沒有網路的逼近誤差混進來。切「比方法」量到 Euler 1.05\approx-1.05、Heun 2.05\approx-2.05;切「比路徑」兩條線斜率一樣,但線性路徑高出 VP 兩到三倍。demo 只積到 t=0.9t=0.9:最後那一小段 σt0\sigma_t\to0 太陡,會把階數的訊號蓋掉。

補充為什麼要用中位數,而不是平均

少數幾條軌跡會在兩個 mode 之間的分岔點上「選錯邊」——步數少的時候走去左邊的月牙,步數多的時候走去右邊。這種樣本的誤差是 O(1)O(1),跟步數幾乎無關,平均起來會把整條曲線壓平,斜率就讀不出來了。

量收斂階數之前,先確認你量的是離散化誤差,不是分岔選錯邊。

取中位數就避開這件事。這不是把不好看的資料藏起來:分岔造成的誤差確實存在,但它和「離散化誤差隨步數怎麼縮」是兩個不同的現象,要分開量。跑自己的實驗時,值得把兩者都畫出來。

步驟 4:不重訓的轉換(圖 c)

載入 U1ϵθ\epsilon_\theta。它是在 VP 路徑上訓的,所以轉換時要用 VP 的 (αt,σt)=(αˉs,1αˉs)(\alpha_t,\sigma_t)=(\sqrt{\bar\alpha_s},\sqrt{1-\bar\alpha_s}),其中 ss 是把 FM 的 t[0,1]t\in[0,1] 映回 DDPM 步數的那個對應。注意它是反向的:FM 的 t=1t=1 是資料那一端,對到的是 DDPM 的 s=0s=0αˉ0=1\bar\alpha_0=1);FM 的 t=0t=0 是噪聲端,對到 s=Ts=T。寫 to_ddpm_step 的時候先把這個邊界條件對一次,方向弄反了整張圖都會是錯的。

def eps_to_velocity(eps_model, x, t):
    a, s, da, ds = vp_alpha(t), vp_sigma(t), vp_alpha_dot(t), vp_sigma_dot(t)
    eps = eps_model(x, to_ddpm_step(t))
    return (da / a) * (x - s * eps) + ds * eps

euler_sample 去解這個速度場,和 DDIM 50 步的終點比對。會看到的事情是:兩邊幾乎重合,剩下的差來自兩個取樣器的時間格點對不齊,不是模型不同。這張圖就是 U2.4「同一個物件、兩套座標」的數值實證;那一篇的那個動手推導已經把代數那一半做完了——代 VP 進轉換式,兩個係數都化成 β/2-\beta/2,正好是 PF-ODE。

課堂提問Q1

把步驟 4 跑出來之後,很自然會想:既然 ϵθ\epsilon_\theta 轉一下就能當速度場,那 U1 那個模型是不是也可以直接拿去用線性路徑的 FM 取樣器取樣?

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

不行,而且這個坑很容易踩。

ϵθ\epsilon_\theta 學到的是 E[ϵxt]\mathbb E[\epsilon\mid x_t],其中 xt=αˉsx1+1αˉsϵx_t=\sqrt{\bar\alpha_s}\,x_1+\sqrt{1-\bar\alpha_s}\,\epsilon——它只在VP 路徑的那一族中間分布上見過資料。線性路徑的 xt=tx1+(1t)x0x_t=t\,x_1+(1-t)\,x_0 給出的是不同的 ptp_t:同樣的 tt,兩邊的訊噪比不一樣,點的分布也不一樣。

具體一點:VP 在 α2+σ2=1\alpha^2+\sigma^2=1 上走,所以 Ext2\mathbb E\|x_t\|^2 大致固定;線性路徑的 Ext2=t2Ex12+(1t)2d\mathbb E\|x_t\|^2=t^2\mathbb E\|x_1\|^2+(1-t)^2 d,在中間會明顯縮小。把線性路徑的 xtx_t 餵進 VP 訓的網路,等於問它一個沒見過的分布上的問題,答案沒有理由是對的。

所以步驟 4 的轉換式裡,(αt,σt)(\alpha_t,\sigma_t) 必須是模型當初訓練時用的那一條路徑:「不用重訓」只換得了取樣器,換不了路徑。路徑是訓練時就寫進模型裡的東西。

這也順便解釋了 U2.4 為什麼要把四個旋鈕分開講:能事後換的(取樣器、座標)和不能事後換的(起點、路徑、配對、加權)不是同一類事情。

作業

  1. 非 Gaussian 起點。x0 換成 [2,2]2[-2,2]^2 上的均勻分布,重訓 FM,畫軌跡。說明 U1 的框架為什麼做不到這件事——對應 U2.0 Q1 的哪一個旋鈕?
  2. 加權對照。 訓兩個線性 FM 模型:一個均勻抽 tt,一個從 logit-normal(0,1)(0,1)tt。比較圖 b 的兩條曲線:斜率變了還是高度變了?先猜再跑,U3.5 會給答案。
  3. 路徑對照(把一個軸單獨隔離出來)。 保持獨立配對、保持 FM 目標,只把 (αt,σt)(\alpha_t,\sigma_t) 換成 VP 的 (sinπt2,cosπt2)(\sin\frac{\pi t}{2},\cos\frac{\pi t}{2}) 重訓。它的軌跡比線性 FM 直還是彎?和上面 demo 量到的方向一致嗎?如果不一致,先懷疑「網路有沒有訓好」而不是「理論錯了」。
  4. 分岔樣本。 把步驟 3 的誤差改成平均而不是中位數,看曲線怎麼變形;再把「終點跑到另一個月牙」的樣本單獨挑出來畫。你會得到兩條性質完全不同的曲線。
  5. (選)Likelihood。 用 Hutchinson 估計散度、沿 ODE 積分算測試點的 logp1\log p_1,和月牙混合 Gaussian 的精確 log-density 比對。

消化一下

想一想

圖 b 裡兩條 log-log 線斜率相同、高度不同。這說明什麼?

想一想

步驟 4 中若省掉 to_ddpm_step 的時間對應、直接把 FM 的 tt 餵給 ϵθ\epsilon_\theta,會怎樣?

想一想

線性 FM 的訓練 loss 停在一個大於零的數字上,最合理的解讀是:

參考文獻

  1. Lipman, Y., Chen, R. T. Q., Ben-Hamu, H., Nickel, M., Le, M. Flow Matching for Generative Modeling. ICLR 2023. (步驟 1 的訓練目標。)
  2. Karras, T., Aittala, M., Aila, T., Laine, S. Elucidating the Design Space of Diffusion-Based Generative Models. NeurIPS 2022. (把路徑、加權、取樣器當成可分開調的設計軸,並且是二階取樣器的標準參考。)
  3. Esser, P., et al. Scaling Rectified Flow Transformers for High-Resolution Image Synthesis. ICML 2024. (作業 2 的 logit-normal 時間取樣出自這裡。)