実は業務でもStan使い始めてるんですが、まだまだ単位根ありパネルデータの分析に回すなど低レベルなものが多く、無情報事前分布と階層事前分布を巧みに使いこなして華麗にサンプリング。。。なんて夢のまた夢という情けない状況です(泣)。
で、気が付いたら@berobero11さんのStan関連ブログ記事が超絶充実していて、久保先生もびっくりみたいな状況に。もはや僕が何かをだらだら書くのもアホらしいので、先にStanの使い方を覚えたいという方は是非@berobero11さんのブログから読んで下さい(笑)。僕はひたすらそちらの記事を(例えばinfer.NETあたりの例題を解きながら)トレースしていくだけのショボい記事をだらだら書いていこうと思ってます。
ということで、とりあえずStanマニュアルであるstan-reference-2.1.0.pdfを斜め読みして*1、ざっと僕が興味のある範囲でまとめただけのものを書き出してみます。Stanコード例は全てマニュアルからの引用です。
はじめに
基本的にStan(というかMC / MCMCサンプラー)は尤度計算をモンテカルロ法に基づいてやってくれるものです。なので、尤度計算さえ収束すれば良いというタイプの統計モデリングは事実上何でもStanでできるはずです。よって、ここに名前が挙がってないモデリング手法も、うまくコードを書いて実装できればStanで走らせることが可能だとも言えると思います。

