Muestreador de Gibbs

El algoritmo de Metrópolis es muy general y se puede aplicar a una gran variedad de problemas. Sin embargo, afinar los parámetros de la distribución propuesta para que el algoritmo funcione correctamente puede ser complicado. Por otra parte, el muestredor de Gibbs no necesita de una distribución propuesta.

Para implementar un muestreador de Gibbs se necesita ser capaz de generar muestras de la distribución posterior condicional a cada uno de los parámetros individuales. Esto es, el muestreador de Gibbs permite generar muestras de la posterior:

\[{p}({θ}_{1},...,{θ}_{p}|{x})\]

siempre y cuando podamos generar valores de todas las distribuciones condicionales:

\[{p}({θ}_{k},|{θ}_{1},...,{θ}_{k−1},{θ}_{k+1},...,{θ}_{p},{x})\]

El proceso del muestreador de Gibbs es una caminata aleatoria a lo largo del espacio de parámetros. La caminata inicia en un punto arbitrario y en cada tiempo el siguiente paso depende únicamente de la posición actual. Por tanto el muestredor de Gibbs es un proceso cadena de Markov vía Monte Carlo. La diferencia entre Gibbs y Metrópolis radica en como se deciden los pasos.

Algoritmo del muestreador Gibbs

En cada punto de la caminata se selecciona uno de los componentes del vector de parámetros (típicamente se cicla en orden):

  • Supongamos que se selecciona el parámetro \({θ}_{k}\), entonces obtenemos un nuevo valor para este parámetro generando una simulación de la distribución condicional \({p}({θ}_{k},|{θ}_{1},...,{θ}_{k−1},{θ}_{k+1},...,{θ}_{p},{x})\)

El nuevo valor \({θ}_{k}\) junto con los valores que aun no cambian \({θ}_{1},...,{θ}_{k−1},{θ}_{k+1},...,{θ}_{p}\) constituyen la nueva posición en la caminata aleatoria.

Seleccionamos una nueva componente \(({θ}_{k+1})\) y repetimos el proceso.

El muestreador de Gibbs es útil cuando no podemos determinar de manera analítica la distribución conjunta y no se puede simular directamente de ella, pero si podemos determinar todas las distribuciones condicionales y simular de ellas.

Ejemplificaremos el muestreador de Gibbs con el ejemplo de las proporciones, a pesar de no ser necesario en este caso.

Comenzamos identificando las distribuciones condicionales posteriores para cada parámetro:

\[ \begin{align} {p}({θ}_{1}|{θ}_{2},{x})&=p({θ}_{1},{θ}_{2}|{x})/p({θ}_{2}|{x})\\ &=\frac{p({θ}_{1},{θ}_{2}|{x})}{{\int}{p({θ}_{1},{θ}_{2}|{x})}d{θ}_{1}} \end{align} \]

Usando iniciales beta(a1,b1) y beta(a2,b2), obtenemos:

\[ \begin{align} p({θ}_{1}|{θ}_{2},{x})&=\frac{beta({θ}_{1}|{z}_{1}+{a}_{1},{N}_{1}−{z}_{1}+{b}_{1})beta({θ}_{2}|{z}_{2}+{a}_{2},{N}_{2}−{z}_{2}+{b}_{2})}{{\int}{beta({θ}_{1}|{z}_{1}+{a}_{1},{N}_{1}−{z}_{1}+{b}_{1})beta({θ}_{2}|{z}_{2}+{a}_{2},{N}_{2}−{z}_{2}+{b}_{2})}d{θ}_{1}}\\ &=\frac{beta({θ}_{1}|{z}_{1}+{a}_{1},{N}_{1}−{z}_{1}+{b}_{1})beta({θ}_{2}|{z}_{2}+{a}_{2},{N}_{2}−{z}_{2}+{b}_{2})}{beta({θ}_{2}|{z}_{2}+{a}_{2},{N}_{2}−{z}_{2}+{b}_{2})}\\ &=beta({θ}_{1}|{z}_{1}+{a}_{1},{N}_{1}−{z}_{1}+{b}_{1}) \end{align} \]

Debido a que la posterior es el producto de dos distribuciones Beta independientes es claro que \(p({θ}_{1}|{θ}_{2},{x})=p({θ}_{1}|{x})\).

