「大人の教養・知識・気付き」を伸ばすブログ

一流の大人(ビジネスマン、政治家、リーダー…)として知っておきたい、教養・社会動向を意外なところから取り上げ学ぶことで“気付く力”を伸ばすブログです。データ分析・語学に力点を置いています。 →現在、コンサルタントの雛になるべく、少しずつ勉強中です(※2024年1月21日改訂)。

MENU

Juliaを使ってみる(17/22):ラプラス近似(その4)

今回やりたいこと

 勾配を用いた効率的な事後分布のシミュレーションを行う。

を参照する。

1. 勾配を用いる必要性

 対数事後分布の勾配を計算するのが今後のアプローチのコアである。

1.2 勾配を用いた計算の効率化

1.2.4 ロジスティック回帰
###########################################
### Laplace近似によるロジスティック回帰 ###
###########################################

using Distributions, PyPlot, ForwardDiff, LinearAlgebra

# n次元単位行列
eye(n) = Diagonal{Forward64}(I, n)

# パラメータ抽出用の関数
unzip(a) = map(x -> getfield(a, x), fieldnames(eltype(a)))

# グラフの諸設定を行う関数
function set_options(ax, xlabel, ylabel, title;
                      grid = true, gridy = false, legend = false)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_title(title)
    if grid
        if gridy
            ax.grid(axis = "y")
        else
            ax.grid()
        end
    end
    legend && ax.legend()
end

# 最適化ラッパー関数の定義
function gradient_method(f, x_init, η, maxiter)
    x_seq = Array{typeof(x_init[1]), 2}(undef,length(x_init), maxiter)
    g(x) = ForwardDiff.gradient(f, x)
    x_seq[:, 1] = x_init
    
    for i in 2:maxiter
        x_seq[:, i] = x_seq[:, i-1] + η*g(x_seq[:, i-1])
    end
    
    x_seq
end


function inference_wrapper_gd(log_joint, params, w₁_init, η, maxiter)
    ulp(w₁) = log_joint(w₁, params...)
    w₁_seq = gradient_method(ulp, w₁_init, η, maxiter)
    w₁_seq
end


### 観測値
X_obs = [-3,1,2]
Y_obs = Bool.([0,1,1])

sig(x) = 1/(1 + exp(-x))

# 事前分布の設定
μ₁ = 0
μ₂ = 0
σ₁ = 10.0
σ₂ = 10.0

# 対数同時分布
log_joint(w, X, Y, μ₁, σ₁, μ₂, σ₂) = 
    logpdf(Normal(μ₁,σ₁), w[1]) +
    logpdf(Normal(μ₂,σ₂), w[2]) +
    sum(logpdf.(Bernoulli.(sig.(w[1]*X_obs .+ w[2])), Y_obs))
params = (X_obs, Y_obs, μ₁, σ₁, μ₂, σ₂)

# 非正規化対数事後分布
ulp(w) = log_joint(w, params...)

### 最適化用パラメータ
w_init = [0.0, 0.0]
maxiter = 10000
η = 0.1

# 最適化の実施
w_seq = inference_wrapper_gd(log_joint, params, w_init, η, maxiter)

# 勾配法の過程を可視化
fig,axes = subplots(2, 1, figsize = (8,8))
axes[1].plot(w_seq[1,:])
set_options(axes[1], "iteration", "w₁", "w₁ sequence")
axes[2].plot(w_seq[2,:])
set_options(axes[2], "iteration", "w₂", "w₂ sequence")

tight_layout()

# 平均
μ_approx = w_seq[:,end]

# 共分散
hessian(w) = ForwardDiff.hessian(ulp, w)
Σ_approx = inv(-hessian(μ_approx))

w₁s = range(-10, 30, length = 1000)
w₂s = range(-20, 20, length = 1000)

fig, axes = subplots(1, 2, figsize = (8,4))
cs = axes[1].contour(w₁s, w₂s, [exp(ulp([w₁,w₂])) + eps() for w₁ in w₁s, w₂ in w₂s]', cmap = "jet")
axes[1].clabel(cs, inline = true)
set_options(axes[1], "w₁", "w₂", "unnormalised posterior")
cs = axes[2].contour(w₁s, w₂s, [pdf(MvNormal(μ_approx, Σ_approx), [w₁,w₂]) for w₁ in w₁s, w₂ in w₂s]', cmap = "jet")
axes[2].clabel(cs, inline = true)
set_options(axes[2], "w₁", "w₂", "approximate posterior")


このようにロジスティック回帰では、事後分布が多変量正規分布にならず、近似が上手くいかない。

プライバシーポリシー お問い合わせ