モンテカルロ法による尤度計算のイメージはこんな感じ、というのは前回記事で紹介した通りです。で、大事なことはどんなモデリングにせよ「元のモデル式をきちんと『左辺=右辺(確率分布)』の形に直せる」ということ。例えば正規線形モデルであれば、
↓
↓
↓
y[i] ~ normal( alpha + beta * x[i], sigma )
というようにStanコードで表現できるところまで式変形できるということですね。慣れればパッと書けるようになると思いますが、複雑なモデリングを行いたい場合は最初にベタッと原理に従ったモデリング式を書き、それを式変形してStanコードに改めるというのが良さそうです。
多変量解析まわり
大体何でもできますが、Stanはコード文法がややこしかったり、サンプリングの仕方によって収束の度合いがまるっきり変わったりするので注意が必要です。分からない時は迷わず@berobero11さんあたりに聞きましょうw(何でも丸投げww)
正規線形モデル
まず超の字がつく基本中の基本として、正規線形モデル。これは正規分布normal(mu, sigma)で計算できます。
<例>
data {
int<lower=0> N;
vector[N] x;
vector[N] y;
}
parameters {
real alpha;
real beta;
real<lower=0> sigma;
}
model {
for (n in 1:N)
y[n] ~ normal(alpha + beta * x[n], sigma);
}
ロジスティック回帰・プロビットモデル
これはベルヌーイ分布bernoulli(theta)で計算できます。目的変数が二値でちゃんとintで与えておかないと普通にコンパイルエラーになるので要注意。
<例>
data {
int<lower=0> N;
real x[N];
int<lower=0,upper=1> y[N];
}
parameters {
real alpha;
real beta;
}
model {
for (n in 1:N)
y[n] ~ bernoulli(inv_logit(alpha + beta * x[n]));
}
多項ロジットモデル
二値ロジットモデルができるなら、当然多項ロジットもできます。これは多項分布categorical(theta)で実現可能です。ちなみに順序ロジスティック回帰はそのまんまのordered_logistic(eta,c)でできます。
<例>
data {
int K;
int N;
int D;
int y[N];
vector[D] x[N];
}
parameters {
matrix[K,D] beta;
}
model {
for (k in 1:K)
for (d in 1:D)
beta[k,d] ~ normal(0,5);
for (n in 1:N)
y[n] ~ categorical(softmax(beta * x[n]));
}
その他
階層ロジスティックモデルとかitem-response theory model(1PL-Rasch model / Multi-level 2PL model)とかも出てくるんですが、僕はよく分からないのでここでは割愛します。。。*2
計量時系列分析まわり
何でも勝手に確率分布をくっつけてモデリングできるというStanの特性を生かして、普通に計量時系列モデルのパラメータ推定を行うことができます。「Rで計量時系列分析」シリーズ記事で見てきたように、これまた最尤法で推定しているのでもちろんStanでバッチリやれるというわけです。
ARモデル
タイムラグの分だけ差分を取って、正規分布normal(mu,sigma)でモデリングすればOKです。なお発展としてARCHモデルも推定できますが、GARCHモデルの説明が詳しいのでここでは割愛。
<例:AR(1)モデル> data { int<lower=0> N; real y[N]; } parameters { real alpha; real beta; real sigma; } model { for (n in 2:N) y[n] ~ normal(alpha + beta*y[n-1], sigma); } <例:AR(K)モデル> data { int<lower=0> K; int<lower=0> N; real y[N]; } parameters { real alpha; real beta[K]; real sigma; } model { for (n in (K+1):N) { real mu; mu <- alpha; for (k in 1:K) mu <- mu + beta[k] * y[n-k]; y[n] ~ normal(mu, sigma); } }
GARCHモデル
実はARCH(1)モデルの説明に使われているパラメータ群の説明があるので要注意w
<例:GARCH(1,1)モデル> data { int<lower=0> T; real r[T]; real<lower=0> sigma1; } parameters { real mu; real<lower=0> alpha0; real<lower=0,upper=1> alpha1; real<lower=0,upper=(1-alpha1)> beta1; } transformed parameters { real<lower=0> sigma[T]; sigma[1] <- sigma1; for (t in 2:T) sigma[t] <- sqrt(alpha0 + alpha1 * pow(r[t-1] - mu, 2) + beta1 * pow(sigma[t-1], 2)); } model { r ~ normal(mu,sigma); }
MAモデル
MA(Q)モデルはベクトル表現を使って以下のように書けます。
<例:MA(Q)モデル>
data {
int<lower=0> Q; // num previous noise terms
int<lower=3> T; // num observations
vector[T] y; // observation at time t
}
parameters {
real mu; // mean
real<lower=0> sigma; // error scale
vector[Q] theta; // error coeff, lag -t
}
transformed parameters {
vector[T] epsilon; // error term at time t
for (t in 1:T) {
epsilon[t] <- y[t] - mu;
for (q in 1:min(t-1,Q))
epsilon[t] <- epsilon[t] - theta[q] * epsilon[t - q];
}
}
model {
vector[T] eta;
mu ~ cauchy(0,2.5);
theta ~ cauchy(0,2.5);
sigma ~ cauchy(0,2.5);
for (t in 1:T) {
eta[t] <- mu;
for (q in 1:min(t-1,Q))
eta[t] <- eta[t] + theta[q] * epsilon[t - q];
}
y ~ normal(eta,sigma);
}
ARMAモデル
ここではARMA(1,1)モデルの例だけ引用しておきます。
<例:ARMA(1,1)モデル> data { int<lower=1> T; // num observations real y[T]; // observed outputs } parameters { real mu; // mean coeff real phi; // autoregression coeff real theta; // moving avg coeff real<lower=0> sigma; // noise scale } model { vector[T] nu; // prediction for time t vector[T] err; // error for time t nu[1] <- mu + phi * mu; // assume err[0] == 0 err[1] <- y[1] - nu[1]; for (t in 2:T) { nu[t] <- mu + phi * y[t-1] + theta * err[t-1]; err[t] <- y[t] - nu[t]; } mu ~ normal(0,10); // priors phi ~ normal(0,2); theta ~ normal(0,2); sigma ~ cauchy(0,5); err ~ normal(0,sigma); // likelihood }
測定誤差とメタアナリシス
この辺僕はまーーーーーーったく詳しくないので全て割愛(ごめんなさい)。とは言え、測定誤差のモデリングもStanを使えば自由自在にできるので、例にも挙がっているようにメタアナリシスなどで測定誤差同士を統合して評価したいケースなんかでは物凄く有用な気がします。
クラスタリングまわり
わざわざ多項分布categorical(theta)が実装されているのを見れば分かる通り、ベイジアンの流儀でクラスタリングを実装することもできます。
K-means
ごくごく普通のユークリッド距離を使ったバージョンがマニュアルに載っています。
<例:"Soft" K-means> data { int<lower=0> N; // number of data points int<lower=1> D; // number of dimensions int<lower=1> K; // number of clusters vector[D] y[N]; // observations } transformed data { real<upper=0> neg_log_K; neg_log_K <- -log(K); } parameters { vector[D] mu[K]; // cluster means } transformed parameters { real<upper=0> soft_z[N,K]; // log unnormalized clusters for (n in 1:N) for (k in 1:K) soft_z[n,k] <- neg_log_K - 0.5 * dot_self(mu[k] - y[n]); } model { // prior for (k in 1:K) mu[k] ~ normal(0,1); // likelihood for (n in 1:N) increment_log_prob(log_sum_exp(soft_z[n])); }
「ベイジアンで生成モデルやるのは難しいんだよコラ」
と、何故か1節まるまる使ってマニュアルで物凄く説教してます(笑)。詳しくはマニュアル本文を読んでもらいたいのですが、Non-IdentifiabilityとMultimodalityが元凶であるというコメントがされてます。ひとまずここは@berobero11さんにでもご解説を賜るとして*3、次にいきましょう。
混合モデル
実はマニュアルのトップに出てる例なんですが、普通に混合正規分布とかも多項分布categorial(theta)と正規分布normal(mu,sigma)との合わせ技でいけます。
<例:混合モデル>
data {
int<lower=1> K; // number of mixture components
int<lower=1> N; // number of data points
real y[N]; // observations
}
parameters {
simplex[K] theta; // mixing proportions
real mu[K]; // locations of mixture components
real<lower=0,upper=10> sigma[K]; // scales of mixture components
}
model {
real ps[K]; // temp for log component densities
for (k in 1:K) {
mu[k] ~ normal(0,10);
}
for (n in 1:N) {
for (k in 1:K) {
ps[k] <- log(theta[k])
+ normal_log(y[n],mu[k],sigma[k]);
}
increment_log_prob(log_sum_exp(ps));
}
}
Latent Dirichlet Allocation (LDA)
ということで色々制約はあるらしいんですが、事前分布にディリクレ分布dirichlet(alpha)を充てて、尤度計算を多項分布categorical(theta)でやれば、ズバリLatent Dirichlet Allocationを走らせることができます。
<例:LDA>
data {
int<lower=2> K; // num topics
int<lower=2> V; // num words
int<lower=1> M; // num docs
int<lower=1> N; // total word instances
int<lower=1,upper=V> w[N]; // word n
int<lower=1,upper=M> doc[N]; // doc ID for word n
vector<lower=0>[K] alpha; // topic prior
vector<lower=0>[V] beta; // word prior
}
parameters {
simplex[K] theta[M]; // topic dist for doc m
simplex[V] phi[K]; // word dist for topic k
}
model {
for (m in 1:M)
theta[m] ~ dirichlet(alpha); // prior
for (k in 1:K)
phi[k] ~ dirichlet(beta); // prior
for (n in 1:N) {
real gamma[K];
for (k in 1:K)
gamma[k] <- log(theta[doc[n],k]) + log(phi[k,w[n]]);
increment_log_prob(log_sum_exp(gamma)); // likelihood
}
}