Una vez que determinamos las distribuciones condicionales, simplemente hay que encontrar una manera de obtener muestras de estas, en R podemos usar la función rbeta.

z_1 = 5
N_1 = 7
z_2 = 2
N_2 = 7
a_1 = a_2 = b_1 = b_2 =3

pasos <- 12000
camino <- matrix(0, nrow = pasos, ncol = 2) # vector que guardará las simulaciones
camino[1, 1] <- 0.1 # valor inicial
camino[1, 2] <- 0.1

# Generamos la caminata aleatoria
for (j in 2:pasos){
  if(j %% 2){
    camino[j, 1] <- rbeta(1, z_1 + a_1, N_1 - z_1 + b_1)
    camino[j, 2] <- camino[j - 1, 2]
  }
  else{
    camino[j, 2] <- rbeta(1, z_2 + a_2, N_2 - z_2 + b_2)
    camino[j, 1] <- camino[j - 1, 1]
  }
}

caminata <- data.frame(pasos = 1:pasos, theta_1 = camino[, 1], 
  theta_2 = camino[, 2])

library(ggplot2)

ggplot(caminata[1:2000, ], aes(x = theta_1, y = theta_2)) +
    geom_point(size = 0.8) +
    geom_path(alpha = 0.5) +
    scale_x_continuous(expression(theta[1]), limits = c(0, 1)) +
    scale_y_continuous(expression(theta[2]), limits = c(0, 1)) +
    coord_fixed()

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.2     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ lubridate 1.9.2     ✔ tibble    3.2.1
## ✔ purrr     1.0.1     ✔ tidyr     1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
caminata_g <- filter(caminata, pasos %% 2 == 0) %>%
  gather(parametro, val, theta_1, theta_2) %>%
  mutate(pasos = rep(1:6000, 2)) %>%
  arrange(pasos)

ggplot(caminata_g[1:2000, ], aes(x = pasos, y = val)) +
  geom_path(alpha = 0.3) +
  facet_wrap(~parametro, ncol = 1) +
  scale_y_continuous("", limits = c(0, 1))

Si comparamos los resultados del muestreador de Gibbs con los de Metrópolis notamos que las estimaciones son muy cercanas

caminata_m <- caminata %>%
  gather(parametro, val, theta_1, theta_2) %>%
  arrange(pasos)

ggplot(caminata_m[1:2000, ], aes (x = pasos, y = val)) +
  geom_path(alpha = 0.5) +
  facet_wrap(~parametro, ncol = 1) +
  scale_y_continuous("", limits = c(0, 1))

# Metropolis
caminata_m %>% 
  filter(pasos > 1000) %>% # eliminamos el calentamiento
  group_by(parametro) %>%
  summarise(
    media = mean(val),
    mediana = median(val),
    std = sd(val)
    )
## # A tibble: 2 × 4
##   parametro media mediana   std
##   <chr>     <dbl>   <dbl> <dbl>
## 1 theta_1   0.617   0.624 0.129
## 2 theta_2   0.385   0.381 0.128
# Gibbs
caminata_g %>% 
  filter(pasos > 1000) %>%
  group_by(parametro) %>%
  summarise(
    media = mean(val),
    mediana = median(val),
    std = sd(val)
    )
## # A tibble: 2 × 4
##   parametro media mediana   std
##   <chr>     <dbl>   <dbl> <dbl>
## 1 theta_1   0.617   0.623 0.130
## 2 theta_2   0.385   0.381 0.128

También podemos comparar los sesgos de las dos monedas, esta es una pregunta más interesante.

caminata <- caminata %>%
  mutate(dif = theta_1 - theta_2)

ggplot(caminata, aes(x = dif)) + 
  geom_histogram(fill = "gray") + 
  geom_vline(xintercept = 0, alpha = 0.8, color = "darkgreen")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

La principal ventaja del muestreador de Gibbs sobre el algoritmo de Metrópolis es que no hay necesidad de seleccionar una distribución propuesta y no hay que lidiar con lo ineficiente de rechazar valores. A cambio, debemos ser capaces de derivar las probabilidades condicionales de cada parámetro y de generar muestras de estas.

Ejemplos

Distribución Normal

