R语言stan概率编程

版权声明:本文为博主原创文章,未经博主允许不得转载。作为分享主义者(sharism),本人所有互联网发布的图文均采用知识共享署名 4.0 国际许可协议(https://creativecommons.org/licenses/by/4.0/)进行许可。转载请保留作者信息并注明作者Jie Qiao专栏:http://blog.csdn.net/a358463121。商业使用请联系作者。 https://blog.csdn.net/a358463121/article/details/70194082

介绍

数以千计的用户依靠stan在社会,生物和物理科学,工程和商业进行统计建模,数据分析和预测的工作。

用户在Stan概率编程语言中可以基于对数概率密度函数,得到:

完整的贝叶斯统计推理与MCMC抽样(NUTS,HMC)

近似贝叶斯推理与变分推断(variational inference)(ADVI)

优化最大惩罚似然估计(penalized maximum likelihood estimation)(L-BFGS)

stan的数学库提供了可微的概率函数和线性代数(C ++ autodiff),R包中还提供了基于表达式的线性建模,后验概率可视化和留一法交叉验证。

安装

stan为R语言提供了接口,使用的包就是rstan,它的安装教程请看这里:https://github.com/stan-dev/rstan/wiki/Installing-RStan-on-Windows#toolchain

贝叶斯方法

想要用好stan这种概率编程语言,就得先理解贝叶斯是一种什么样的方法。其实统计学界一直有两种,观点,一种称为频率学派,一种是贝叶斯学派。

按照我的理解,在贝叶斯看来,模型中的参数,都可以看作是一个随机变量,这其中包括你的数据x,还有你要预测的y,x的系数 β ,模型中的噪声等等,都是可以是随机变量,他们都是服从某个分布的,我们一般称参数的分布为先验分布,比如服从参数可以服从正态分布,gamma分布等等。

但在频率学家看来就不太一样,他们觉得模型里面的参数就是一个数,是一个确定的事件,只要你找到足够的样本你就可以得到这个数准确的值,它并不是什么随机变量。

而贝叶斯学派则会是这样想,你的样本如果很多,我可以说你有99.9999%等于这个数,但也有可能等于其他数的,只是概率比较小而已罢了,因此它是从一个概率分布的角度来看到这个问题。

扫描二维码关注公众号,回复: 3686243 查看本文章

觉得先验分布这个概念比较抽象的读者可以先看看我之前写的文章:带你理解beta分布

线性回归

现在我们用一个最小二乘回归来介绍stan的使用方法。

假设我们的模型为:

Y = α + X β + ϵ

其中 X R n × k , β R k × 1 , ϵ N ( 0 , σ 2 )

于是,我们可以得到y也是服从正态分布的, y i N ( x i β , σ 2 )

那么我们可以使用stan,定义这个线性回归模型:

data {  //这里是程序需要输入的参数
int<lower=0> n; // 样本量
int<lower=0> k; // X的维度
matrix[n, k] X; // 变量X,这是一个样本矩阵
vector[n] Y; // 输出变量Y
}
parameters { //这里是需要拟合的参数
real alpha; // 截距项
vector[k] beta; // 系数
real<lower=0> sigma; // 噪声的参数
}
model {
Y ~ normal(X * beta + alpha, sigma); // likelihood
}

程序里面的data就是我们模型需要输入的数据,我们可以用R语言使用一个list把这些数据传进去。另外在定义参数的时候还可以定义参数的取值范围,这可以用来初始化参数的值。我们将上面代码保存为一个叫做lm_demo.stan的文件中,然后,我们在R中随机产生一些数据来看看这个线性回归的效果。

library(rstan)
#如果你有多个CPU和足够的内存可以用以下两条语句开启并行计算
rstan_options(auto_write = TRUE) 
options(mc.cores = parallel::detectCores())
set.seed(0)
n=100
k=5
X<-matrix(runif(n*k,-1,1),nrow=n,ncol=k)
beta<-rnorm(k,0,4)
alpha<-rnorm(1,5,4)
Y<-alpha+X%*%beta+rnorm(n,0,4)
Y<-as.vector(Y)
data=list(n=n,k=k,X=X,Y=Y)
print(beta)
print(alpha)

在这里,我们的模型系数是:

> print(beta)
[1]  9.5809373 -0.9649906 -3.4383390  2.0016643  0.5542299
> print(alpha)
[1] 3.225041

于是我们使用rstan去拟合这个模型,

> fit1<-stan("lm_demo.stan",data=data)
> print(fit1)
Inference for Stan model: lm_demo.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

           mean se_mean   sd    2.5%     25%     50%     75%   97.5% n_eff Rhat
alpha      4.08    0.01 0.40    3.29    3.80    4.08    4.35    4.84  4000    1
beta[1]    9.92    0.01 0.66    8.59    9.48    9.92   10.37   11.21  4000    1
beta[2]   -1.45    0.01 0.80   -3.03   -1.97   -1.44   -0.91    0.08  4000    1
beta[3]   -3.66    0.01 0.69   -5.01   -4.14   -3.66   -3.20   -2.26  4000    1
beta[4]    2.08    0.01 0.73    0.68    1.59    2.08    2.56    3.53  4000    1
beta[5]    1.28    0.01 0.75   -0.20    0.79    1.27    1.77    2.76  4000    1
sigma      3.95    0.00 0.30    3.43    3.73    3.93    4.14    4.60  4000    1
lp__    -185.14    0.05 1.97 -189.84 -186.24 -184.79 -183.65 -182.36  1856    1

Samples were drawn using NUTS(diag_e) at Sat Apr 15 22:40:22 2017.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

里面的表格给出了拟合出来的参数的值。这里的结果提供了其均值方差等信息,我们可以大致确定这些参数的置信区间,这在现实里面也是非常有用的信息。

贝叶斯回归

从贝叶斯的角度来看待回归的问题,我们可以假设系数也服从正态分布,也就是 β N ( 0 , τ 2 ) 这么一个先验分布。

于是,我们只需简单修改一下模型,就得到了新的回归,这么一个回归应该可以看作是Ridge regression:

data {  //这里是程序需要输入的参数
int<lower=0> n; // 样本量
int<lower=0> k; // X的维度
matrix[n, k] X; // 变量X,这是一个样本矩阵
vector[n] Y; // 输出变量Y
}
parameters { //这里是需要拟合的参数
real alpha; // 截距项
vector[k] beta; // 系数
real<lower=0> sigma; // 噪声的参数
}
model {
beta~normal(0,4);
alpha~normal(5,4);
Y ~ normal(X * beta + alpha, sigma); // likelihood
}

它的结果如下:

> fit2<-stan("lm_bayesian_demo.stan",data=data)
> print(fit2)
Inference for Stan model: lm_bayesian_demo.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

           mean se_mean   sd    2.5%     25%     50%     75%   97.5% n_eff Rhat
alpha      4.08    0.01 0.40    3.29    3.81    4.08    4.35    4.85  4000    1
beta[1]    9.63    0.01 0.65    8.30    9.20    9.63   10.07   10.92  4000    1
beta[2]   -1.35    0.01 0.74   -2.77   -1.83   -1.36   -0.85    0.11  4000    1
beta[3]   -3.56    0.01 0.68   -4.87   -4.02   -3.55   -3.10   -2.22  4000    1
beta[4]    1.96    0.01 0.73    0.49    1.46    1.95    2.44    3.41  4000    1
beta[5]    1.24    0.01 0.74   -0.21    0.73    1.26    1.74    2.70  4000    1
sigma      3.95    0.00 0.29    3.44    3.75    3.93    4.14    4.59  4000    1
lp__    -188.74    0.05 1.88 -193.15 -189.80 -188.44 -187.34 -186.03  1633    1

Samples were drawn using NUTS(diag_e) at Sat Apr 15 22:40:26 2017.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

可以看到,beta的每一项都比没加先验分布的结果要更接近0。

将他们的它们拟合的参数可以画出来,看到它们的取值范围:

library(gridExtra)
plot1=plot(fit1)+labs(title="lm_demo")
plot2=plot(fit2)+labs(title="lm_bayesian_demo")
grid.arrange(plot1, plot2, ncol=2)

这里写图片描述

两个方法在这个数据上也没什么区别,

除此之外,stan还能定义各种各样的概率模型,比如说高斯过程,时间序列等等。

想进一步了解可以参考:

https://github.com/stan-dev/rstan/wiki/RStan-Getting-Started#how-to-use-rstan

http://mc-stan.org/documentation/

概率编程语言Stan介绍《Stan: A Probabilistic Programming Language》B Carpenter, A Gelman, M Hoffman (2015) [Columbia University]

作为分享主义者(sharism),本人所有互联网发布的图文均遵从CC版权,转载请保留作者信息并注明作者a358463121专栏:http://blog.csdn.net/a358463121,如果涉及源代码请注明GitHub地址:https://github.com/358463121/。商业使用请联系作者。

猜你喜欢

转载自blog.csdn.net/a358463121/article/details/70194082