天天看點

BSC貝葉斯-MCMC

library(mvtnorm)
set.seed(123)
           

主要函數

LogPosterior (N(0,I) , X 2 ( 10 ) X^2(10) X2(10) )

LogPosterior <- function(beta, sigma2, y, X){
  p <- length(beta)
  if (sigma2<=0){
    return(NA)
  }
  else{
    ## The log likelihood function 取對數log,加和sum
    LogLikelihood <- sum(dnorm(x = y, mean = X%*%beta, 
                               sd = sqrt(sigma2), log = TRUE))
    
    ## The priors -beta-機率密度
    LogPrior4beta <- dmvnorm(x = t(beta), mean = matrix(0, 1, p),
                             sigma = diag(p), log = TRUE)
    # dmvnorm計算多元正态密度函數
    
    ## The priors -sigma2-機率密度
    LogPrior4sigma2 <- dchisq(x = sigma2, df = 10, log = TRUE)
    LogPrior <- LogPrior4beta + LogPrior4sigma2
    
    ## The log posterior
    LogPosterior <- LogLikelihood + LogPrior
    return(LogPosterior)
  }
}
           

這裡中間加了一個判斷,因為是随機抽出的sigma2(來自正太)有一定可能是負數,是以加了一個if和else判斷。否則會持續報錯NANANANANANA。

Gibbs函數

Gibbs<-function(X,y,beta.ini,sigma2.ini,beta.step,sigma2.step,nIter){
  ## 儲存sample
  p <- ncol(X) #次元
  beta <- matrix(NA, nIter, p)
  sigma2 <- matrix(NA, nIter, 1)
  acc <- matrix(NA, nIter, 2)
  #接受機率,2列分别是beta和sigma2
  beta[1, ] <- beta.ini
  sigma2[1, ] <- sigma2.ini
  
  for(i in 2:nIter){
    beta.current <- beta[i-1, ]
    sigma2.current <- sigma2[i-1,]
    
    ## The Metropolis Algorithm For beta
    beta.prop <- rmvnorm(n = 1,
      mean = matrix(beta.current, 1),
      sigma = diag(p)*beta.step)# FV
    #beta.prop <- rmvnorm(n = 1, mean = matrix(beta.current, 1), sigma = diag(p)) 
    
    logPosterior.beta.prop <- LogPosterior(
      beta = t(beta.prop),
      sigma2 = sigma2.current,
      y = y, X = X)
    
    logPosterior.beta.current <- LogPosterior(
      beta = beta.current,
      sigma2 = sigma2.current,
      y = y, X = X)
    
    logratio <- (logPosterior.beta.prop -
                   logPosterior.beta.current)
    
    acc.prob.beta <- min(exp(logratio), 1) #做差取指數,就是機率之比
    
    if(!is.na(acc.prob.beta) & runif(1) < acc.prob.beta)
    {
      beta.current <- beta.prop
    }
    acc[i, 1] <- acc.prob.beta
    beta[i, ] <- beta.current
    
    
    ## The Metropolis Algorithm For sigma2
    # sigma2.prop <- runif(1,0,2*sigma2.current)
    sigma2.prop <- rnorm(1, sigma2.current,sigma2.step) 
    
    logPosterior.sigma2.prop <- LogPosterior(
      beta = matrix(beta.current),
      sigma2 = sigma2.prop,
      y = y, X = X)
    
    logPosterior.sigma2.current <- LogPosterior(
      beta = matrix(beta.current),
      sigma2 = sigma2.current,
      y = y, X = X)
    
    logratio <- logPosterior.sigma2.prop -
      logPosterior.sigma2.current
    
    acc.prob.sigma2 <- min(exp(logratio), 1)
    
    if(!is.na(acc.prob.sigma2) & runif(1, 0, 1) < acc.prob.sigma2) # accept the proposal
    {
      sigma2.current <- sigma2.prop
    }
    
    ## Update the matrix
    acc[i, 2] <- acc.prob.sigma2
    sigma2[i, ] <- sigma2.current
  }
  return(list(beta=beta,acc=acc,sigma2=sigma2))
}
           

有關Gibbs:其中修改過sigma2.prop <- runif(1,0,2*sigma2.current),但是可能是因為沒有展現在old附近産生新樣本,多次都無法收斂,是以用正太随機更适宜。

數值模拟

# real: beta -2 3 -1 2  sigma2 1
beta <- matrix(c(-2, 3, -1, 2))
X <- cbind(1, matrix(runif(1000*3), 1000))
y <- X%*%beta + rnorm(1000,0,1)
ou<-Gibbs(X,y,beta.ini = c(0,2,2,1),sigma2.ini = 0.5,
          beta.step = 0.01,sigma2.step=0.1,nIter = 10000)
