Code
using CairoMakie
using Distributions
using MCMCChains
set_theme!(
=18,
fontsize=(xgridvisible=false, ygridvisible=false,
Axis=false, rightspinevisible=false),
topspinevisible )
We want to estimate the unknown parameters \(\mu\) and \(\sigma\) of a normal distribution. We will use the following model: \[ \begin{align} &y = \mu + \varepsilon \\ &\varepsilon \sim \mathcal{N}(0, \sigma) \\ \end{align} \]
and the following priors: \[ \begin{align} &\mu \sim \mathcal{N}(0, 5) \\ &\sigma \sim \mathcal{N}_+(0, 2) \end{align} \]
We will use the CairoMakie
package for plotting and the Distributions
package for generating data and calculating the likelihood. We will also use the MCMCChains
package for checking the convergence of the chains.
using CairoMakie
using Distributions
using MCMCChains
set_theme!(
=18,
fontsize=(xgridvisible=false, ygridvisible=false,
Axis=false, rightspinevisible=false),
topspinevisible )
We generate 500 data points from a normal distribution with the true parameters \(\mu = 5\) and \(\sigma = 2\):
= 5, 2
μ, σ = rand(Normal(μ, σ), 500)
y hist(y)
We start with random values for \(\mu\) and \(\sigma\) from the prior distributions. For each step, we either sample \(\mu\) or \(\sigma\) and keep the other parameter constant. The method is a special calse of the Metropolis-Hastings algorithm.
= 6
nchains = 5_000
nsamples = nsamples ÷ 2
burnin = zeros(2, nsamples, nchains)
θ
= Normal(0, 5)
μ_prior = truncated(Normal(0, 2); lower = 0)
σ_prior
= [0.5, 0.5]
proposals_sigma
for n in 1:nchains
:, 1, n] = [rand(μ_prior), rand(σ_prior)]
θ[= logpdf(μ_prior, θ[1, 1, n]) + logpdf(σ_prior, θ[2, 1, n])
logprior_init = sum(logpdf.(Normal(θ[1, 1, n], θ[2, 1, n]), y))
loglikelihood_init = logprior_init + loglikelihood_init
current_logposterior = θ[:, 1, n]
current_μ, current_σ
for i in 2:nsamples
if i % 2 == 0
# sample new μ
= rand(Normal(θ[1, i-1, n], proposals_sigma[1]))
current_μ = θ[2, i-1, n]
current_σ else
# sample new σ
= θ[1, i-1, n]
current_μ = rand(Normal(θ[2, i-1, n], proposals_sigma[2]))
current_σ end
# prior
= logpdf(μ_prior, current_μ) + logpdf(σ_prior, current_σ)
logprior if logprior == -Inf
:, i, n] = θ[:, i-1, n]
θ[continue
end
# likelihood
= sum(logpdf.(Normal(current_μ, current_σ), y))
loglikelihood
# posterior
= logprior + loglikelihood
logposterior
= logposterior - current_logposterior
r if log(rand()) < r
:, i, n] = [current_μ, current_σ]
θ[= logposterior
current_logposterior else
:, i, n] = θ[:, i-1, n]
θ[end
end
end
let
= Figure(; size = (1200, 600))
fig Axis(fig[1, 1]; ylabel = "μ", title = "mcmc trace")
for n in 1:nchains
lines!(burnin:nsamples, θ[1, burnin:end, n])
end
Axis(fig[2, 1]; ylabel = "σ")
for n in 1:nchains
lines!(burnin:nsamples, θ[2, burnin:end, n])
end
Axis(fig[1, 2]; title = "posterior density")
density!(vec(θ[1, burnin:end, :]))
Axis(fig[2, 2];)
density!(vec(θ[2, burnin:end, :]))
Axis(fig[1, 3]; title = "posterior vs prior")
density!(vec(θ[1, burnin:end, :]))
plot!(μ_prior; color = :red)
Axis(fig[2, 3];)
density!(vec(θ[2, burnin:end, :]))
plot!(σ_prior; color = :red)
figend
Chains(permutedims(θ[:, burnin:end, :], (2, 1, 3)), [:μ, :σ])
Chains MCMC chain (2501×2×6 Array{Float64, 3}):
Iterations = 1:1:2501
Number of chains = 6
Samples per chain = 2501
parameters = μ, σ
Summary Statistics
parameters mean std mcse ess_bulk ess_tail rhat ⋯
Symbol Float64 Float64 Float64 Float64 Float64 Float64 ⋯
μ 4.8861 0.0933 0.0027 1220.3378 1074.1633 1.0182 ⋯
σ 2.0420 0.0650 0.0024 726.4000 874.8685 1.0115 ⋯
1 column omitted
Quantiles
parameters 2.5% 25.0% 50.0% 75.0% 97.5%
Symbol Float64 Float64 Float64 Float64 Float64
μ 4.7083 4.8223 4.8817 4.9501 5.0808
σ 1.9141 1.9951 2.0411 2.0863 2.1704
let
= Figure(; size = (900, 450))
fig Axis(fig[1, 1]; xlabel = "μ", ylabel = "σ", title = "with burn-in period")
for n in 1:nchains
scatterlines!(θ[1, :, n], θ[2, :, n])
end
Axis(fig[1, 2]; xlabel = "μ", ylabel = "σ", title = "burn-in removed")
for n in 1:nchains
scatter!(θ[1, burnin:end, n], θ[2, burnin:end, n];
= (Makie.wong_colors()[n], 0.2))
color end
figend