U2.1 10 分鐘閱讀 2026年9月

U2.1 速度場怎麼推動一整個分布?

本篇重用M2.0河面上的箭頭:Vector Field 與 ODE·M2.1從一片葉子到一群葉子:Flow Map 與 Pushforward·M2.2站內人數變化 = 進 − 出:Continuity Equation·M0.1閘門的進出:Divergence 與通量

先把三個容易混在一起的東西分開

上一篇把問法換成了:先決定中間分布,再找推動它的速度場。 要把這句話寫成數學,得先分清三個常常被混著用的物件。

可以想成開車。速度場是每個路口的指示牌——站在這裡、這個時刻,該往哪走、走多快;軌跡是某一台車照著指示牌走出來的那條路線;flow map 則是一張「所有車的位置對照表」:給定出發點,告訴你它在時刻 tt 會在哪裡。

速度場 ut(x):[0,1]×RdRdu_t(x):[0,1]\times\mathbb R^d\to\mathbb R^d。這是網路要學的東西。

三個物件裡只有速度場是被參數化的;軌跡與 flow map 都是它的後果。

ODE 與軌跡。 給一個起點 x0x_0,解

ddtxt=ut(xt),xt=0=x0,\frac{d}{dt}x_t=u_t(x_t),\qquad x_{t=0}=x_0 ,

就得到一條軌跡 txtt\mapsto x_t。在 Lipschitz 條件下解存在且唯一,所以軌跡不會交叉:兩條軌跡一旦在某個時刻碰到同一點,之後就是同一條。這件事在 U2.3 會變得很關鍵。

Flow map ψt(x0):=xt\psi_t(x_0):=x_t,把每個起點送到它在時刻 tt 的位置。對每個固定的 ttψt\psi_t 都是可逆的(把 ODE 反著解就是反函數)。

於是生成器就是 ψ1\psi_1:抽 x0p0x_0\sim p_0、解 ODE 到 t=1t=1、輸出 x1x_1。這正好回答了 U1.0 的問題——我們要的那個 GG 就是 ψ1\psi_1,只是它被一個速度場隱式地定義,而不是某個直接吐出 x1x_1 的網路。

既然生成器就是 ψ1\psi_1
為什麼不乾脆直接學 ψ1\psi_1,一次算完就好?

答案是:我們寫不出 ψ1\psi_1 的訓練目標。 回想 U1.0:要直接回歸一個「噪聲 → 資料」的函數,就得知道哪個 x0x_0 該對到哪個 x1x_1;沒有配對,L2L_2 的最佳解會塌成一張平均圖。ψ1\psi_1 正是那個我們沒辦法直接監督的物件。

速度場不一樣。一旦路徑被指定下來,中間每一個點「該往哪走」就是已知的——那是一個現成的回歸目標。我們付出的代價是取樣時要解 ODE(很多小步),換到的是一個真的訓得動的 loss。

這個取捨也不是永久的。U6U7 會回頭直接學 ψ\psi 本身——但那時候我們手上已經有一個訓好的速度場可以當老師了。先有速度場,才有辦法學 flow map。

從推動一顆點,到推動一整個分布

一群粒子都在動,它們的分布 ptp_t 當然也在變。ptp_t 就是 p0p_0ψt\psi_t 推過去的結果,寫成 pt=[ψt]#p0p_t=[\psi_t]_\#p_0——正是 U1.0 那個 pushforward,只是現在它隨 tt 連續變化。

ptp_t 隨時間怎麼變?想像空間裡一小塊區域:裡面的密度會變多還是變少,取決於流進來的粒子減掉流出去的。寫下來就是

  tpt(x)+(pt(x)ut(x))=0  \boxed{\;\partial_t p_t(x)+\nabla\cdot\big(p_t(x)\,u_t(x)\big)=0\;}

這就是 continuity equation(質量守恆)。ptutp_t u_t 是「流量」——多少東西正經過這裡、往哪個方向;它的散度就是淨流出。

動手自己導一次:一維的小盒子,兩行就出來了

先在一維做,因為高維只是把「兩端」換成「表面」。

取一段 [x, x+Δx][x,\ x+\Delta x]。裡面的質量是 xx+Δxpt(y)dypt(x)Δx\int_x^{x+\Delta x}p_t(y)\,\mathrm dy\approx p_t(x)\,\Delta x

質量只能從兩端進出,而單位時間通過一個點的量就是「密度 × 速度」=ptut=p_tu_t。所以

t(pt(x)Δx)=pt(x)ut(x)左端流進pt(x+Δx)ut(x+Δx)右端流出.\frac{\partial}{\partial t}\big(p_t(x)\,\Delta x\big) =\underbrace{p_t(x)u_t(x)}_{\text{左端流進}}-\underbrace{p_t(x+\Delta x)u_t(x+\Delta x)}_{\text{右端流出}} .

