忘記物理也要搞懂的 Hamiltonian Monte Carlo (HMC) 筆記


先說我物理什麼的都還給老師了, 只能用自己理解的方式, 筆記下 Hamiltonian dynamic.

 💡 如果連我都能懂, 相信大家都能理解 HMC 了

但還是建議先看 MCMC by Gibbs and Metropolis-Hasting Sampling, 因為這篇要說的 Hamiltonian Monte Carlo (HMC) 是 Metropolis-Hastings (MH) 方法的一種, 只是 proposal distribution 從 random walk 改成使用 Hamiltonian dynamics 來做, 因而變的非常有效率 (accept rate 很高), 且對於高維度資料採樣也很有效.

首先粗體字如 x,v,p 都是 column vector, 而非粗體字表 scalar, e.g. m,t

Hamiltonian dynamic


一物體在位置 x (這裡可想成是高度) 的重力位能 (potential energy) 為

U(x)=mgTx

其中 m 表質量, g 表重力加速度 (是個向量, 所以有方向性).
同時該物體, 其本身也存在動能 (kinetic energy) 且可表示為:

K(p)=pTp2m(=12mv2)p=mdxdt(=mv)

v 表速度 (是個向量, 所以有方向性), p 我們稱為動量 (momentum).
整個封閉系統 (沒有外界的其他能量介入) 的能量為:

H(x,p)=U(x)+K(p)=mgTx+pTp2m

根據能量守恆 (energy conservation), 不管時間 t 是什麼, 整個系統的能量 H(x,p) 都維持相同.
此時如果我們知道該物體的初始狀態 (x0,p0) 的話, 事實上可以知道任何時間 t 下的位置和動量 (x,p)
而這樣的關係可以由下面的 Hamiltonian equations 描述出來:

dxidt=Hpidpidt=Hxi

其中 i{1,..,d}, d 表空間的維度.

只要使用 p=mv, 速度 vx 對時間的微分, 以及
速度對時間的微分等於負加速度 g (座標系統定義為往上的座標是正的, 而重力加速度是向下的, 所以值為負)
就可以從 (5) 推導出 (6) 和 (7).
Hpi=K(p)pi=pijp2j2m=pim=mvim=vi=dxidtHxi=U(x)xi=mjgjxjxi=mgi=md(vi)dt=dmvidt=dpidt

所以如果已知時間 t 的位置 xi(t), 想預估 t+ε 的位置 xi(t+ε) 的話, 可以透過 (6) 的方式更新:

xi(t+ε)xi(t)+εdxi(t)dt=xi(t)+εK(p(t))pi=xi(t)+ε(p(t)Tp(t)/2m)pi=xi(t)+εpi(t)m

只要 ε 夠小的話, 就會夠接近.
同理 pi(t+ε) 也能用 (7) 估計出來. 總結為以下 update 方法 (令 m=1), 而這個方法稱為 Euler’s method:

xi(t+ε)=xi(t)+εpi(t)pi(t+ε)=pi(t)εU(x(t))xi

但致命的缺點是一旦時間長了, 估計就愈來愈不準了. 因此實作上會採用 Leapfrog method: pdf 介紹.
我們先看看兩種方法的精確度差異 (取自 DeepBayes 2019 Summer School Day 5, MCMC slides):

Leapfrog method 描述如下:

pi(t+ε/2)=pi(t)(ε/2)U(x(t))xixi(t+ε)=xi(t)+εpi(t+ε/2)pi(t+ε)=pi(t+ε/2)(ε/2)U(x(t+ε))xi

主要的想法是, 在 update xi(t+ε) 時 (式 (8)), 原來使用 pi(t) 來更新, 改成使用”更準的” pi(t+ε/2) 來更新, 如同式 (11). 然後 pi(t+ε) 分成兩次的 ε/2 steps 來更新.

抱歉沒有嚴謹的數學來證明 error 的 order.

注意到, 我們只需要 xU(x) 就能更新 (x,p)!
也就是說只要有 potential energy 的 gradient 就可以模擬 Hamiltonian dynamic!
這點很重要, 因為變成 sampling 方法後等同於這句話: 只要有 score function 就能採樣! 而 score function 怎麼估計, Score Matching 是個好方法.
Sample codes from (Markov Chain Monte-Carlo Solution.ipynb):

