『ゼロからできるMCMC』 Chapter6 HMCをJuliaで
最終更新:2020/11/23
『ゼロからできるMCMC』 Chapter6 HMCのコードを, 練習を兼ねてJuliaで書いてみました. 詳細なことを書くと著作権的にだめな気もするので, 解説などはありません.
Julia Version 1.5.0
HMCを用いた一1次元正規分布からのサンプリング
ハミルトニアン ( ハイブリッド ) モンテカルロ ( HMC ) 法を用いて, 1次元正規分布からサンプルをとります.
Julia
using Plots
using Random:seed!
using Distributions
##
# 函数を定義: 作用を計算 ( 定義 ) する
function calc_action(x)
action = 0.5*x*x
end
# 函数を定義:ハミルトニアンHを計算する
function calc_h(x, p)
ham = calc_action(x)
ham = ham + 0.5*p*p
end
# 函数を定義:ハミルトニアンの微分dH/dxを計算する
function calc_dhdx(x)
delh = x
end
function hmc1d(;niter=10^5, nτ=40, dτ=1.0)
# ================= 準備 =================
seed!(12345)
x = 0.0 # 初期配位
naccept = 0 # 受理回数カウンター
res = zeros(niter+1) # サンプルを格納する
res[1] = x # 初期配位を格納する
# ========================================
for iSample = 1:niter # niter回サンプル
backup_x = x
p = randn() # 共役運動量のサンプル ( サボってライブラリを使う )
h_init = calc_h(x, p) # LF前のハミルトニアンを計算
# ============= Leap Frog ================
# 1st step
x += 0.5*p*dτ
# 2nd - nτ step
for iLF in 1:nτ
dhdx = calc_dhdx(x)
p -= dhdx*dτ
x += p*dτ
end
# last step
dhdx = calc_dhdx(x)
p -= dhdx*dτ
x += 0.5*p*dτ
# ========================================
h_fin = calc_h(x, p) # LF後のハミルトニアンを計算
# =========== Metropolis test ============
metropolis = rand()
exp(h_init-h_fin)>metropolis ? naccept+=1 : x = backup_x
# ========================================
res[iSample+1] = x
end
return res
end
# 結果
pyplot(fmt=:svg, size=(400, 250))
res = hmc1d();
histogram(res)
PyPlot.savefig("HMC_1.png", dpi=300)
多分大丈夫でしょう.

HMCを用いた3次元正規分布からのサンプリング
今度は, 3次元正規分布からサンプルをとります. 最初に\( A \)で3次元正規分布を指定しています.
普段は, 転置を表すのにx'を使っていますが, HTMLで表示するときおかしくなので,
以下ではtranspose(x)としています.
Julia
# 作用にでててくるAᵢⱼを定義
A = [1.0 1.0 1.0;
1.0 2.0 1.0;
1.0 1.0 2.0];
# 函数を定義:作用を計算 ( 定義 ) する
function calc_action(x, A)
action = 0.5*transpose(x)*A*x
end
# 函数を定義:ハミルトニアンHを計算する
function calc_h(x, A, p)
ham = calc_action(x, A)
ham = ham + 0.5*transpose(p)*p
end
# 函数を定義:ハミルトニアンの微分dH/dxを計算する
function calc_dhdx(x, A)
dhdx = A*x
end
function hmc2d(;niter=10^3, nτ=20, dτ=0.5, ndim=3)
# ================= 準備 =================
seed!(12345)
x = zeros(ndim) # 初期配位
naccept = 0 # 受理回数カウンター
res = zeros(niter+1, ndim) # サンプルを格納する
res[1, :] = x
# ========================================
for iSample = 1:niter # niter回サンプル
backup_x = x
p = randn(ndim) # 共役運動量のサンプル ( サボってライブラリを使う )
h_init = calc_h(x, A, p) # LF前のハミルトニアンを計算
# ============= Leap Frog ================
# 1st step
@. x = x + 0.5*p*dτ
# 2nd - nτ step
for iLF in 1:nτ
dhdx = calc_dhdx(x, A)
@. p = p - dhdx*dτ
@. x = x + p*dτ
end
# last step
dhdx = calc_dhdx(x, A)
@. p = p - dhdx*dτ
@. x = x + 0.5*p*dτ
# ========================================
h_fin = calc_h(x, A, p) # LF後のハミルトニアンを計算
# =========== Metropolis test ============
metropolis = rand()
exp(h_init-h_fin)>metropolis ? naccept+=1 : x = backup_x
# ========================================
res[iSample+1, :] = x
end
return res
end
# 結果
pyplot(fmt=:svg, size=(400, 400))
res = hmc2d();
scatter3d(res[:, 1], res[:, 2], res[:, 3])
PyPlot.savefig("HMC_2.png", dpi=300)
多分大丈夫でしょう.
