In this blog post I hope to introduce you to the powerful and simple Metropolis-Hastings algorithm. This is a common algorithm for generating samples from a complicated distribution using Markov chain Monte Carlo, or MCMC.
By way of motivation, remember that Bayes’ theorem says that given a prior
We call
The algorithm
The Metropolis-Hastings algorithm is a powerful way of approximating a distribution using Markov chain Monte Carlo. All this method needs is an expression that’s proportional to the density you’re looking to sample from. This is usually just the numerator of your posterior density.
To present the algorithm, let’s start by assuming the distribution you want to sample from has density proportional to some function
- Choose some initial value
.θ0 - For
:i=1,…,N - Sample
fromθ∗i .q(x|θi−1) - Set
with probabilityθi=θ∗i Otherwise setα=min(f(θ∗i)q(θi−1|θ∗i)f(θi−1)q(θ∗i|θi−1),1). .θi=θi−1
- Sample
The intuition behind this algorithm is that
Often, you’ll want to use a normal distribution as your proposal distribution,
Examples
This shiny
app uses the following function to approximate a few different distributions. Play around with it. What happens when the start value is far from the mean? What does the density look like when the number of iterations is big? How does the location of the chain jump around? In big jumps or little jumps? This is the time to gain some intuition about how Markov chain Monte Carlo works.
mh_sampler <- function(dens, start = 0, nreps = 1000, prop_sd = 1, ...){
theta <- numeric(nreps)
theta[1] <- start
for (i in 2:nreps){
theta_star <- rnorm(1, mean = theta[i - 1], sd = prop_sd)
alpha = dens(theta_star, ...) / dens(theta[i - 1], ...)
if (runif(1) < alpha) theta[i] <- theta_star
else theta[i] <- theta[i - 1]
}
return(theta)
}
You can also sample from another distribution. For example, here we’re approximating samples from an gamma distribution. There are also some diagnostic plots that tell us how good the approximation is. The densities match up pretty well.
set.seed(20171124)
theta_sample <- tibble(theta = mh_sampler(dgamma, nreps = 1e4, start = 1, shape = 5, rate = 5))
ggplot(theta_sample, aes(x = theta)) +
geom_density(color = "red") +
stat_function(fun = dgamma, args = list(shape = 5, rate = 5)) +
theme_minimal()