兩邊除以 Δx\Delta x、令 Δx0\Delta x\to0,右邊那個差商就是 x(ptut)-\partial_x(p_tu_t)

tpt(x)=x(pt(x)ut(x)).\partial_t p_t(x)=-\partial_x\big(p_t(x)u_t(x)\big).

移項就是上面那條式子。高維唯一的差別是「兩端」變成一個封閉曲面,差商變成散度 \nabla\cdotM0.1)。

注意這裡沒有用到任何機率的性質——同一條式子對水、對車流、對熱都成立。它說的只是「東西不會憑空出現或消失」。

這條式子真正好用的地方在於它是雙向的:

  • 給我一個速度場,它告訴我分布會怎麼變;
  • 反過來,如果我手上先有一條分布路徑 ptp_t,那任何滿足這條式子的 utu_t 都能推動它——注意這是一個充分條件,而且滿足它的 utu_t 通常不只一個(下一節會看到到底有多少個)。

我們就說這樣的 utu_t 生成 ptp_t。這正是上一篇那個新問法能站得住的原因。

要驗證一個速度場對不對,不必解 ODE——代進 continuity equation 就好。

互動 demo:速度場遊樂場。 選一個速度場,按播放看粒子沿著它走。先試「旋轉」:每一顆粒子都在動、軌跡是一圈一圈的,但整團雲從頭到尾一模一樣——因為 Gaussian 是旋轉對稱的。這就是「粒子在動、分布不動」最乾淨的例子。再試「收縮」「鞍點」「推到雙月」,看分布真的被推著變形。

同一對端點,無限多條路

固定 p0=N(0,I)p_0=\mathcal N(0,I)p1=pdatap_1=p_{\text{data}},中間怎麼走?

  • 可以讓 ptp_t 沿 VP 路徑走——U1 的 PF-ODE 就是這樣。
  • 可以讓 ptp_t 是兩端的線性插值。
  • 也可以先把所有東西縮成一個點、再展開(很怪,但合法)。

每一條分布路徑都有速度場生成它。更麻煩的是,同一條分布路徑也有無限多個速度場:只要 ww 滿足 (ptw)=0\nabla\cdot(p_t w)=0,那 ut+wu_t+w 也生成同一個 ptp_t。粒子可以沿著等密度面繞圈圈,分布一點都不會變——demo 裡的「旋轉」就是這件事的具體例子。

所以「找一個速度場」其實有兩層自由度:挑哪一條分布路徑,以及在那條路徑上挑哪一個速度場。

課堂提問Q1

continuity equation 說:任何滿足它的 utu_t 都生成同一條 ptp_t。而生成要的只是最後那個 p1=pdatap_1=p_{\text{data}}

所以聽起來,這兩層自由度其實是免費的——隨便挑一個滿足式子的速度場,生出來的樣本分布都一樣。

這樣想哪裡不對?

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

就「生出來的分布」而言,完全沒有不對——那正是 continuity equation 說的話。

不對的是「所以隨便挑都一樣好」這個推論。因為我們不是把 ψ1\psi_1 算出來,而是用數值方法解 ODE,而解 ODE 要花的步數取決於軌跡長什麼樣子

demo 裡的「旋轉」就是最極端的例子:粒子一圈一圈地繞,分布從頭到尾不動。它確實滿足 continuity equation((ptw)=0\nabla\cdot(p_tw)=0),但你要是把它加到一個正確的速度場上,軌跡會被繞得很長——而樣本分布一模一樣。

兩層自由度不影響「走到哪裡」,只影響「要走幾步才走得到」。

所以挑速度場的標準不是對錯,是代價U2.3 會看到路徑的選擇怎麼影響這件事,U3.2 會把「軌跡有多彎」寫成一個算得出來的數。

Flow matching 的做法很乾脆:用一個簡單的建構把兩層一次固定下來。那就是下一篇。

消化一下

想一想

兩條 ODE 軌跡在 t=0.5t=0.5 相遇於同一點,之後會發生什麼?

想一想

utu_t 生成 ptp_t,而 wtw_t 滿足 (ptwt)=0\nabla\cdot(p_t w_t)=0,那麼:

想一想

為什麼我們學速度場,而不是直接學生成器 ψ1\psi_1

參考文獻

  1. Chen, R. T. Q., Rubanova, Y., Bettencourt, J., Duvenaud, D. Neural Ordinary Differential Equations. NeurIPS 2018. (用神經網路當速度場、解 ODE 當生成。)
  2. Lipman, Y., Chen, R. T. Q., Ben-Hamu, H., Nickel, M., Le, M. Flow Matching for Generative Modeling. ICLR 2023. (下一篇的主線。)