Retomemos el caso de observaciones normales, supongamos que tengo una muestra \({x}_{1},...,{x}_{N}\) de observaciones independientes e identicamente distribuidas, con \({x}_{i}∼N({\mu},{\sigma}^{2})\), veremos el caso de media desconocida, varianza desconocida y de ambas desconocidas.

Normal con media desconocida. Supongamos que \({\sigma}^{2}\) es conocida, por lo que nuestro parámetro de interés es únicamente \({\mu}\) entonces si describo mi conocimiento inicial de \({\mu}\) a través de una distribución normal:

\[{\mu}{\sim}{N}(m,{\tau}^{2})\]

resulta en una distribución posterior:

\[ {\mu}|{x}∼{N}\left(\frac{σ2}{{\sigma}^{2}+N{\tau}^{2}}m+\frac{N{\sigma}^{2}}{{\sigma}^{2}+N{\tau}^{2}}\bar{x},\frac{{\sigma}^{2}{\tau}^{2}}{{\sigma}^{2}+N{\tau}^{2}}\right) \]

Normal con varianza desconocida. Supongamos que μ es conocida, por lo que nuestro parámetro de interés es únicamente \({\sigma}^{2}\). En este caso una distribución conveniente para describir nuestro conocimiento inicial es la distribución Gamma Inversa.

La distribución Gamma Inversa es una distribución continua con dos parámetros y que toma valores en los positivos. Como su nombre lo indica, esta distribución corresponde al recírpoco de una variable cuya distribución es Gamma, recordemos que si \({x}{\sim}{Gamma}(\alpha,\beta)\) entonces:

\[ p(x)=\frac{1}{{\beta}^{\alpha}{\Gamma}({\alpha})}{x}^{\alpha−1}{e}^{−\frac{x}{\beta}} \]

donde \(x>0\). Ahora si \(y\) es la variable aleatoria recírpoco de \(x\) entonces:

\[ p(y)=\frac{{\beta}^{\alpha}}{{\Gamma}({\alpha})}{y}^{-\alpha−1}{e}^{−\frac{\beta}{y}} \]

con media

\[ \frac{\beta}{\alpha-1} \]

y varianza

\[ \frac{{\beta}^{2}}{({\alpha-1})^{2}({\alpha-2})} \]

Debido a la relación entre las distribuciones Gamma y Gamma Inversa, podemos utilizar la función rgamma de R para generar valores con distribución gamma inversa.

x_gamma <- rgamma(2000, shape = 5, rate = 1)

x_igamma <- 1 / x_gamma


library(MCMCpack)
## Loading required package: coda
## Loading required package: MASS
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
## ##
## ## Markov Chain Monte Carlo Package (MCMCpack)
## ## Copyright (C) 2003-2023 Andrew D. Martin, Kevin M. Quinn, and Jong Hee Park
## ##
## ## Support provided by the U.S. National Science Foundation
## ## (Grants SES-0350646 and SES-0350613)
## ##
x_igamma <- data.frame(x_igamma)

ggplot(x_igamma, aes(x = x_igamma)) +
  geom_histogram(aes(y = ..density..), binwidth = 0.05, fill = "gray") + 
  stat_function(fun = dinvgamma, args = list(shape = 5, scale = 1), 
    color = "darkgreen")  
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Volviendo al problema de inferir acerca del parámetros \({\sigma}^{2}\), si resumimos nuestro conocimiento inicial a través de una distribución Gamma Inversa tenemos

\[ p({\sigma}^{2})=\frac{{\beta}^{\alpha}}{{\Gamma}({\alpha})}\frac{1}{{y}^{\alpha+1}}{e}^{−\frac{\beta}{{\sigma}^{2}}} \]

la verosimiltud:

\[ p({x}|{\mu},{\sigma}^{2})=\frac{1}{({2{\pi}{\sigma}^{2}})^{\frac{N}{2}}}exp\left(−\frac{1}{2{\sigma}^{2}}{\sum}_{j=1}^{N}({x}_{j}−{\mu})2\right) \]

y calculamos la posterior:

\[ p(σ2)∝p(x|{\mu},{\sigma}^{2})p({\sigma}^{2}) \]