1
2
3
4
5
6
7
8
def _leapfrog(self, x, v):
self.energy = []
for _ in range(self.n_steps):
v -= 0.5 * self.eps * self.dist.grad_log_density(x)
x = x + self.eps * v
v -= 0.5 * self.eps * self.dist.grad_log_density(x)
self.energy.append(self._energy(x, v))
return x, v

彈簧例子


參考自 MCMC: Hamiltonian Monte Carlo (a.k.a. Hybrid Monte Carlo)
不知道怎麼來的也沒關係, 反正此時的系統能量為:

U(x)=x22K(p)=p22H(x,p)=U(x)+K(p)

位置 x 的參考原點定義在彈簧中心, 也就是剛好 potential energy 為 0 的時候. 使用 Leapfrog method 來模擬 Hamiltonian equations 更新狀態 (x,p):

可以看到 Hamiltonian dynamic 的過程, 能量上 (左下角的圖) 只是 potential 和 kinetic energy 的互相交換 (黃色和青色互相消長), 其總能量是不變的.
哎不對~總能量 H 的那條全黃色的 bar 沒有固定不動啊, 看起來還是會小幅度上上下下的.
是的, 縱使用了 Leapfrog method, 還是會漂移, 這是因為我們對時間離散化造成的. 想要 error 更小, 就必需切更細碎的時間去逼近.
另外需要特別說明, 右下角的 “Phase Space” 圖, 畫出了 (x,p) 狀態值的移動路徑. 由於能量守恆, 這路徑代表了相同的 Hamiltonian energy. 為什麼要特別說明這點, 在後面講 sampling 的時候會再次提到.

能量怎麼看成機率分佈? 或反過來?


給定能量 E(x), 總是可以用下式變成機率分佈 (Gibbs distribution):

p(x)=1ZeE(x)

Z 就是一個 normalization constant 變成機率分佈用的. 所以能量愈大表示的機率密度就愈小.
我們來將 Hamiltonian energy 變機率分佈看看:

p(x,p)=1ZeH(x,p)e(U(x)+K(p))=eU(x)eK(p)=p(x)p(p)

這裡可以發現 xp 互相獨立.
原本從物理那邊我們是先定義了能量, 再利用 Gibbs distribution 變成了機率分佈.
現在我們反過來操作: 先定義我們要的機率分佈, 然後反推能量長什麼樣.

🤔 題外話, 寫到這我就在想, 反過來從先定義機率分布再推導能量是不是仍然能滿足 energy conservation?
i.e. 能量隨時間不變.
dH(x,p)dt=0


但其實不用擔心, 因為只要用 Hamiltonian equations (6), (7) 去更新狀態, 就會滿足 energy conservation.
我們考慮 1-D case 即可, 能量為 H(x(t),p(t)), 滿足 (6) and (7) 並觀察
dH(x(t),p(t))dt=dHdxdxdt+dHdpdpdtby (6)=dHdxdHdp+dHdpdpdtby (7)=dHdxdHdpdHdpdHdx=0

因為互相獨立, 我們可以先定義 p 是 normal distribution N(p|0,I)

p(p)=1ZpepTp2

可以看得出來其 (kinetic) energy 為:

K(p)=pTp2

然後不要忘記我們的目的是要從一個目標分佈 p(x) 取樣, 因此 x 的機率分佈就直接定義成目標分佈. 而其 (potential) energy 為:

p(x)=1ZxeU(x)U(x)=logp(x)+const.

還記得我們之前說過這句話嗎? “我們只需要 xU(x) 就能更新 (x,p)!”
因此 (16) 的 const. 就不重要了, 所以 potential energy 這麼定義就可以了:

U(x)=logp(x)

MHC 採樣過程


好了, 到目前為止我們藉由設定好的 distribution p(x),N(p|0,I), 可以找到對應的 energy functions U(x),K(p).
那就可以套用 Hamiltonian equations (6), (7) 來模擬能量不變的情形下, (x,p) 隨時間的變化.
給定一個初始狀態 (x0,p0) 可以得到:

(x0,p0)(6)(7)(x1,p1)(6)(7)(x2,p2)(6)(7)...

其中 H(x0,p0)=H(x1,p1)=...
實作上由於每一次 (6), (7) 的更新都採用很小的 ε (Leapfrog sample codes 裡的 self.eps), 這樣才能確保夠準確.
但我們也希望能夠走遠一些 (這對 multi-modual 的 distribution 很有幫助, 如下圖所示), 所以會跑個 T 步 updates (Leapfrog sample codes 裡的 self.n_steps)