# View(ou$beta)
plot(ou$beta[, 1], type = "l", lwd = 2, col = 6, ylim = c(-4, 10))
lines(ou$beta[, 2], type = "l", lwd = 2, col = 2)
lines(ou$beta[, 3], type = "l", lwd = 2, col = 3)
lines(ou$beta[, 4], type = "l", lwd = 2, col = 5)
abline(h = -2, lwd = 2, col = 1, lty = 3)
abline(h = 3, lwd = 2, col = 1, lty = 3)
abline(h = -1, lwd = 2, col = 1, lty = 3)
abline(h = 2, lwd = 2, col = 1, lty = 3)
lines(ou$sigma2, type = "l", lwd = 2, col ="yellow2", ylim = c(-4, 10))
abline(h=1,lty = 3)
legend("topright",c("beta1","beta2","beta3","beta4","sigma2","real")
       ,col = c(6,2,3,5,"yellow2",1),lty = c(1,1,1,1,1,3)
       ,ncol=2)
           

數值模拟的真值為beta = -2 3 -1 2 sigma2=1,可以看出結果不錯。千步以後就收斂了。

中間2個關鍵的參數beta.step = 0.01,sigma2.step=0.1,是通過反複試驗得到的,最開始設定過大,一直不接受,始終在初始值。

Bodyfat

OLS結果

# bodyfat
data<-read.csv("Bodyfat.txt")
OLS.bodyfat<-lm(bodyfat~Density,data=data)
beta.ols=OLS.bodyfat$coefficients
sigma2.ols=summary(OLS.bodyfat)$sigma**2
           

先是做了OLS,發現Density的t檢驗通過,是以選擇了二者進行回歸。

beta.ols和sigma2.ols在後續調整先驗的時候用到。

修改先驗

## 修改先驗函數
LogPosterior <- function(beta, sigma2, y, X){
  p <- length(beta)
  if (sigma2<=0){
    return(NA)
  }
  else{
    ## The log likelihood function 取對數log,加和sum
    LogLikelihood <- sum(dnorm(x = y, mean = X%*%beta, 
                               sd = sqrt(sigma2), log = TRUE))
    
    ## The priors -beta-機率密度
    LogPrior4beta <- dmvnorm(x = t(beta), mean =matrix(beta.ols,1,2),
                             sigma = diag(p), log = TRUE)
    # dmvnorm計算多元正态密度函數
    
    ## The priors -sigma2-機率密度
    LogPrior4sigma2 <- dchisq(x = sigma2, df = sigma2.ols, log = TRUE)
    LogPrior <- LogPrior4beta + LogPrior4sigma2
    
    ## The log posterior
    LogPosterior <- LogLikelihood + LogPrior
    return(LogPosterior)
  }
}
           

LogPrior4beta和LogPrior4sigma2進行了修改。均值改為OLS的beta,sigma2的自由度也改為了sigma2.ols

Bodyfat模拟

X <- cbind(1, matrix(data$Density))
y <- as.matrix(data$bodyfat)
ou<-Gibbs(X,y,beta.ini = c(0,2),sigma2.ini = 0.5,beta.step = 100,sigma2.step =100,nIter = 10000)
           
# View(ou$beta)
plot(ou$beta[, 1], type = "l", lwd = 2, col = 6,ylim = c(-500,500))
lines(ou$beta[, 2], type = "l", lwd = 2, col = 2)
abline(h = 477.650, lwd = 2, col = 1, lty = 3)
abline(h =-434.360, lwd = 2, col = 1, lty = 3)
lines(ou$sigma2, type = "l", lwd = 2, col ="yellow2", ylim = c(-4, 10))
abline(h=1.707688,lty = 3)
legend("topright",c("beta1","beta2","sigma2","real")
       ,col = c(6,2,"yellow2",1),lty = c(1,1,1,3)
       ,ncol=2)
           

模型的 β \beta β均收斂至真實值(虛線)。sigma2有一段大幅跳躍。

plot(data$Density,data$bodyfat)
for (i in 500:10000){
    abline(ou$beta[i,1],ou$beta[i,2],col=3,lwd=0.1)
}
abline(OLS.bodyfat$coefficients[1],OLS.bodyfat$coefficients[2],col=2)
legend("topright",c("樣本點","Gibbs","OLS"),col=c("black",3,2),pch=c(1,NA,NA),lty=c(NA,1,1))
           

值得注意的是我去除了前1000個Gibbs的sample,上圖可以看到OLS和Gibbs重合效果很好。

反思和改進

  1. 繪圖的時候應該去掉前面的樣本,噪聲
  2. 先驗和target越近越好,是以bodyfat這個資料集要修改先驗
  3. beta.step 和 sigma2.step 要多吃嘗試,過大會波動劇烈,過小會很慢很慢。
  4. 高階圖例這樣畫 pch=c(1,NA,NA),lty=c(NA,1,1) 巧妙利用NA可以散點圖和折線圖疊加

繼續閱讀