搜尋課程與筆記

搜尋筆記

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

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

    M0.3 11 分鐘閱讀 2026年10月

    M0.3 漂流的溫度計:全導數、微分穿過積分與 JVP

    本篇重用2 篇

    讀數上升了

    你把一支溫度計綁在浮球上放進河裡,它跟著水漂,每分鐘回傳一次讀數。十分鐘後,讀數從 18.0 °C 升到 18.6 °C。

    問題:河水變熱了嗎?

    也可能只是溫度計漂到了原本就比較暖的淺灘,固定位置的水溫並沒有改變。讀數把兩種來源混在一起。要怎麼把「原地在變熱」與「移動到暖處」分開?

    沿用上一篇的想法,我們先看一小段時間裡,時間與位置各改變了多少,再用當下的變化率估計它們對讀數的貢獻。

    課堂提問Q1

    把情境翻成數學:這裡有幾個函數?溫度計的讀數是哪些東西的合成?我們想知道的是哪個量、拆成哪幾塊?

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

    最直覺的說法只說「溫度是時間的函數」,這漏掉了最重要的一層。

    這裡有兩個函數。第一個是溫度場 T(x,t)T(x,t):在位置 xx、時間 tt,那裡的水溫。它是一個定義在「位置 × 時間」上的函數;「水變熱」說的是 TT 對 tt 的變化,「下游比較暖」說的是 TT 對 xx 的變化。第二個是溫度計的路徑 x(t)x(t):時間 tt 時溫度計在哪裡。

    溫度計的讀數是這兩個函數套在一起:g(t)=T(x(t),t)g(t)=T\big(x(t),t\big)。它只是時間的函數——這就是為什麼直覺會把它當成「水溫隨時間的變化」;但它裡面藏著 x(t)x(t)。

    我們想知道的是 g′(t)g'(t) 拆成兩塊:一塊只跟「同一個位置的水溫在變」有關,另一塊只跟「溫度計移動到了溫度不同的位置」有關。翻譯到這裡,工具已經呼之欲出——這是 chain rule,只是要把它用到「有兩種輸入、其中一種自己也在動」的情形。

    電扶梯上走路

    你在往上的電扶梯上往上走。你的高度在上升,原因有兩個:電扶梯本身在動(就算你站著不動也會上升),以及你自己在走(就算電扶梯停了你也會上升)。兩個效果直接相加。

    而且它們可以分開量:站著不動,量到的是電扶梯的速度;等電扶梯停了再走,量到的是你自己的速度。只在動的電扶梯上走,你量到的是總和,拆不開——除非你另外知道其中一個。

    河的情形一樣。「同一個位置的水溫在變」是電扶梯,「漂到別處」是走路,漂流的溫度計量到的是總和。

    浮球溫度計的讀數變化拆成原地升溫與漂到較暖處兩項。

    圖:沿路徑的全導數是時間偏導與空間梯度乘速度之和。

    把兩個來源寫成數學

    沿路徑的全導數。 設 T(x,t)T(x,t) 對 xx、tt 都 differentiable,x(t)x(t) 是一條 differentiable 的路徑。讀數 g(t)=T(x(t),t)g(t)=T(x(t),t) 的變化率是

    ddtT(x(t),t)=∂tT(x(t),t)⏟位置固定、時間在走+∇T(x(t),t)⋅x˙(t)⏟時間固定、位置在動.\frac{d}{dt}T\big(x(t),t\big)=\underbrace{\partial_t T\big(x(t),t\big)}_{\text{位置固定、時間在走}}+\underbrace{\nabla T\big(x(t),t\big)\cdot\dot x(t)}_{\text{時間固定、位置在動}} .

    ∂tT\partial_t T 是把 xx 釘住、只讓 tt 動的 partial derivative;∇T\nabla T 是 M0.0 的 gradient(對位置各分量的 partial derivative),x˙\dot x 是漂速。第二項是 ∇T\nabla T 與 x˙\dot x 的內積——正是 directional derivative 乘上速率:往溫度變化最快的方向漂得越快,這一項越大。左邊叫 TT 沿路徑的 total derivative(全導數)。

    展開細節為什麼是相加(一階 Taylor 兩次,五行)

    從 tt 到 t+ht+h,位置從 xx 變成 x+Δxx+\Delta x,Δx≈hx˙\Delta x\approx h\dot x。用一階 Taylor(M0.2)對兩個輸入同時展開:

    T(x+Δx, t+h)−T(x,t)=∂tT⋅h+∇T⋅Δx+o(h).T(x+\Delta x,\,t+h)-T(x,t)=\partial_t T\cdot h+\nabla T\cdot\Delta x+o(h).

    小位移造成的變化是各方向變化的線性疊加——這就是 differentiable 的意思(局部像一個平面,只是現在的「平面」多了一個時間軸)。代 Δx≈hx˙\Delta x\approx h\dot x、除以 hh、取極限,就得到上面的式子。如果 TT 在某處不 differentiable(例如兩股水溫不同的水流交會、溫度有跳躍),這個線性疊加不成立,式子在那一點失效。

    微分穿過積分(Leibniz 法則)。 同一個「微分可以逐項拆開」的邏輯,再往前推一步。假設你關心的不是一支溫度計,而是很多支固定位置的溫度計的加權平均:

    m(t)=∫w(x) T(x,t) dx.m(t)=\int w(x)\,T(x,t)\,dx .

    有限筆觀測的加權和,可以逐項微分。積分是連續版本的平均,因此我們會想把同樣的動作延伸過去:

    ddt∫w(x) T(x,t) dx=∫w(x) ∂tT(x,t) dx.\frac{d}{dt}\int w(x)\,T(x,t)\,dx=\int w(x)\,\partial_t T(x,t)\,dx .

    這就是 Leibniz 法則:微分可以穿過積分符號,變成對被積函數的 partial derivative。 它成立的條件是 ∂tT\partial_t T 存在,並且 ∣w(x) ∂tT(x,t)∣|w(x)\,\partial_t T(x,t)| 能被一個不依賴 tt、可積的函數壓住(技術名詞是 dominated convergence;直覺是「沒有任何一支溫度計的變化率大到壞掉整個平均」)。一般形式是 ddy∫f(x,y) dx=∫∂yf(x,y) dx\frac{d}{dy}\int f(x,y)\,dx=\int\partial_y f(x,y)\,dx——變數叫什麼不重要,重要的是被微分的變數不是積分變數,而且積分範圍不隨它改變。這條規則在之後很多推導裡是關鍵的一步,後面會直接用到。

    一支溫度計拆不開

    結論。 十分鐘升了 0.6 °C,是兩項沿路徑積分後的總和;單靠讀數,不能知道各占多少。直接把它當成原地升溫,就是忽略了移動項。河水處處同溫會讓移動項為零,但這需要另外確認,不能從同一支漂流溫度計的讀數推出。

    要補什麼才能拆開。 我們需要浮球所在位置、當下時間的額外資訊。知道附近水溫的空間 gradient 與浮球速度,就能算出 ∇T⋅dx/dt\nabla T\cdot \mathrm dx/\mathrm dt,再從總變化率扣掉。也可以量當下位置的固定觀測值,估計那裡的 ∂tT\partial_tT。只在起點放一支固定溫度計不一定夠:它量到的是起點的升溫率,未必等於下游浮球所在位置的升溫率。

    例如在 T(x,t)=18+0.001x+0.0005tT(x,t)=18+0.001x+0.0005t 的一維模型裡,xx 以公尺、tt 以秒計,兩個變化率處處相同。浮球以 0.50.5 m/s 漂了十分鐘,原地升溫與移動帶來的變化各為 0.30.3 °C,合計 0.60.6 °C。這時固定測站才可以直接幫我們拆開兩項。

    假設與失效點。 式子要求 TT 對位置與時間都 differentiable。若浮球漂過一條支流的匯入口——那裡兩股不同溫度的水還沒混合、溫度幾乎是跳躍的——∇T\nabla T 在匯入口沒有定義,讀數會在一分鐘內跳一大格,那一段的變化不能用兩項之和來解釋,也不該把它算進「水變熱」。

    程式裡怎麼算全導數

    當 TT 不是手寫的式子,而是一個程式(例如一個神經網路 f(x,t)f(x,t),輸入輸出都是高維向量),全導數 ∂tf+(∇xf) x˙\partial_t f+(\nabla_x f)\,\dot x 有一個計算上的陷阱:∇xf\nabla_x f 是一個 d×dd\times d 的 Jacobian 矩陣。dd 是幾萬時,把它整個算出來要 dd 次前向或反向傳播,記憶體 d2d^2,完全不可行。

    但我們不需要整個 Jacobian,只需要它乘上一個方向向量,這叫 Jacobian-vector product(JVP)。Forward-mode 自動微分沿計算傳遞這個方向,通常只需原函數運算的常數倍成本,不必為每個輸入座標各跑一次;實際比例依運算與實作而定。把時間方向設成 1,便會一起算入 ∂tf\partial_t f:

    import torch
    from torch.func import jvp
    def T(x, t):                                   # 溫度場:x=300 附近有一塊暖區,整體每秒升溫 0.0005 °C
        return 20 + 0.3*torch.exp(-((x-300)/100)**2) + 0.0005*t
    x0, t0, xdot = torch.tensor(250.), torch.tensor(0.), torch.tensor(0.5)
    reading, dg_dt = jvp(T, (x0, t0), (xdot, torch.tensor(1.)))       # 全導數 ∂_t T + ∂_x T · ẋ,一次算完
    _, dT_dt_fixed = jvp(T, (x0, t0), (torch.tensor(0.), torch.tensor(1.)))   # 釘住位置:只剩 ∂_t T
    print(f"讀數 {reading:.3f}  全導數 {dg_dt:.6f}  釘住 {dT_dt_fixed:.6f}  漂移項 {dg_dt-dT_dt_fixed:.6f}")

    jvp(f, primals, tangents) 回傳 (f(primals), J·tangents):第一個 tangent 是 x˙\dot x、第二個是 dt/dt=1dt/dt=1,回傳的第二項就是全導數。上面這個例子會印出全導數 0.0016680.001668、釘住 0.00050.0005、漂移項 0.0011680.001168(手算:∂xT(250)=0.3 e−0.25×0.01≈0.00234\partial_x T(250)=0.3\,e^{-0.25}\times0.01\approx0.00234 °C/m,乘 x˙=0.5\dot x=0.5 得 0.001170.00117)。把 x0 換成一個幾千維的向量、T 換成一個網路,程式一個字都不用改。

    檢查理解

    想一想

    一台空拍機邊飛邊量 PM2.5,讀數在三分鐘內從 20 升到 35。要判斷「這個城市的空氣正在變差」,你需要哪個工具?

    想一想

    下面哪一個情況會讓 ddtT(x(t),t)=∂tT+∇T⋅x˙\frac{d}{dt}T(x(t),t)=\partial_t T+\nabla T\cdot\dot x 在某一刻不成立?

    想一想

    m(t)=∫0LT(x,t) dxm(t)=\int_0^{L}T(x,t)\,dx 是固定河段上溫度的積分。dmdt=∫0L∂tT dx\frac{dm}{dt}=\int_0^L\partial_t T\,dx 這一步用到的是:

    參考文獻

    1. Strang, G. Calculus. Wellesley-Cambridge Press;MIT OpenCourseWare RES.18-001 免費全文。(多變數 chain rule、微分與積分交換順序的章節。)
    2. Baydin, A. G., Pearlmutter, B. A., Radul, A. A., Siskind, J. M. Automatic Differentiation in Machine Learning: a Survey. JMLR 2018.(forward mode 與 JVP、reverse mode 與 VJP 的成本比較。)
    3. PyTorch documentation, torch.func.jvp.(本篇程式碼的 API。)