但就算如此, 由於 energy conservation (13) 的關係, x 只會在 phase space 上具有相同能量的 contour 上採樣.
(Phase space 定義為 (x,p) 的空間)
為了能夠採樣出其它的點, 我們需要換到其他能量的 contour, 因此改變 p, 即對它重新採樣即可.
(follow 之前定義好的分佈 N(p|0,I)).
但是別忘了, Hamiltonian MC 所提出的採樣是 Metorpolis-Hastings 的 proposal distribution. 所以也需要有 accept/reject, 但也得益於 energy conservation 所以會有非常高的 accept rate. 因而採樣效率很好.
總結一下 HMC 方法 [ref 3]:

而 Tuning HMC 有幾個要點 [ref 3]:

一般來說要控制 rejection rate 在 [1/4,3/4] 之間會比較好. 還有多跑幾個 threads 來確認收斂狀況.

與 Langevin Dynamics 的關係


先總結一下 Hamiltonian Monte Carlo (HMC) 是 Metropolis-Hastings (MH) 方法的一種, 只是 proposal distribution 從 random walk 改成使用 Hamiltonian dynamics 來做 (式 (6)(7) 的更新)
HMC 有一個特點是, 可以只用 xU(x) 就能對目標分佈 p(x) 取樣, 說精確一點是對 joint pdf p(x,p) 採樣, 我們透過 MH 的 accept/rejection 機制變成對 p(x) 取樣
實務上 Hamiltonian dynamics 的更新步驟使用 leapfrog method (式 (10)-(12)), 如果只用 single leapfrog step 其實就 reduce 成 Langevin Monte Carlo (LMC) [6], 而其更新步驟就稱為 Langevin Dynamics
擷取參考資料[6]最後一頁:

通常在 DDPM (Denoising Diffusion Probabilistic Models) or SMLD (如同本文開頭說的這兩個只是不同角度下看相同的問題) 裡面用 Langevin Dynamics 更新後, 直接省略 MH 的 accept/reject 機制.
重複一下上面投影片寫的 Langevin Dynamics:
Δx=δ22xlogp(x)+δp

其中 p 一樣從 N(p|0,I) 採樣
所以在 SMLD 中, 目標就是訓練出 score function NN sθ(x) 來取代 xlogp(x) 就可以用 score function 採樣了
最後 Langevin Dynamics 採樣還可以從另一種方式推導: 先假設粒子運動遵從 Langevin Dynamics 可以發現每個時間點粒子的機率分佈遵從 Fokker-Planck equation, 而當時間點無窮大時, Fokker-Planck equation 可以發現會有穩態分佈, 所以反向來說如果穩態分佈能設定成我們要採樣的目標分佈, 那一切就大功告成! (許久之前閱讀的, 如有錯誤歡迎包含並指證)
[DeepBayes2019] Day 5, Lecture 3. Langevin dynamics for sampling and global optimization [YouTube], 影片的解說真的非常棒, 可以一步一步跟著推導理解! 或直接參考 odie’s Whisper 的筆記.
MCMC, Metropolis Hastings, Hamiltonian Dynamic, Langevin Dynamics, Fokker-Planck equation, Score Maching and DDPM 這一系列龐大且精美的數學串連起來現在除了 LLM 之外, 另一種強大的 GenAI (e.g. Imagen, Midjourney, Stability AI, …). 讓我們一起念咒語: Diffusion model 萬歲, 萬萬歲

但老實說我快要被淹沒了… 救命啊… 咕嚕咕嚕


References

  1. 马尔可夫链蒙特卡洛算法 (二) HMC
  2. MCMC: Hamiltonian Monte Carlo (a.k.a. Hybrid Monte Carlo)
  3. DeepBayes 2019 Summer School Day 5, MCMC slides
  4. MHC sample codes from (https://github.com/bayesgroup/deepbayes-2019/blob/master/seminars/day5/Markov Chain Monte-Carlo Solution.ipynb)
  5. Leapfrog method: https://www2.atmos.umd.edu/~ekalnay/syllabi/AOSC614/NWP-CH03-2-2.pdf
  6. Hamiltonian Monte Carlo and Langevin Monte Carlo
  7. Denoising Diffusion Probabilistic Models
  8. Score-Based Generative Modeling through Stochastic Differential Equations
  9. [DeepBayes2019] Day 5, Lecture 3. Langevin dynamics for sampling and global optimization [YouTube]
  10. odie’s Whisper, Langevin Dynamics 抽樣方法