obtenemos que \({{\sigma}^{2}|{x}}{\sim}{GI}\left[{\frac{N}{2}+{\alpha},{\beta}+\frac{1}{2}{\sum}({{x}_{i}−{\mu}})^{2}}\right]\)

.

Por tanto tenemos que la inicial Gamma con verosimilitud Normal es una familia conjugada.

Normal con media y varianza desconocidas

Sea \({\theta}=({\mu},{\sigma}^{2})\) especificamos la siguiente inicial para \(\theta\):

\[p({\theta})=N({\mu}|{m},{\tau}^{2}){IG}({\sigma}^{2}|{\alpha},{\beta})\]

suponemos hiperparámetros \(m\),\({\tau}^{2}\),\(\alpha\),\(\beta\) conocidos. Entonces, la distribución posterior es:

\[p({\theta}|{x})∝p(x|{\theta})p({\theta})=\frac{1}{{({\sigma}^{2}})^{\frac{N}{2}}}exp\left[−\frac{1}{2{\sigma}^{2}}{\sum}_{i=1}^{N}({{x}_{i}−{\mu}})^{2}\right]exp\left[−\frac{1}{2{\tau}^{2}}({{\mu}−{m}})^{2}\right]\frac{1}{({\sigma}^{2})^{{\alpha}+1}}exp\left(−\frac{{\beta}}{{\sigma}^{2}}\right)\]

en esta última distribución no reconocemos el núcleo de niguna distribución conocida (existe una distribución normal-gamma inversa) pero si nos concenteramos únicamente en los términos que involucran a \({\mu}\) tenemos:

\[exp\left\{−\frac{1}{2}\left[{\mu}^{2}\left(\frac{N}{{\sigma}^{2}}+\frac{1}{{\tau}^{2}}\right)−2{\mu}\left(\frac{{{\sum}_{i=1}^{n}}{{x}_{i}}}{σ2}+\frac{m}{{\tau}^{2}}\right)\right]\right\}\]

esta expresión depende de \({\mu}\) y \({\sigma}^{2}\), sin embargo condicional a \({\sigma}^{2}\) observamos el núcleo de una distribución normal,

\[{{\mu}|{\sigma}^{2},{x}}{\sim}N\left(\frac{{n}{\tau}^{2}}{{n}{\tau}^{2}+{\sigma}^{2}}\bar{x}+\frac{{\sigma}^{2}}{{N}{\tau}^{2}+{\sigma}^{2}}m,\frac{{\tau}^{2}{\sigma}^{2}}{{n}{\tau}^{2}+{\sigma}^{2}}\right)\]

Si nos fijamos únicamente en los tárminos que involucran a \({\sigma}^{2}\) tenemos:

\[\frac{1}{{({\sigma}^{2}})^{\frac{N}{2}+{\alpha}+{1}}}exp\left[−\frac{1}{{\sigma}^{2}}{{\sum}_{i=1}^{N}}{\frac{({{x}_{i}−{\mu}})^{2}}{2}}+{\beta}\right]\]

y tenemos

\[{{\sigma}^{2}|{\mu},{x}}{\sim}{GI}\left[{\frac{N}{2}+{\alpha},{\beta}+\frac{1}{2}{\sum}({{x}_{i}−{\mu}})^{2}}\right]\]

Obtenemos así las densidades condicionales completas \(p({{\mu}|{\sigma}^{2},{x}})\) y \(p({{\sigma}^{2}|{\mu},{x}})\) cuyas distribuciones conocemos y de las cuales podemos simular.

Implementaremos un muestreador de Gibbs.

Comenzamos definiendo las distrbuciones iniciales:

  • \({\mu}{\sim}N(1.5,16)\), esto es \(m=1.5\) y \({\tau}^{2}=16\)

  • \({\sigma}^{2}{\sim}GI(3,3)\) esto es \(\alpha=\beta=3\).

Ahora supongamos que observamos 20 realizaciones provenientes de la distribución de interés:

N <- 50
set.seed(122)
x <- rnorm(N, 2, 2) 
x
##  [1]  4.62140176  0.24829384  2.39904749  2.93190885 -1.60411353  4.89748691
##  [7]  2.59770769  2.72362329 -0.01388084  1.48600171  1.73574244  0.31673052
## [13]  2.54850449 -2.92518071 -2.30679198  4.31835150  3.37948021  3.76050265
## [19]  0.11325957  3.43814572  0.92434912  0.95470268 -0.10584378  2.20303449
## [25]  5.72700211  1.96078181 -0.15661507  2.34520855  3.06610819  5.90452895
## [31]  4.82270939  3.20273052  0.17200480  5.16085186  1.06016874  5.20367778
## [37]  2.74547989  2.06775571  2.20820677 -2.03674850  0.68398061 -1.21700489
## [43] -0.68818260  1.60665220  4.75132535  2.34663000  1.12144484 -1.19064581
## [49]  0.51144488  5.63041621

Escribimos el muestreador de Gibbs.

m <- 1.5; tau2 <- 16; alpha <- 3; beta <- 3

pasos <- 20000
camino <- matrix(0, nrow = pasos + 1, ncol = 2)
camino[1, 1] <- 0

# Generamos la caminata aleatoria
for (j in 2:(pasos + 1)){
  # sigma^2
  mu <- camino[j - 1, 1]
  a <- N / 2 + alpha
  b <- sum((x  - mu) ^ 2) / 2 + beta
  camino[j - 1, 2] <- 1/rgamma(1, shape = a, rate = b)
  
  # mu
  sigma2 <- camino[j - 1, 2]
  media <- (N * tau2 * mean(x) + sigma2 * m) / (N * tau2 + sigma2)
  var <- sigma2 * tau2 / (N * tau2 + sigma2)
  camino[j, 1] <- rnorm(1, media, sd = sqrt(var))
}

caminata <- data.frame(pasos = 1:pasos, mu = camino[1:pasos, 1], 
  sigma2 = camino[1:pasos, 2])

caminata_g <- caminata %>%
  gather(parametro, val, mu, sigma2) %>%
  arrange(pasos)

ggplot(filter(caminata_g, pasos > 15000), aes(x = pasos, y = val)) +
  geom_path(alpha = 0.3) +
  facet_wrap(~parametro, ncol = 1, scales = "free") +
  scale_y_continuous("")

ggplot(filter(caminata_g, pasos > 5000), aes(x = val)) +
  geom_histogram(fill = "gray") +
  facet_wrap(~parametro, ncol = 1, scales = "free") 
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Algunos resúmenes de la posterior:

caminata_g %>%
  filter(pasos > 1000) %>% # eliminamos la etapa de calentamiento
  group_by(parametro) %>%
  summarise(
    mean(val), 
    sd(val), 
    median(val)
    )
## # A tibble: 2 × 4
##   parametro `mean(val)` `sd(val)` `median(val)`
##   <chr>           <dbl>     <dbl>         <dbl>
## 1 mu               1.91     0.305          1.91
## 2 sigma2           4.74     0.933          4.62

Predicción. Para predecir el valor de una realización futura \(y\) recordemos que:

\[p(y)={\int}{p(y|\theta)p(\theta|x)}{d\theta}\]

Por tanto podemos aproximar la distribución predictiva posterior como:

caminata_f <- filter(caminata, pasos > 5000)

caminata_f$y_sims <- rnorm(1:nrow(caminata_f), caminata_f$mu, caminata_f$sigma2)

ggplot(caminata_f, aes(x = y_sims)) + 
  geom_histogram(fill = "gray") +
  geom_vline(aes(xintercept = mean(y_sims)), color = "darkgreen")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

En estadística bayesiana es común parametrizar la distribución Normal en términos de precisión (el inverso de la varianza). Si parametrizamos de esta manera \({\nu}=\frac{1}{{\sigma}^{2}}\) podemos repetir el proceso anterior con la diferencia de utilizar la distribución Gamma en lugar de Gamma inversa.

Ejercicios

  • Simular una variable aleatoria discreta que tiene distribución normal multivariada con parámetros:

\[ \begin{align} \boldsymbol{\mu}&=\begin{bmatrix} 14\\ 1\\ 4 \end{bmatrix} \end{align} \]

\[ \begin{align} \boldsymbol{\Sigma}&=\begin{bmatrix} 64 & -3 & 4\\ -3 & 81 & -4\\ 4 & -4 & 49 \end{bmatrix} \end{align} \]