← 回首頁

反向模式自動微分 AutoDiff(計算圖前向求值 → 逆拓撲序 adjoint 累加)

想像你在調一鍋湯,裡面幾十種調味料,你想知道每種料各自讓湯變鹹變淡多少——笨辦法是每種都多加一點、 重嚐一次,料愈多要嚐愈多次。反向模式自動微分(backprop)換個聰明作法:先照食譜把湯煮好一次, 再從最後的味道一路往回追,一趟就把每種料的影響全部算出來——這正是 AI 模型訓練時,用來一次調好上萬個 參數的核心手法。下面你會看到它怎麼在一張計算圖上先前向算出每個值、再反向逐格回推梯度

反向模式自動微分(Reverse-mode Automatic Differentiation)把一個複合函式拆成一張計算圖 (有向無環圖,DAG),分兩階段求梯度:前向傳值依拓撲序逐節點代入求出每個中間變數的值; 反向傳梯度依逆拓撲序逐節點把 adjoint(v̄=∂f/∂v,輸出對這個節點的偏導)依鏈式法則推送給它的 直接上游節點。本頁用 Baydin et al.(2018)論文的經典教學例 f(x1,x2) = ln(x1) + x1·x2 − sin(x2) 逐幀展示這個過程,並在終幀把 AD 反向傳播結果、 解析偏導(獨立算)、中央差分數值梯度三方對照,驗證彼此一致。

為什麼 AI 時代重要

反向模式自動微分(backprop)是所有深度學習模型訓練的本質:前向傳一次值、反向傳一次梯度,就能同時 得到所有輸入變數(神經網路裡就是全部參數)的梯度,不需要對每個參數各別重跑一次微分;PyTorch 的 autograd、TensorFlow 的 GradientTape 等自動微分框架,底層做的正是本頁示範的「計算圖 + 拓撲序前向 求值 + 逆拓撲序 adjoint 累加」——只是真實模型的計算圖有百萬甚至十億個節點,本頁縮小到 7 個節點, 原理完全相同。

它是拓撲排序與 DAG 上 DP 的應用

計算圖是一張有向無環圖(DAG):前向傳值要求每個節點在它所有上游都求值完成後才能求值,這正是 圖遍歷(拓撲排序建立在 BFS/DFS 之上)要處理的順序 問題;反向傳梯度則是這張 DAG 上的動態規劃——每個節點的 adjoint 由「所有下游節點的 adjoint × 局部 導數」加總而成,狀態轉移的思想與 動態規劃基礎頁的 dp[i] = f(dp[i-1], dp[i-2], ...) 完全一致,差別只在這裡的「狀態」是梯度、轉移方向是 逆拓撲序而非正向遞推。

AutoDiff(前向傳值 → 反向 adjoint 累加 → 三方梯度對照)

計算圖固定 7 節點 8 邊:v1=x1,v2=x2,v3=ln(v1),v4=v1·v2,v5=sin(v2),v6=v3+v4, v7=v6−v5(=f),v1..v7 的建構序即為此圖的一組合法拓撲序。前向階段依此順序逐節點求值; 反向階段依逆序(v7→v6→v5→v4→v3)逐節點把已算好的 adjoint 推送給直接上游,上游節點 adjoint 以 += 累加一筆貢獻——v1 同時是 v3、v4 的直接上游,v2 同時是 v4、v5 的直接上游, 各自有兩條通往輸出的路徑,故各自累加兩次,是 backprop 最核心也最容易被忽略的地方。

固定資料:f(x1,x2) = ln(x1) + x1·x2 − sin(x2);x1=2,x2=5(Baydin et al. 2018 經典教學例); 中央差分步長 h=1e-6。
已執行操作(ops):0 / 13 節點標籤目前語意:
階段:初始化
計算圖:節點內數字為節點標籤(v1..v7),節點上方數字依目前階段顯示前向值 (init/forward 階段)或 adjoint(backward-seed/backward/done 階段)——? 代表該節點在目前階段尚未被算出對應數值,不是預先填好答案,而是誠實反映「目前狀態」。 橘色(current)=本幀正在處理的節點;藍色外框(frontier)=前向已知輸入節點,或反向本幀 正在推送貢獻的來源節點;紫色(visited)=前向已求值的節點集合,或反向已推送完自己全部 貢獻的來源節點集合;綠色(finalized)=終幀三方梯度對照已對整張圖定案。
誠實揭露 1——教學縮尺:本頁計算圖僅 7 個節點,純粹是為了讓每一步 adjoint 累加都能 逐幀看清楚;真實神經網路的計算圖可達百萬甚至十億個節點,前向拓撲序求值 + 反向逆拓撲序推送 adjoint 貢獻的原理完全相同,只是規模無法逐幀視覺化。
誠實揭露 2——中央差分僅驗證用、非生產:下方三方對照表的中央差分數值梯度 (f(x+h)−f(x−h))/2h 只用來驗證 AD 反向傳播的結果是否正確;它對每個輸入變數各需 重新跑兩次完整前向傳值,n 個參數就要 2n 次前向,百萬參數規模下完全不可行——這正是 AD 的價值: 一次前向 + 一次反向就能同時拿到所有輸入變數的梯度,數值差分在真正的模型訓練中不會被使用。
誠實揭露 3——v1/v2 雙路徑累加是 backprop 核心:v1 是 v3=ln(v1) 與 v4=v1·v2 的共同 上游,在計算圖中有 v1→v3→v6→v7 與 v1→v4→v6→v7 兩條通往輸出的路徑;v2 同理有兩條路徑。反向 傳播依逆拓撲序逐節點推送,v1 的 adjoint 先由處理 v4 時推送一次(v̄1=5.0),再由處理 v3 時推送 一次(累加後 v̄1=5.5)——下方 adjoint 表與 opLog 逐幀分列這兩筆貢獻,刻意不合併成一步,凸顯 「adjoint = 所有下游貢獻加總」而非「只走一條路徑」。

前向值表(forwardVals,v1..v7)

adjoint 表(v̄1..v̄7,v̄=∂f/∂v,Σ 下游貢獻累加)

三方梯度對照(AD 反向傳播 / 解析偏導 / 中央差分,僅終幀齊備)

方法∂f/∂x1∂f/∂x2

已執行操作紀錄(opLog)

(空)