なお最新のpdfはこちらからダウンロードできます.
このドキュメントは統計モデリング言語であるStanのユーザーガイド兼リファレンスマニュアルです. この導入の章ではStanの全体像について紹介しますが, 残りの章ではモデルの実際のプログラミングや, Stanのモデリング言語としての詳細な解説を, コードやデータの型も含めて, 実践的な解説を行います.
最新のコード, 例, マニュアル, バグレポート, 機能追加の要望など, Stanに関する情報は下記のリンクにあるStanのホームページから参照できます.
Stan Projectでは3つのインターフェースをプロジェクトの一部としてサポートしています. モデリング部分やその使い方に関しては3つのインターフェースで共通していてるので, このマニュアルはその3つに共通するモデリング言語としてのマニュアルとなります. 初期化やサンプリング, チューニング方法についてはすべてのインターフェースで共通していて, また事後分布を分析する機能についてもおおむね共通しています.
提供されているすべてのインターフェースについて, getting-started guideやドキュメントが完全なソースコードと共に提供されています.
CmdStanはコマンドラインからStanを利用することを可能にします. ある意味でCmdStanはStanのリファレンス実装ともいえます. もともとCmdStanのドキュメントはこのドキュメントの一部でしたが, 今では独立したドキュメントとなっています. CmdStanのホームページは以下になります.
http://mc-stan.org/cmdstan.html
RStanはRにおけるStanのインターフェースです. R2WinBUGSやR2jagsでは, Rから外部のソフトウェアとしてWinBUGSやJAGSを呼び出していました. RStanではそうではなく, むしろRのメモリを通じたインターフェースになっています. RStanのホームページは以下になります.
PyStanはPythonにおけるStanのインターフェースです. 外側のStanを呼び出すというよりは, RStanと同様にPythonのメモリレベルのインターフェースです. PyStanのホームページは以下になります.
http://mc-stan.org/pystan.html
MatlabStanはMATLABにおけるStanへのインターフェースです. RstanやPyStanとは異なり, 現状MatlabStanはCmdStanのラッパーです. MatlabStanのホームページは以下になります.
http://mc-stan.org/matlab-stan.html
Stan.jlはJuliaにおけるStanのインターフェースです. これもMatlabStanと同様に, CmdStanのラッパーです. Stan.jlのホームページは以下になります.
http://mc-stan.org/julia-stan.html
StataStanはStataにおけるStanのインターフェースです. MatlabStan, Stan.jl と同様にこれもCmdStanのラッパーです. StataStanのホームページは以下になります.
http://mc-stan.org/stata-stan.html
Stanのプログラムでは条件付き確率分布 \(p(\theta \mid y, x)\) を通して統計モデルが定義されます. ここで\(\theta\)はモデルに組み込まれる, 一連の未知の変数(例: モデルのパラメータ, 隠れ変数, 欠測データ, 将来の予測値)です. \(y\)はモデルに組み込まれる, 一連の値が得られている変数です. \(x\)はモデルに組み込まれない, 一連の説明変数と定数です(例:サイズ, ハイパーパラメータ).
Stanのプログラムは, 変数の型宣言と文(ステートメント)からなります. 変数の型には整数, 実数, ベクトル, 行列はもちろん, その他の型の(多次元な)配列があり, それぞれ値を制約することもできます. 変数は, その役割に応じて, data
・transformed data
・parameters
・transformed parameters
・generated quantities
というブロックの中で宣言されます. また制約のないローカル変数を各ブロックで定義することもできます.
transformed data
, transformed parameters
, generated quantities
ブロックは, そのブロックで宣言された変数を定義する文を含んでいます. 特別なmodel
ブロックはモデルの対数尤度を定義する文で構成されています.
またmodel
ブロックの中では, Stanが内部で持っている対数確率をインクリメントするための略記として, BUGS風のサンプリング記法を利用できます. 対数確率の変数には直接アクセスすることもでき, ユーザによる確率関数の定義や変数変換のヤコビアンの調整を可能にします.
変数の制約はStanにおいて, 特にパラメータについて重要です. Stanが効率的にサンプリングをするためには, 宣言時の制約を満たすパラメータの値が, model
ブロックにおいて台(サポート)を持つ必要があります(言い換えると, ゼロでない事後密度をもつ必要があります).
data
ブロックとtransformed data
ブロックにおける制約は, データの入力および変換のエラーチェックのために使われるだけです. transformed parameters
ブロックにおける制約は, parameters
ブロックにおける制約と同じように満たされなければなりません. さもなくばサンプリングが完全にランダムウォークになってしまうか, 失敗することでしょう. generated quantities
における制約は必ず満たされる必要があり, さもなくばサンプリングが完全に停止することでしょう. なぜならgenerated quantities
ブロックが評価される時点で抽出されたサンプルを棄却するのは遅すぎるからです.
Stanの文は手続きとして解釈されるため, 順序が重要です. 文は最低でも変数に対する値の代入を伴います. 一連の文(と必要に応じたローカル変数の定義)によりブロックが構成されます. そしてStanもまたRやBUGSで使われている有限なfor-eachのループを提供しています.
Stanは手続き型の確率的プログラミング言語です. DSL(domain-specific language, ドメイン固有言語)の一種です. それは統計的な推論という特定の目的のために開発されたという意味です.
Stanは確率変数を正真正銘の第一級オブジェクトとして扱うという意味で確率的プログラミング言語です. Stanでは変数を確率変数として扱いますが, あるものは観測されていたり, またあるものは未知で推定の必要がある(もしくは事後の予測推論に使う必要がある)ものです. 観測された確率変数はdata
ブロックの中で宣言され, 観測されていない変数はパラメータとして宣言されます(parameters
ブロックで宣言された変数や, それに依存する, transformed parameters
ブロック内の変数・generated quantities
ブロック内の変数・ローカル変数も含みます). また観測されていない確率変数の周辺分布や同時分布からサンプリングすることもできますし, 確率変数の平均や分散を推定することもできますし, 二次的な事後の予測推論ではそれらの値をプラグインすることもできます.
StanはCやFortranと同様(C++, R, PythonやJavaなどと部分的に同じ)の手続き型プログラミング言語です. これは, 代入, ループ, 条件分岐, ローカル変数, オブジェクトレベルの関数や配列のようなデータ構造に基づいているという意味です. 関数型言語と比較すれば, 関数型言語では典型的には高階関数(汎関数)が許可され, しばしばリフレクション(プログラムの中でそのプログラムに含まれる型や変数/メソッドの情報を参照/操作できるようにする仕組み)が許可されています. 一方で純粋な関数型言語では代入が全く許可されていません. またオブジェクト指向言語はより一般的なデータ型を取り入れており, 動的に関数などが選ばれて実行されます.
Stan言語は, CやRと同様に, チャーチ=チューリング完全 [Church (1936); Turing (1936); Hopcroft and Motwani (2006)]です. それはチューリングマシン(もしくはC)で計算可能ないかなるプログラムもStanで実装できることを意味しています(もちろんその道は険しいですが). ちなみにチューリング完全であるために求められるのは, ループと条件分岐, そしてループの中でサイズが変更できる配列のみです.
Stanのコードはまず最初にStanのコンパイラstanc
によってC++の言語へと変換され, そしてそのC++はプラットフォーム依存の単独で実行可能な形式に変換されます. またStanはWindows, Mac OS X, Linuxなど様々な実行可能形式を出力することができます. 1 Stanの実行ファイルを実行すると, まず既知の \(y\) と \(x\) を読み込み, その妥当性を評価します. そして(独立ではない)同一分布に従うサンプルの列 \(\theta^{(1)},\theta^{(2)},\dots\) を生成します. これらは各々, 周辺分布 \(p(\theta \mid y, x)\) に従います.
連続値をとるパラメータに対してStanは Hamiltonian Monte Carlo (HMC) Sampling (Duane et al., 1987; Neal, 1994, 2011) というある種のマルコフ連鎖モンテカルロ(MCMC)サンプリング(Metropolis et al., 1953)を用います. Stanは離散値をとるパラメータのサンプリングは提供していません. 観測値としては離散値を直接利用することができますが, 離散値をとるパラメータはモデルから周辺化消去されている必要があります. 10章と12章では, いかにして有限の離散値をとるパラメータを和をとってモデルから消去するか, そしてその消去がもたらすサンプリング効率の大幅な向上について議論します.
HMCは対数確率関数の勾配を使うことで, 定常分布への収束とパラメタの探索の効率化を促進しています. 推定すべき量のベクトル\(\theta\)は仮想的な粒子の位置と解釈されます. それぞれのiterationにおいて, ランダムな運動量を生成し, (負の)対数確率関数を決めているポテンシャルエネルギー内の粒子の経路をシミュレーションします. ハミルトンの分解からこのポテンシャルの勾配が運動量の変化を与え, この運動量が位置の変化を与えることが分かります. この時間的に連続な変化は, 時間をシミュレーションが容易な離散値とみなすleapfrogアルゴリズムによって近似されます. 続けて, 様々なシミュレーションエラーを直し, マルコフ連鎖の遷移において詳細釣り合い条件を満たすようにするために, メトロポリス法の棄却ステップが適用されます (Metropolis et al., 1953; Hastings, 1970).
基本的なユークリッド空間上のHamiltonian Monte Carlo法には, 振る舞いに対して大きな影響を与える3つの「チューニングすべき」パラメータがあります. Stanのサンプラーはそれらを手動で設定する方法と, ユーザを介さずに自動で設定する方法の両方を提供しています.
1つ目のチューニングパラメータはステップサイズで, ハミルトニアンが測定される時間的な単位(すなわち, 離散化の単位)です. Stanではユーザが指定したステップサイズに設定することもできますが, 双対平均化法(Nesterov, 2009; Hoffman and Gelman, 2011, 2014)によってwarmupの段階で最適なステップサイズを推定することができます. どちらの場合もとり得るステップサイズの区間からステップサイズを抽出するために, 追加のランダム化が適用されます(Neal, 2011).
2つ目のチューニングパラメータはiterationごとのステップ数で, このステップ数と先ほどのステップサイズの積によりハミルトニアンに関する全シミュレーション時間が決まります. Stanはこのステップ数も特定のステップ数を指定することができますが, No-U-Turn (NUTS) sampler (Hoffman and Gelman, 2011, 2014)を使うと, このステップ数をサンプリング中に自動的に決定することができます.
3つ目のチューニングパラメータは仮想粒子の質量行列です. Stanはwarmupの間に対角行列となる質量行列や完全な質量行列を推定するように設定できます. また, 将来的にはユーザが指定した質量行列をサポートする予定です. 対角行列となる質量行列を推定すると, 未知パラメータ系列\(\theta\)の各要素\(\theta_k\)のスケールは正規化されます. 一方, 完全な質量行列を推定すると, スケーリングと回転の両方を考慮することができますが, 2行列演算のためにleapfrogの各ステップにおいて, より多くのメモリと計算を要します.
マルコフ連鎖から得られるサンプルは, その連鎖が定常分布に収束したあとに, 周辺分布 \(p(\theta \mid y, x)\) から抽出されます. MCMCが収束したかどうかを判定する方法は複数ありますが, 残念ながら, そうしたテストをパスしたからといって収束が保証されるものではありません. Stanにおいておすすめの方法は, いくつか異なるパラメータの初期値を用いてランダムに初期化した複数のマルコフ連鎖を走らせ, warmup/adaptationの期間のサンプルを捨て, 各チェーンの残りのサンプルを半分に分割することで, 潜在的なスケールの逓減に関する統計量 \(\hat{R}\) (Gelman and Rubin, 1992)を計算することです. もしその結果, 十分なサイズの有効サンプルが得られなければ, iterationの数を2倍にして再びやり直します. warmupなど全て含めてやり直しです. 3
\(M\) 個の独立に抽出されたサンプルに基づいて母平均を推定する場合, その推定誤差は \(\frac{1}{\sqrt{M}}\) に比例します. もしMCMCを使って抽出されたサンプルのように典型的にサンプル間に正の相関があれば, その推定誤差は \(\frac{1}{\sqrt{\text{N\_EFF}}}\) に比例します. ここで\(\text{N\_EFF}\) は有効サンプルサイズです. したがって, 目下の推定や推論のタスクに対し十分な大きさになるまで, 有効サンプルサイズ(やその推定値)をモニターするのは標準的な習慣です.
Stanはフルベイズの推定をサポートするために開発されました. ベイズ推定は下記の正規化されていないベイズ則
\[p(\theta \mid y, x) \propto p(y \mid \theta, x)p(\theta, x)\]
に基づいています. これは, データ \(y\)(と定数\(x\))が与えられたもとでのパラメータ \(\theta\) の事後分布 \(p(\theta \mid y, x)\) が尤度 \(p(y \mid \theta, x)\) と事前分布 \(p(\theta, x)\) の積に比例することを表します.
Stanでは, ベイズモデリングは定係数を除いて事後確率関数のコーディングを伴います. ベイズ則から事後確率分布は定係数を除いて尤度関数と事前確率の積と等価です.
フルベイズ推定では, 事後分布\(p(\theta|y, x)\)によるモデル化において, パラメータ\(\theta\)の値に関する不確実性が含まれ伝搬されてゆくことになります. このことは, 事後分布からの一連のサンプルについて推定を行うことで実現できます. その際には, 事後平均や事後確率区間等を関心のある量のためにプラグイン推定値として利用したり, 事後分布に基づいて事象の結果や未観測のデータ値を予測したりします.
Stanは最適化に基づく推論もサポートしています. 与えられた事後分布 \(p(\theta \mid y)\) に対して, Stanは以下で定義される事後分布の最頻値 \(\theta^\ast\) を見つけることができます.
\[\theta^\ast = \text{argmax}_\theta p(\theta \mid y)\]
ここで \(\text{argmax}_v f(v)\) という記法は 関数 \(f(v)\) を最大化する \(v\) の値を選ぶことを意味します.
もし事前分布が一様分布ならば, 事後分布の最頻値は, パラメータの最尤推定値(maximum likelifood estimate, MLE)に対応します. また, 事前分布が一様分布でなければ, この事後分布の最頻値はしばしばMAP(maximum a posterior)推定値と呼ばれます.
最適化においては, 変数の制約に由来するいかなる変換のヤコビアンも無視されます. 多くの最適化問題でより効率的に計算するには, 変数宣言時における上下限の制約を取り除いて, 代わりに台の範囲外となる無効な解を許さないようにmodel
ブロックで棄却するようにします.
推定値\(\theta^\ast\) はいわゆる「点推定値」と呼ばれています. これは事後分布を分布と言うよりむしろ一つの点として要約することを意味します. もちろん点推定値それ自体は推定のばらつきを考慮しません. 事後予測確率\(p(\tilde{y} \mid y)\)は, データ \(y\) が与えられた場合の事後最頻値を用いて, \(p(\tilde{y} | \theta^\ast)\) のように作ることができます. しかし, これらは事後確率に不確実性を考慮しないため, たとえ事前分布を持っていたとしてもベイズ推定ではありません. もし, 事後分布の分散が小さく, その平均が最頻値に近ければ, 点推定の結果はフルベイズの推定結果と非常に近くなります.
Stanはベイズ推定を近似する変分推論もサポートしています(Jordan et al., 1999; Wainwright and Jordan, 2008). 変分推論では近似した事後分布を用いて, 事後分布の平均値と不確実性を推定することができます. 近似した事後分布は, パラメトリックな分布が真の事後分布にあてはまるように最適化して求めます. 変分推論は, 特に機械学習の分野においてベイズ推定の計算にすさまじい影響を与えてきました. 典型的には変分推論は従来のサンプリングによる推定に比べて速く, 巨大なデータにスケールしやすいという特徴があります(Hoffman et al., 2013).
変分推論は事後分布 \(p(\theta \mid y)\) をシンプルなパラメトリックな分布 \(q(\theta \mid \phi)\) で近似します. このことは真の事後分布との以下で与えられるKullback-Leibler情報量を最小化することに対応します.
\[\phi^\ast = \text{argmin}_\phi \text{KL}[q(\theta \mid \phi) \Vert p(\theta \mid y)]\]
これはベイズ推定の問題を, 解がwell-definedな計量をもつ最適化問題に帰着できることを意味します. 変分推論では, サンプリングに比べてオーダーが異なるほど収束が速いです. 近似の精度はモデルによって異なります. 変分推論が点推定のテクニックではないことに注意すると, 結果は事後分布を近似した分布だということができます.
Stanでは自動微分変分推論(Automatic Differentiation Variational Inference, ADVI)というアルゴリズムが実装されています. このアルゴリズムはStanの変数変換ライブラリや自動微分に関するツールボックスを活用するようにデザインされています(Kucukelbir et al., 2015). ADVIは変分推論のアルゴリズムを導出するのに典型的に必要となるすべての数学を回避し, いかなるStanのモデルにおいても動作します.
この短い章では, Stan言語における文字のエンコーディング, ファイルを組み込むメカニズム, コメントする文法について説明しています.
Stanプログラムの内容は, ASCII(半角英数字)でなければなりません. UTF-8でエンコードされたUnicodeのような拡張文字セットは,ファイル中の識別子や他の文字として使ってはいけません.
コメントの中身はコンパイラに無視されるので, どのような文字コード(例えばASCII, UTF-8, Latin1やBig5)を使って書いても構いません. コメント区切り文字自体は, ASCIIで書く必要があります.
Stanは以下の書き方で, あるファイルに別のファイルを含めることができます. 例えば, std-normal.stan
というファイルが標準正規分布の(規格化定数を除いた)確率密度関数の対数を定義していたとしましょう.
functions {
real std_normal_lpdf(vector y) {
return -0.5 * y' * y;
}
}
さらにまた, include文を使った以下のStanプログラムも持っているとしましょう.
#include std-normal.stan
parameters {
real y;
}
model {
y ~ std_normal();
}
このStanプログラムは, #include
文が書かれた行をstd-normal.stan
ファイルの中身で置き換えたかのように振る舞います. すなわち, 以下の単一のStanプログラムが与えられたかのように振る舞います.
functions {
real std_normal_lpdf(vector y) {
return -0.5 * y' * y;
}
}
parameters {
real y;
}
model {
y ~ std_normal();
}
読み込み側ファイルのどこにinclude文が置かれるか, また置き換えられるファイルの中身が何であるかについては, 制限はありません. 読み込む側のファイルに空白が追加されることもありません.
再帰的includeは無視されます. 例えば, a.stan
というファイルが次のようなものだったとします.
#include b.stan
そしてb.stan
の中身が
#include a.stan
だったとします. このファイルを処理すると空になります. というのも, a.stan
がb.stan
をincludeし, それが読もうとするa.stan
は無視されるからで, 警告が表示されます.
Stanは, システムで定義された一連のPATH(複数のディレクトリ・フォルダ)を特定し, includeで指定されたファイルを探すメカニズムを提供しています. そのPATHの場所を先頭から順に探していき, 最初に見つかったファイルがincludeされます.
StanはC++スタイルの行ごとのコメント, あるいはカッコ付きコメントをサポートしています. Stanプログラムで空白が許されているところであれば, どこにコメントを入れても構いません.
二つのフォワードスラッシュ(//
)の後に続くいかなる文字列も, スラッシュとともに無視されます. これは例えば, 変数名に説明をつけるために使われたりします.
data {
int<lower=0> N; // 観測されたデータの数
real y[N]; // 観測されたデータ
}
フォワードスラッシュとアスタリスクのペア(\*
)と, アスタリスクとフォワードスラッシュのペア(*/
)で囲まれたあらゆるテキストは, カッコ付きコメントとして無視されます.
空白文字(そしてそのアスキーコード)は空白(0x20),タブ(0x09), キャリッジリターン(0x0D),そしてラインフィード(0x0A)です4.
Stanはこれらの空白文字を同じように扱います. 特に, タブ, キャリッジリターン, ラインフィード, その他スペースなどでテキストを縦方向に揃えることは実行結果に影響しません.
Stanプログラムでは, 文字の間に0以上の空白文字を置くことができます. 例えば a \* b
のようなバイナリ演算子の前後に, 文の終わりを示すセミコロンの前に, 丸カッコまたは角カッコの周辺に, 関数の引数を区切るコンマの前後などに, いろいろな種類の空白文字を置くことができます.
識別子とリテラルは空白で区切ることができません. つまり, 数字の10000
を10 000
と書いたり, 識別子のnormal_lpdf
をnormal _ lpdf
と書くことは不適切です.
本章ではStanで変数の宣言や, 式の値に使われるデータ型について議論します. 変数の型はパラメータの宣言, データの一貫性のチェック, 関数の呼び出し, 変数に値を代入する場合のすべてにおいて重要な要素となります. Stanにおいて, すべての式と変数の宣言は, 静的に定められたデータ型と関係しています(すなわちプログラムがコンパイルされた時). 一方, vector
,matrix
, 配列の大きさは動的に決定されます(プログラムが動作する際). これは動的に変数に文字列を代入し, 後から変数に行列を代入することができる, Rのような言語とは大きく異なっています. 式は変数, 定数のような基礎要素, もしくは引数に適用される関数や演算子のような要素から構成されているでしょう. 本章では基本的なデータ型に話題を絞り, それらがどのように宣言, 代入, 使用されるかについて解説します. 配列, vector
,matrix
などのコンテナ型の詳細な比較については次章に譲ります.
組み込み型関数やユーザ定義が定義した関数の引数, ローカル変数はいずれも基本データ型である必要があります. 基本データ型とは, 制約のないプリミティブ型, vector
型, matrix
型, およびそれらを並べた配列のことです.
Stanには連続値に対応したreal
と, 整数に対応したint
の2種のプリミティブ型が用意されています.
vector
, matrix
型Stanには列ベクトルに対応したvector
, 行ベクトルに対応したrow_vector
, 行列に対応したmatrix
の3種の行列ベースのデータ型が用意されています.
添え字をつけて宣言することで, 任意の型(次節で紹介する制約付き型も含む)を配列とすることができます. 以下に具体例を示します.
real x[10];
matrix[3,3] m[6,7];
上記のように記述すると, x
は1次元で10個の実数が含まれた値として宣言することになります. m
は同様に, 3 × 3行列を6 × 7個並べた2次元配列を宣言したことになります.
ローカル変数以外の変数を宣言する際には, 制約をつけても構いません. この際, それぞれの制約付きデータ型は, 制約付きの基本データ型と対応しています. 制約はdata
, transformed data
, transformed parameters
, generated quantities
ブロックで宣言された変数のエラーをチェックします. そのため, 制約は, parameters
ブロック内で宣言された変数を扱う際に重要になります. ここでは, parameters
ブロックで, 制約付き変数(宣言された制約を満たすもの)を制約のない変数(実数全体)にどのように変換するかを決めています. これは, "モデルは宣言された制約を満たしているパラメータすべての値に対応 (密度がNon-zero)する必要がある" という, 制約付きデータ型の最も重要な側面を示しています.
もし宣言されたパラメータの制約が対応よりも厳格でない場合, サンプラーやオプティマイザはさまざまな問題を抱えることになるでしょう. このような例として, プログラムが動作しない, 初期化の失敗, 過剰なMetropolis rejection, 分布の裾をサンプリングできないことに起因するサンプリングのバイアスなどが問題として挙げられます.
以下のようにすべての基本データ型に制約を宣言する構文を使うことで, 変数の上限, 下限を与えることができます.
int<lower=1> N;
real<upper=0> log_p;
vector<lower=-1,upper=1>[3,3] corr;
Stanには構造化されたvector
を扱うためのデータ型が用意されています. 昇順で並べた値のvector
に使われるordered
と, 昇順に並べた正の値のvector
について使われるpositive_ordered
がそれにあたります. また, 合計すると1になる非負の値のベクトルに対するデータ型としてsimplex
が, 2乗の合計が1になるベクトルに対するデータ型としてunit_vector
が用意されています.
対称な正定値行列を表すために, cov_matrix
が, 対角成分が1である対称な正定値行列, すなわち相関行列を表すために, corr_matrix
が用意されています. また, コレスキー因子の型も用意されています. cholesky_factor_cov
型は対称な正定値行列のコレスキー因子, すなわち正の対角成分を持つ下三角行列を表します. cholesky_factor_corr
型は相関行列のコレスキー因子, すなわち正の対角成分を持つ下三角行列で, さらに各々の行が単位ベクトル(つまり2乗して合計すると1)であるような下三角行列を表すために使用できます. 因子分解, スケーリングが容易なコレスキー因子型を使うことで, すべての相関行列, 分散共分散行列を計算するよりもはるかに効率よく計算を行うことができます.
制約付けられたデータの値は基本型にマッチする制約付けられていない変数に代入されるかもしれないし, その逆の, 制約付けられていないデータの値が基本型にマッチする制約付けられた変数に代入されることもあるかもしれません. マッチングに際しては配列の次元数, 基本型が同じであるか, 厳密に解釈されます. 制約付けは考慮されませんが, 基本データ型については考慮されます. 配列, vector
はお互いに代入することはできません. 同様に, たとえ次元が一致していても, vector
, matrix
はお互いに代入することはできません. 第4章ではvector
, 配列をどのように使い分けるのが適切かについて, 詳細を示します.
Stanの関数に引数を渡すと, 基本型への代入のように動作します. Stanの関数は配列の次元(real a[10,10,10]
なら3)を含む, 引数の基本データ型だけからどの関数が呼ばれるかが決まります(配列の大きさ, 制約は含まない). もちろん, 関数はしばしばそれらの動作の一部として制約を確認することがあります.
式は, Stanのプログラムで値を示す基本文法単位です. 整形式のStanのプログラムでは式はすべて, 静的に(コンパイル時に)決定される型を持ちます. 式の型を静的に決定できないときには, Stanのコンパイラは, この問題がある位置を報告します.
この章では, Stanにおける式の文法, 型決定, さまざまな形での使い方について扱います.
最も単純な形式の式は, プリミティブな数値を示すリテラルです.
整数リテラルはint
型の整数を表します. 整数リテラルは, セパレータなしで10進基数で書かれます. 整数リテラルには, 単一の負符号を含めることができます(--1
という式は, リテラル-1
を負にしたものと解釈されます).
以下は, 整形式の整数リテラルです.
0, 1, -1, 256, -127098, 24567898765
整数リテラルは, 整数値の上下限の間に収まる値をとる必要があります(2.2節を参照).
整数リテラルには, 小数点(.
)を含めることができません. したがって, 1.
も1.0
もreal
型の式であり, int
型の値が必要な場所で使うことはできません.
ピリオドあるいは科学記法を含めて書かれた数は, 連続値の数値型であるreal
型に割り当てられます. 実数リテラルは, ピリオド(.
)をセパレータとする10進数で書かれます. 以下は, 整形式の実数リテラルの例です.
0.0, 1.0, 3.14, -217.9387, 2.7e3, -2E-5
e
またはE
の後に正あるいは負の整数を続けた記法は10の累乗をかけることを示します. 例えば, 2.7e3
は\(2.7 \times 10^3\)を示し, -2E-5
は\(-2 \times 10^{-5}\)を示します.
変数はそれ自身が, その変数と同じ型を持つ整形式の式です. Stanの変数はASCIIの文字列からなり, 含めることができるのは, 基本的な小文字と大文字のローマ文字, 数字, アンダースコア(_
)文字のみです. 変数は, 文字(a-z
とA-Z
)で始めなくてはならず, 2個のアンダースコア(__
)で終わってはなりません.
以下は, 正しい変数識別子の例です.
a, a3, a_3, Sigma, my_cpp_style_variable, myCamelCaseVariable
RおよびBUGSとは異なり, Stanの変数識別子はピリオド文字を含めることができません.
Stanは, 内部用に多くの文字列を予約しており, 同じ名前の変数を使うことはできません. 内部用の文字列と同じ名前を変数につけようとすると, stanc
トランスレーターはエラーメッセージを出して止まり, 使われようとした予約名と, モデルコード中の位置を示します.
モデルの名前を, そのモデル内で変数に使うことはできません. これは普通は問題にはなりません. デフォルトではbin/stanc
が, モデル指定を含むファイルの名前の後に__model
を加えるからです. 例えば, モデルがfoo.stan
というファイルにあるとき, bin/stanc
によるデフォルトのモデル名を使うなら, foo_model
という名前の変数は使えません. ユーザーがモデルの名前を指定するときは, 変数はモデルの名前と同じにはできません.
ユーザー定義関数の名前をモデル中の変数に使うことはできません.
以下のリストは, Stanのプログラミング言語に予約されているワードです. Stanではまだ実装されていないものもありますが, これらトークンは将来の使用のために予約されています.
for, in, while, repeat, until, if, then, else, true, false
変数には, 型と同じ名前もつけてはいけません. したがって, 以下のいずれも使えません.
int, real, vector, simplex, unit_vector, ordered,
positive_ordered, row_vector, matrix, cholesky_factor_corr,
cholesky_factor_cov, corr_matrix, cov_matrix.
変数名は, 以下のブロック識別子と衝突はしません.
functions, model, data, parameters, quantities, transformed,
generated
StanのC++実装で使われているために予約されている変数名もあります.
var, fvar, STAN_MAJOR, STAN_MINOR, STAN_PATCH, STAN_MATH_MAJOR,
STAN_MATH_MINOR, STAN_MATH_PATCH
あらかじめ定義されている関数の名前と変数名とは衝突します. 定数とは衝突しません. したがって, 変数にlogit
やadd
という名前を付けることができませんが, pi
やe
という名前を付けることはできます.
変数名はまた, _lpdf
, _lpmf
, _lcdf
, _lccdf
, _cdf
, _ccdf
という接尾辞の分布名, たとえばnormal_lcdf
(訳注: 原文のnormal_lcdf_log
は誤り)とも衝突します. これは, 非推奨となった_log
, _cdf
, _cdf_log
, _ccdf_log
についても同様です.
こうした変数名のいずれかを使うと, stanc
トランスレーターは停止し, 衝突を起こした変数の名前と位置を報告することになります.
最後に, モデル名を含む変数名は, C++のキーワードのいずれとも衝突してはいけません.
alignas, alignof, and, and_eq, asm, auto, bitand, bitor, bool,
break, case, catch, char, char16_t, char32_t, class, compl,
const, constexpr, const_cast, continue, decltype, default, delete,
do, double, dynamic_cast, else, enum, explicit, export, extern,
false, float, for, friend, goto, if, inline, int, long, mutable,
namespace, new, noexcept, not, not_eq, nullptr, operator, or, or_eq,
private, protected, public, register, reinterpret_cast, return,
short, signed, sizeof, static, static_assert, static_cast, struct,
switch, template, this, thread_local, throw, true, try, typedef,
typeid, typename, union, unsigned, using, virtual, void, volatile,
wchar_t, while, xor, xor_eq
変数に使用可能な文字は, 0--127の範囲のASCIIコードポイントのもので, これはユニコードでも同じです.
文字 | ASCII(ユニコード)コードポイント |
---|---|
a -- z | 97 -- 122 |
A -- Z | 65 -- 90 |
0 -- 9 | 48 -- 57 |
_ | 95 |
もっとも表現力豊かな文字セットとは言えませんが, ASCIIはもっとも広く利用可能で, 不適切な文字エンコーディングやデコーディングをされてももっとも文字化けしにくいのです.
コメント内では, ASCIIと互換性のある文字エンコーディングを使用することができます. これには, ASCII自身やUTF-8, Latin1が含まれます. そのような文字を正しく表示できるかは, ユーザーのシェルやエディターに依存します.
式の列を波括弧で囲むと, 配列式となります. 例えば, { 1, 10, 100 }
という式は, 1, 10, 100という3つの要素からなる整数配列を表します. この文法は, 以下のように, 小さな配列を1行で定義するのにとくに便利です.
int a[3] = { 1, 10, 100 };
値は, 複合式でも構いません. つまり, { 2 * 3, 1 + 4}
と書いても正しい式です. また, 以下の例のように, 2次元配列を直接書くこともできます.
int b[2, 3] = { { 1, 2, 3 }, { 4, 5, 6 } };
このとき, b[1]
は{ 1, 2, 3 }
で, b[2]
は{ 4, 5, 6 }
です.
Stanでは, ホワイトスペースはどれも同じ扱いですから, 上の例は, 以下のように並べる方が, 行と列の構造で2次元配列になっていることをよりはっきりさせることができます.
int b[2, 3] = { { 1, 2, 3 },
{ 4, 5, 6 } };
式の型がどんなものであっても, 括弧で囲めば配列式となります. もっとも単純な場合では, 要素のすべてが同じ型で, その型の要素からなる配列が結果となるというものでしょう. 例えば, 配列の要素がベクトルのこともあります. その場合, 結果はベクトルの配列となります.
vector[3] b;
vector[3] c;
...
vector[3] d[2] = { b, c };
また要素には, int
型とreal
型の式が混ざっていても構いません. この場合, 結果は実数値の配列となります.
real b[2] = { 1, 1.9 };
配列式は使い方にいくつか制限があります. これは, 型がボトムアップで計算されることと, Stanの基本データ型および代入規則から生じます.
不ぞろいな配列式を定義したくなることもありますが, Stanのデータ型はすべて矩形(あるいは直方体や, それを高次に一般化したもの)です. したがって, 以下のようなネストした配列式は, 矩形ではない配列をつくろうとしたところでエラーとなります.
{ { 1, 2, 3 }, { 4, 5 } } // コンパイル時エラー: サイズのミスマッチ
これは, 1次元の整数配列(int[ ]
)2個からなる2次元の整数配列(int[ , ]
)をつくるものなので, 大丈夫に見えるかもしれません. しかし, 2個の1次元配列が同じサイズではないので, 許されません. 要素が配列式のときは, これはコンパイル時に検査されます. 一方あるいは両方の式が変数のときは, 実行時までエラーにならないでしょう.
{ { 1, 2, 3 }, m } // mのサイズが3でなければ実行時エラー
結果の型を推測する方法がないことから, 空の配列式({ }
)は許されません. これは表現力を犠牲にするものではありません. なぜなら, 要素数がゼロの配列として初期化するように宣言すれば十分だからです.
int a[0]; // aは要素数がゼロの配列として完全に定義されます
配列式の要素が, { 1, 2, 3 }
のように整数のみであれば, 結果の型は整数配列int[]
となります. これは, 以下のように書くのは正しくないことを意味します.
real a[2] = { -3, 12 }; // エラー: int[]はreal[]に代入できません
整数配列は実数値に代入できません. しかし, 実数リテラルの式を使えば, この問題は簡単に回避できます.
real a[2] = { -3.0, 12.0 };
これで型が合い, 代入できるようになります.
式を括弧で囲んだものもすべて式です. C++と同様に, しかしRとは異なり, 丸括弧(
および)
が使用可能です. 角括弧[
および]
は, 配列のインデックス操作に予約されており, 波括弧{
および}
は, 文のグルーピングに予約されています.
括弧を使うと, 部分式と演算子を明示的にグルーピングできます. 括弧なしの場合, 式1 + 2 * 3
は, 部分式2 * 3
を持ち, 7
と評価されます. 括弧を使った場合, 式1 + (2 * 3)
というふうに明示的にグルーピングできます. もっと大事なことは, 式(1 + 2) * 3
は, 1 + 2
を部分式として9
と評価されるということです.
整数値と実数値の式に対しStanでは, 加算(+
), 減算(-
), 乗算(*
), 除算(/
)の基本2項算術演算子を普通に使うことができます.
整数式に関してはStanには剰余(%
)の2項算術演算子があります. Stanにはまた, 整数値と実数値の式に対する負の単項演算子があります. 例えば, n
とm
を整数の変数とし, x
とy
を実数の変数とするとき, 以下は正しい式です.
3.0 + 0.14, -15, 2 * 3 + 1, (x - y) / 2.0,
(n * (n + 1)) / 2, x / n, m % n
符号反転・加算・減算・乗算の演算子は, 行列およびベクトル・行ベクトルにも拡張されています. アポストロフィ('
)として書かれる転値演算子もベクトル・行ベクトル・行列に使用可能です. 行列演算の戻り値の型は, 結果を格納できることが静的に保証できるもっとも小さな型となります. 許容される入力の型と対応する戻り値の型の組み合わせについての詳細は, 41章にすべて挙げてあります.
例えば, y
とmu
がvector
型の変数で, Sigma
がmatrix
型の変数のとき, 以下の式は, 整形式のreal
型の式です.
(y - mu)’ * Sigma * (y - mu)
式全体の型は, 部分式から外側へ向かって推測されます. 部分式y - mu
は, 変数y
とmu
がvector
型の式なのでvector
型です. この式を転値した部分式(y - mu)'
はrow_vector
型です. 乗算は左から結合し, 転値は乗算よりも優先順位が高いので, 上の式は, 以下の指定を完全に行った整形式のものと同じです.
(((y - mu)’) * Sigma) * (y - mu)
部分式(y - mu)' * Sigma
の型はrow_vector
と推測されます. 行ベクトルに行列を掛けた結果だからです. したがって, この式全体の型は, 行ベクトルに(列)ベクトルを掛けた型, つまりreal
型となります.
Stanには, 行列の要素ごとの乗算および除算演算子, a .* b
およびa ./ b
があります. ループを置き換えて簡略化できますが, 効率性の面では, ループ内で要素ごとに計算して代入するようプログラミングしたものと本質的には変わりません. 例えば, 以下のように宣言して,
vector[N] a;
vector[N] b;
vector[N] c;
このように代入するのと,
c = a .* b;
ループとでは同じ結果となり, 効率もほぼ同じです.
for (n in 1:N)
c[n] = a[n] * b[n];
Stanでは, 整数と実数値の式の累乗(^
)を使えます. 累乗の戻り値の型は常に実数値です. たとえば, n
とm
が整数の変数で, x
とy
が実数の変数とすると, 以下は正しい式です.
3^2, 3.0^-2, 3.0^0.14,
x^n, n^x, n^m, x^y
累乗は右から結合します. ですから, 下の式は,
2 ^ 3 ^ 4
以下の, 指定を完全に行った整形式と同じものです.
2 ^ (3 ^ 4)
演算子の優先順位と結合性は, 配列のインデックス操作や関数の適用といった組込みの文法と合わせて図4.1に表形式で示しました. その他の式形式の演算(関数の適用と添え字の指定)はどの算術演算よりも強く結合します.
優先順位と結合性は, 式がどのように解釈されるかを決定します. 加算は左結合ですから, 式a+b+c
は(a+b)+c
と解釈されます. 同様に, a/b*c
は(a/b)*c
と解釈されます.
乗算は加算よりも優先順位が高いので, 式a*b+c
は(a*b)+c
と解釈されますし, 式a+b*c
はa+(b*c)
と解釈されます. 同様に, 2*x+3*-y
は(2*x)+(3*(-y))
と解釈されます.
転値と累乗は, ほかの算術あるいは論理演算よりも強く結合します. ベクトルと行ベクトル・行列に関しては, -u'
は-(u')
と, u*v'
はu*(v')
と, u'*v
は(u')*v
と解釈されます. 整数と実数に関しては, -n ^ 3
は-(n ^ 3)
と解釈されます.
演算子 | 優先 | 結合 | 位置 | 説明 |
---|---|---|---|---|
? : |
10 | 右 | 3項間に入る | 条件 |
\|\| |
9 | 左 | 2項間に入る | 論理和 |
&& |
8 | 左 | 2項間に入る | 論理積 |
== |
7 | 左 | 2項間に入る | 等しい |
!= |
7 | 左 | 2項間に入る | 等しくない |
< |
6 | 左 | 2項間に入る | より小さい |
<= |
6 | 左 | 2項間に入る | より小さいか等しい |
> |
6 | 左 | 2項間に入る | より大きい |
>= |
6 | 左 | 2項間に入る | より大きいか等しい |
+ |
5 | 左 | 2項間に入る | 加算 |
- |
5 | 左 | 2項間に入る | 減算 |
* |
4 | 左 | 2項間に入る | 乗算 |
/ |
4 | 左 | 2項間に入る | (右)除算 |
% |
4 | 左 | 2項間に入る | 剰余 |
\\ |
3 | 左 | 2項間に入る | 左除算 |
.* |
2 | 左 | 2項間に入る | 要素ごとの乗算 |
./ |
2 | 左 | 2項間に入る | 要素ごとの除算 |
! |
1 | n/a | 単項接頭 | 論理否定 |
- |
1 | n/a | 単項接頭 | 負 |
+ |
1 | n/a | 単項接頭 | 正(Stanでは演算しない) |
^ |
0.5 | 右 | 2項間に入る | 累乗 |
' |
0 | n/a | 単項接尾 | 転値 |
() |
0 | n/a | 接頭,前後 | 関数の適用 |
[] |
0 | 左 | 接頭,前後 | 配列・行列のインデックス操作 |
図4.1: Stanの単項・2項・3項演算子における, 優先順位, 結合性, 式中の位置と説明. 最後の2行は関数適用と, 配列・行列・ベクトルのインデックス操作です. 演算子は, 優先順位にしたがって載せており, 上の方がより弱く, 下の方がより強く結合します. 正しい引数と, 対応する戻り値の組み合わせ全体は, 第7部の関数の説明のうち, operator
が前にあるものを見てください(たとえばoperator*(int, int):int
は, 乗算演算子を2つの整数に適用し, 整数を返すことを示しています). 括弧は, 優先順位と結合性に依存せず, 明示的に式をグルーピングするのにも使われます.
3項条件演算子は, 3つの引数をとるところと, 複合構文をつかうというところで独特です. a
がint
型の式で, b
とc
が互いに変換できる(すなわち==
で比較できる)式のとき,
a ? b : c
上の式はb
とc
とで昇格した型の式になります. Stanで認められている昇格は, 整数から実数のみです. 一方がint
型で他方がreal
型のとき, 条件式は全体としてreal
型になります. その他の場合はすべて, これら引数は同じStanの基本型である必要があり(この際重要なのは, 制約ではなく形式です), 条件式はその型となります.
条件演算子はもっとも優先順位が低い演算子です. そのため, あいまいさを避けるために引数に括弧を使う必要があることは稀です. 例えば,
a > 0 || b < 0 ? c + d : e - f
は, 明示的にグルーピングした以下の式と等価です.
(a > 0 || b < 0) ? (c + d) : (e - f)
括弧は厳密には必要ないとしても, 後者の方が読みやすいでしょう.
条件演算子は右から結合します. つまり,
a ? b : c ? d : e
は, 明示的にグルーピングすると以下のようになります.
a ? b : (c ? d : e)
前と同じく, 明示的にグルーピングした方が読みやすいでしょう.
Stanの条件演算子は, C++にあるものとほとんど同様に動作します. 最初の引数は, 整数となる式でなくてはなりません. 普通ここは, 上の例のa
のように変数または関係演算子です. 次に2つの結果の引数があり, 1番目は条件が真(すなわち非零)の場合, 2番目は条件が偽(すなわち零)の場合のものです. 上の例では, 条件が非零値と評価されればb
の値が返され, 条件が例と評価されればc
が返されます.
なぜ高性能計算で条件演算子がとても有用かというと, 戻り値となる部分式のみが評価され, そうでない式は評価されないという特性が鍵です. 言い換えると, 通常の関数とは違って, 関数に値を渡すために引数の式を残らず評価するということをしません. とくに, 組込みのif_else
関数よりも条件演算子の方がかなり高速になります. if_else
関数はほかの関数と同様, 呼び出されたときに3つの引数をすべて評価するのです. ほかの例と同じく, この節約のほとんどは, 基本的な関数評価そのものではなく, 導関数を計算しないことによります.
一方の戻り値の式がデータ値(定数と, data
あるいはtransformed data
ブロックで定義される変数のみからなる式)であり, 他方がそうではない場合, 3項演算子は, データ値をパラメータ値に昇格させます. これにより, 必要のない導関数の計算が起こる場合があり, 完全なif
-then
条件文より効率的でなくなることがあります. 以下はその例です.
data {
real x[10];
...
parameters {
real z[10];
...
model {
y ~ normal(cond ? x : z, sigma);
...
上のコードはより効率的に(より明快ではなくとも)下のように書けます.
if (cond)
y ~ normal(x, sigma);
else
y ~ normal(z, sigma);
条件文は, 条件演算子と同様, 結果の文のひとつしか評価しません. この場合, 変数x
はパラメータには昇格せず, そのため, 導関数の計算の間に連鎖律を伝播するとき, 不要な計算はまったく行なわれません.
Stanの配列・行列・ベクトルと行ベクトルはすべて, 配列と同様の記法でアクセスします. 例えば, x
がreal[]
型(1次元の実数配列)なら, x[1]
はその配列の1番目の要素の値です.
添え字は, ほかの算術演算子より高い優先順位にあります. 例えば, alpha*x[1]
はalpha*(x[1])
と等価です.
複数の添え字は, 1対の角括弧内の中で使用できます. x
が, real[ , ]
型, つまり2次元配列なら, x[2,501]はreal
型です.
添え字演算子は配列の部分配列も返します. 例えば, x
がreal[ , , ]
型のとき, x[2]
はreal[ , ]
型で, x[2,3]
はreal[]
型です. 結果として, 式x[2,3]
はx[2][3]
と同じ意味になります.
Sigma
がmatrix
型の変数のとき, Sigma[1]
はSigma
の1番目の行を指し, row_vector
型になります
Stanでは, 配列と, そのベクトル・行ベクトル・行列の値のインデックス操作とを混ぜることができます. 例えば, m
がmatrix[ , ]
型, つまり行列の2次元配列のとき, m[1]
はこの配列の1番目の行を示し, その型は行列の1次元配列となります. 2つ以上のインデックスも使うことができます. m[1,2]
はmatrix
型で, この配列の1番目の行と2番目の列にある行列を示します. インデックスをさらに増やすと, m[1,2,3]
はrow_vector
型で, m[1,2]
が指す行列の3行目を示します. 最後に, m[1,2,3,4]
はreal
型で, 配列m
の1行2列にある行列の3行4列の値を示します.
4.7節で述べた整数の単一インデックスに加え, Stanでは複数インデックス操作が使えます. 複数インデックスには, インデックスの整数配列や, 下限, 上限, 上下限, あるいはインデックス全体を簡略化したものがあります. インデックスの種別を全部しめした表は図4.2になります.
インデックス型 | 例 | 値 |
---|---|---|
整数 | a[11] |
11番目のインデックスの値 |
整数配列 | a[ii] |
a[ii[1]], ..., a[ii[K]] |
下限 | a[3:] |
a[3], ..., a[N] |
上限 | a[:5] |
a[1], ..., a[5] |
範囲 | a[2:7] |
a[2], ..., a[7] |
全体 | a[:] |
a[1], ..., a[N] |
全体 | a[] |
a[1], ..., a[N] |
図4.2: インデックスの型および, サイズN
の1次元コンテナと, サイズK
のint[]
型整数配列ii
の場合の例
複数インデックス操作を扱う場合の意味について, その基本則は次の通りです. idxs
が複数インデックスの場合, 結果にはインデックス操作が可能な位置が生成されます. つまり, 結果のインデックス位置を評価する際, まず複数インデックスに添え字が渡され, その結果得られるインデックスが使われることになります.
a[idxs, ...][i, ...] = a[idxs[i], ...][...]
一方, idx
が単一インデックスとのときには, 出力の次元が削減されます.
a[idx, ...] = a[idx][...]
問題となるのは, 行列とベクトルのときどうなるかだけです. ベクトルは配列とまったく同様です. 行に複数インデックスにつけ, 列にも複数インデックスをつけた行列は, 行列になります. 行に複数インデックスをつけ, 列に単一インデックスをつけた行列は, (列)ベクトルになります. 行に単一インデックスをつけ, 列に複数インデックスをつけた行列は, 行ベクトルになります. これらの型は図4.3にまとめてあります.
式 | 行インデックス | 列インデックス | 結果の型 |
---|---|---|---|
a[i] | 単一 | n/a | 行ベクトル |
a[is] | 複数 | n/a | 行列 |
a[i, j] | 単一 | 単一 | 実数 |
a[i, js] | 単一 | 複数 | 行ベクトル |
a[is, j] | 複数 | 単一 | ベクトル |
a[is, js] | 複数 | 複数 | 行列 |
図4.3: 引数が単一か複数かによって行列の次元が削減される特別な規則. この例では, a
が行列, i
とj
が整数の単一インデックス, is
とjs
が整数配列の複数インデックス. 同じ型の規則がすべての複数インデックスに適用されます.
複数インデックスをつけた行列の評価は, 以下の分配条件にしたがって定義されます.
m[idxs1, idxs2][i, j] = m[idxs1[i], idxs2[j]]
m[idxs, idx][j] = m[idxs[j], idx]
m[idx, idxs][j] = m[idx, idxs[j]]
行列の配列およびベクトル・行ベクトルの配列の評価は, 配列の次元からはじまり, 再帰的に定義されます.
Stanには, 幅広い種類の数学および統計関数が組み込まれており, それについては第7部で説明しています.
Stanの式には, 関数名と, その後に続く0個以上の引数の式からなっているものもあります. 例えば, log(2.0)
はreal
型の式で, 実数リテラル2.0
の値を自然対数に適用した結果を示しています.
文法的には, 関数適用はほかのどの演算子よりも高い優先順位を持ちますから, y + log(x)
はy + (log(x))
と解釈されます.
関数はおのおの, 引数と戻り値にとりうる型を決めている型のシグネチャがあります. 例えば, 対数関数のシグネチャは以下のように表されます.
real log(real);
lmultiply
関数のシグネチャは以下です.
real lmultiply(real,real);
関数は, その名前と, 引数の型の並びとによって, ただひとつに決定されます. たとえば, 以下の2つの関数は別々の関数です.
real mean(real[]);
real mean(vector);
1番目は, 実数値の1次元配列に適用され, 2番目はベクトルに適用されます.
関数の同一性条件により, 同じ名前と引数の型を持ちながら戻り値の型が異なる2つの関数をつくることは明示的に禁止されています. この制限により, 関数の式の型を構成から推測するのに, その部分式の型を調べればよくなってもいます.
Stanの定数は, 引数なしの関数に過ぎません. 例えば, 数学定数\(\pi\)と\(e\)は引数なし関数pi()
とe()
です. 組込み定数の一覧は39.2節を参照してください.
整数型は実数型に昇格するので, 一連の引数の型が与えられた場合, どの関数が呼ばれるのか, 規則を決めておく必要があります. Stanで採用されているのは, C++で使われているものと同じやり方です. すなわち, 必要とする型の昇格の数を最小にする関数を呼び出すように解決しています.
例えば, 以下の2つの関数シグネチャがfoo
に登録されているとします.
real foo(real,real);
int foo(int,int);
式foo(1.0,1.0)
でfoo
を使うと, foo(real,real)
と解決されます. したがって, 式foo(1.0,1.0)
自体にはreal
型が割り当てられます.
整数は実数に昇格することがありますから, 式foo(1,1)
は潜在的にはfoo(real,real)
とfoo(int,int)
のいずれにも当てはまります. 前者では型の昇格が2回必要ですが, 後者ではその必要がありません. したがって, foo(1,1)
は関数foo(int,int)
と解決され, int
型が割り当てられます.
式foo(1,1,0)
は引数の型が(int,real)
なので, 明示的にはどの関数シグネチャにも当てはまりません. 整数式1
がreal
型に昇格することにより, foo(real,real)
に当てはめることができ, したがって関数式foo(1,1.0)
の型はreal
です.
(Stanの組込み関数にはありませんが)式からはどの関数が参照されるのか多義的で決められないという状況もありえます. 例えば, 以下のシグネチャをもつ, まさに2つの関数があるという場合を考えましょう.
real bar(real,int);
real bar(int,real);
シグネチャからは, 式bar(1.0,1)
とbar(1,1.0)
は, それぞれ上の関数の1番目と2番目と解決されます. 実数値は整数に降格できませんから, 式bar(1.0,1.0)
は誤りです. 式bar(1,1)
は別の理由で誤りです. 1番目の引数が実数値に昇格すれば, 1番目のシグネチャに当てはまりますが, 2番目の引数が実数値に昇格すれば, 2番目のシグネチャに当てはまります. 問題は, 両方とも1回の昇格を必要とするということにあり, したがって関数bar
は多義的となります. 上のbar(1, 1)
のように二つを超える解釈が存在する場合, ほかの関数よりもより少ない回数の昇格を必要とする関数がただ1つでないときには, Stanのコンパイラは式が誤りであるというフラグを立てます.
Stanにある分布のほとんどには, 対応する乱数発生関数があります. これら乱数発生器は, 分布に_rng
という接尾辞をつけた名前となっています. 例えば, 1変量正規乱数はnormal_rng(0,1)
で生成できます. 変量は生成されますから, 分布のパラメータ, ここでは位置(0)とスケール(1)のみを指定します.
乱数発生関数の使用は, transformed data
あるいはgenerated quantities
ブロックに限られます. ほかの場所で使おうとすると, パースエラーとなり診断メッセージが出力されます. このほか, 名前が_rng
で終わるユーザー定義関数の本体でも使うことができます.
これにより, 乱数生成関数は一般にシミュレーションのために利用することができますが, 特にベイジアン事後予測のチェックのために使えます.
事後予測チェックは通常, モデルのパラメータを使って, (個体レベルで, 階層モデルでは群レベルでも)シミュレートしたデータを生成します. これにより, 見た目ならグラフにプロットして, 正式には検定統計量を使って, 実際のデータと比較することにより, モデルの適合性を調べることができます. 事後予測チェックについてさらに知るにはGelman et al. (2013)の6章を参照してください.
Stanは強い静的型付けをします. これは, 式の実装の型がコンパイル時に解決できることを意味します.
Stanでは, プリミティブな実装の型は, int
, real
, vector
, row_vector
, matrix
です. 基本的な型宣言はすべて, プリミティブな型と対応しています. 型と, そのプリミティブな型との対応は図4.4を参照してください. 実装の型は完全には, プリミティブな実装の型と, 0以上の整数配列の次元数からなります. これらを記述する際には, 配列としての特徴が強調されることになるでしょう. 例えば, int[]
は次元数1の配列で, int
は次元数0の配列で, int[ , , ]
は次元数3の配列です. matrix[ , , ]
という実装型は全部で5の次元数を持ち, そのうちの3つは配列のもので, 2つは行列のものです.
式では, 配列の次元は, 行列やベクトルの次元の前に来るということを思い出してください. 以下の宣言は, 行列の3次元配列の例です.
matrix[M, N] a[I, J, K];
この行列はa[i, j, k, m, n]
と添え字が付きます(配列のインデックスが先で, その後が行列のインデックスです)ので, a[i, j, k]
は行列で, a[i, j, k, m]
は行ベクトルとなります.
型 | プリミティブな型 |
---|---|
int |
int |
real |
real |
matrix |
matrix |
cov_matrix |
matrix |
corr_matrix |
matrix |
cholesky_factor_cov |
matrix |
cholesky_factor_corr |
matrix |
vector |
vector |
simplex |
vector |
unit_vector |
vector |
ordered |
vector |
positive_ordered |
vector |
row_vector |
row_vector |
図4.4: Stanの変数宣言の型と対応するプリミティブな実装の型の表. Stanの関数・演算子・確率関数は引数と戻り値の型を持ち, それらはプリミティブな型と配列次元数によって宣言されます.
Stanの型推定規則は, 変数宣言の背後にある組み合わせに基づいて, ある式の実装の型を定義します. この規則は, プリミティブなリテラルと変数の式から, 複合した式へとボトムアップに作用します.
42
のような整数リテラルの式はint
型です. 42.0
のような実数リテラルはreal
型です.
局所的に, あるいは前のブロックで宣言された変数の型はその宣言で決定されます. ループ変数の型はint
です.
各変数の宣言は, スコープ毎に常に唯一となります. Stanでは, 既に宣言された変数をもう一度宣言することを禁止しているからです. 5
x
が全体の次元数が\(N\)以上の式であっても, 式e[i1, ..., iN]
の型がe[i1]...[iN]
と同じことからもわかるように, 単一インデックスによる型が定義できれば説明には十分です. e
を式, i
をプリミティブな型がint
の式とします. すると, 以下のようになります.
e
が, 次元数\(K > 0\)の配列の式のとき, e[i]
は, 次元数\(K - 1\)であり, e
と同じプリミティブな実装の型を持ちます.e
の実装型が, 次元数0の配列のvector
あるいはrow_vector
のとき, e[i]
の実装型はreal
です.e
の実装型がmatrix
のとき, e[i]
の型はrow_vector
です.f
が関数の名前で, \(N \ge 0\)でe1,...,eN
が式のとき, f(e1,...,eN)
は式で, その型は, e1
からeN
に対するf
の関数シグネチャでの戻り値で決定されます. 関数シグネチャは, 引数の型と戻り値の型の宣言であることを思い出してください.
関数を調べるとき, real * real
のような2項演算子は, ドキュメントや索引ではoperator*(real,real)
というふうに定義されています.
関数の定義を照合するとき, int
型の引数は必要に応じてreal
型に昇格する場合があります(Stanの整数-実数の型昇格規則についての正確な仕様は, 4.9節の型昇格についての小節を参照してください).
一般に行列演算は, 推測される型の中でもっとも低い次元のものを返します. 例えば, row_vector * vector
はreal
型の値を返します. この演算子は, 関数のドキュメントと索引ではreal operator*(row_vector,vector)
と宣言されています.
モデルにより定義される対数確率関数の導関数は, Stanでは何通りかで使われます. NUTSを含むハミルトニアンモンテカルロサンプラーでは, 更新を動かすのに勾配を使います. BFGSオプティマイザーも, 事後最頻値の探索を動かすのに勾配を使います.
純粋に数学的に評価するのとは異なり, Stanでの導関数の評価は, 浮動小数点演算を使った評価で, 式ごとに連鎖律を適用することにより行なわれます. その結果, 以下のようなモデルでは導関数に関係した推定に問題が発生します.
parameters {
real x;
}
model {
x ~ normal(sqrt(x - x), 1);
}
代数的には, このサンプリング文は以下のように簡単にできるでしょう.
x ~ normal(0, 1);
そしてこのモデルは, x
について単位正規分布のサンプルを生成するように思えます. このように相殺しておかないと, 式sqrt(x - x)
は, 導関数に問題を発生させます. 原因は, 連鎖律を機械的に評価するためです.
\[\begin{array}{rl} \frac{d}{dx}\sqrt{x - x} &= \frac{1}{2\sqrt{x - x}}\times\frac{d}{dx}(x - x)\\ &= \frac{1}{0}\times(1 - 1)\\ &= \infty \times 0 \\ &= \mathrm{NaN} \end{array}\]
\(x - x\)が消去されるのではなく, 連鎖律の評価の分子と分母に0を発生させます.
この種の問題を避ける唯一の方法は, 必要な代数的消去をモデルの一部として注意深く行ない, 連鎖律が非数値(NaN)を生成するsqrt(x - x)
のような式を組み込まないようにすることです.
導関数で何か問題が発生しているかどうかを診断するもっともよい方法は, サンプラーあるいはオプティマイザーの入力で勾配テストのオプションを使うことです. このオプションは, StanでもRStanでも使えます(ただし遅くなるかもしれません. 組込みの自動微分と比較するために有限差分に依存するからです. ).
例えば, 上のモデルを実行形式sqrt-x-minus-x
にコンパイルすると, 以下のようにテストできます.
> ./sqrt-x-minus-x diagnose test=gradient
...
TEST GRADIENT MODE
Log probability=-0.393734
param idx value model finite diff error
0 -0.887393 nan 0 nan
有限差分が勾配を正しく0と計算しているにもかかわらず, 自動微分は連鎖律に従い, 非数値の出力を生成しています.
Stan プログラムのブロック (6章を参照) は, 変数の宣言とステートメント (文) から成り立っています. BUGS と違って, Stan プログラム中の宣言とステートメントはそれらが 記載された順序で実行されます. 変数は, それが参照される前に何らかの値 (と何らかのデータ型の宣言) によって定義されていなければいけません --- そうでない場合, 実行結果は未定義となります.
BUGS と同じく, Stan の基本的なステートメントには代入とサンプリングの 2 種類があります. また, いくつかのステートメントは 連続した処理やfor-each ループの繰り返し処理にグルーピング できるでしょう. 加えて, Stan では ブロック内でのローカル変数の宣言や, セミコロンのみからなる 空のステートメントも許容されています.
代入ステートメントは変数と式からなります. 変数はインデックス情報を持つ多変量の場合もあります. 代入ステートメントが実行されると, ステートメントの右辺にある式が評価され, その結果が左辺の変数 (インデックスがある場合は指定された箇所) に代入されます. 簡単な代入の例は以下のようなものです. 1
n = 0;
このステートメントが実行されると, 式 0
, つまり 整数のゼロが評価され, 変数 n
に代入 されます. 代入が成立するためには, 右辺の式のデータ型と左辺の変数のデータ型は一致していな ければなりません. 上の例では, 0
という式は int
型になるため, 変数 n
は int
型もしくは real
型で宣言されていなければなりません. 変数が real
型で宣言されている場合は, 整数のゼロは浮動小数点のゼロに変換されて 変数に代入されます. ステートメントが実行された後は, 変数 n
はゼロ (変数のデータ型に応じて整数 もしくは浮動小数点) という値を持ちます.
1Stan の 2.10.0 より前のバージョンでは, 代入には等号 =
ではなく <-
演算子が用いられていました. <-
演算子は現在では非推奨となり警告が表示されます. <-
演算子は将来のバージョンで削除されます.
文法的には, すべての代入ステートメントはセミコロンで終わっていなければなりません. それ以外では, トークンの間の空白は処理に影響しません (ここでのトークンは左辺の変数, 代入演算子, 右辺の式, セミコロンを指します).
右辺の式が最初に評価されるため, Stan でも C++ や他のプログラミング言語と同じように 変数を変数をインクリメントすることができます.
n = n + 1;
このような自己代入は BUGS では許されていません. なぜなら, 自己代入は 有向グラフィカルモデル中で循環を引き起こすからです.
代入ステートメントの左辺は配列, matrix
, vector
といったデータ構造に対するインデックスを 含むことがあります. 例えば, matrix
として定義された Sigma
に対して,
Sigma[1,1] = 1.0;
という代入ステートメントは, Sigma
の 1 行 1 列目の値に 1
を代入します.
代入ステートメントにはあらゆるデータ型の複雑なオブジェクトを含むことができます. Sigma
と Omega
がmatrix
, sigma
が vector
の場合, 次の代入ステートメントは 成立します. 変数のデータ型と式の結果はどちらも matrix
型になるためです.
Sigma
= diag_matrix(sigma)
* Omega
* diag_matrix(sigma);
また, この例は複雑な代入ステートメントを複数の行に分割して記載する場合に望ましい例を 示しています.
Stan は より大きな多変量のデータ構造の一部に対する代入もサポートしています. 例えば, a
が real[ , ]
型の配列, b
が real[]
型の配列の場合, 以下二つの 代入ステートメントはどちらも成立します.
a[3] = b;
b = a[4];
同じように, x
が row_vector
型, Y
が matrix
型の変数として 宣言されている場合, 次の一連の処理は成立します. 処理の結果, Y
の最初の 2 行が 入れ替わります.
x = Y[1];
Y[1] = Y[2];
Y[2] = x;
代入ステートメント中の左辺として適切な式を "左辺値" と呼びます. Stan では, 適切な左辺値はこの 2 種類しかありません.
インデックス指定された変数を左辺値として使うためには, その変数は少なくともインデックスと同じ数の 次元を持っていなければいけません. 実数や整数の配列は宣言された数の次元を持ちます. matrix
は 2 次元, vector
やrow_vector
は 1 次元です. これは分散共分散行列 ( cov_matrix
) や 相関行列 ( corr_matrix
), またそれらのコレスキー因子 ( cholesky_factor_cov
, cholesky_factor_corr
)や 昇順のベクトル ( orderd
), 昇順で正のベクトル ( positive_ordered
), 各要素が [0, 1]
で合計が1
となるベクトル (simplex
) といった 制約付きデータ型についてもあてはまります. 通常の配列の次元と比べて, matrix
の配列の次元は 2 多くなり, vector
やrow_vector
の配列の次元は 1 多くなります. 左辺値のインデックスの数は左辺の変数の次元よりも 少なくてよいことに注意してください. この時, 右辺は多次元で左辺値の次元と一致している必要があります.
すべての代入は, 事前に右辺の式のをコピーしたかのようにして行われます. これにより, 「代入ステートメントの実行中に右辺の式の値が変更される」ことによって生じる, エイリアスの潜在的な問題を解決しています.
Stanでは, 単純な線形回帰からマルチレベルの一般化線形モデルまで, 回帰モデルを扱えます.
以下は最も単純な線形回帰モデルで, 1つの予測変数と, 傾きと切片の係数があり, ノイズは正規分布です. このモデルは, 標準的な回帰の記法を用いて記述できます.
\[ y_{n} = \alpha + \beta x_{n} + \epsilon_{n} \quad\text{ここで}\quad \epsilon_{n} \sim \mathsf{Normal}(0, \sigma) \]
これは, 以下のように残差を取り入れてサンプリングするのと等価です.
\[ y_{n} - (\alpha + \beta X_{n}) \sim \mathsf{Normal}(0, \sigma) \]
さらに短くなります.
\[ y_{n} \sim \mathsf{Normal}(\alpha + \beta X_{n}, \sigma) \]
このモデルの最後の形はStanでは以下のようにコーディングします.
data {
int<lower=0> N;
vector[N] x;
vector[N] y;
}
parameters {
real alpha;
real beta;
real<lower=0> sigma;
}
model {
y ~ normal(alpha + beta * x, sigma);
}
N
回の観測がおこなわれ, その各回に予測変数x[n]
と結果変数y[n]
とがあります. 切片と傾きのパラメータはalpha
とbeta
です. このモデルでは, スケールsigma
の, 正規分布するノイズ項を仮定しています. また, 2つの回帰係数には非正則事前分布が設定されています.
前のモデルのサンプリング文はベクトル化されています.
y ~ normal(alpha + beta * x, sigma);
同じモデルの, ベクトル化されていないバージョンは以下のとおりです.
for (n in 1:N)
y[n] ~ normal(alpha + beta * x[n], sigma);
より簡潔なことに加えて, ベクトル化された形の方がはるかに高速です. 1
Stanでは一般に, normal
のような分布に渡す引数はベクトルにすることができます. いずれかの他の引数がベクトルまたは配列なら, すべて同じサイズでなくてはなりません. いずれかの他の引数がスカラーなら, その値はベクトルの各要素に再利用されます. 確率関数のベクトル化についてのより詳しい情報は46.8節を参照してください.
この書き方がうまくいく他の理由は, 行列には行列演算を行なうように, Stanの算術演算子がオーバーロードされるからです. この場合では, x
がvector
型でbeta
がreal
型なので, 式beta * x
はvector
型です. Stanはベクトル化をサポートしているので, 2つ以上の予測変数がある回帰モデルもそのまま行列の記法を用いて書くことができます.
data {
int<lower=0> N; // データ項目の数
int<lower=0> K; // 予測変数の数
matrix[N,K] x; // 予測変数の行列
vector[N] y; // 結果変数のベクトル
}
parameters {
real alpha; // 切片
vector[K] beta; // 予測変数の係数
real<lower=0> sigma; // 誤差のスケール
}
model {
y ~ normal(x * beta + alpha, sigma); // 尤度
}
sigma
の宣言にはlower=0
という制約をつけて, 値が0以上になるように制限しています. model
ブロックには事前分布がないので, 非負の実数の非正則事前分布ということになります. より情報のある事前分布を加えることもできますが, 正則事後分布が導ける限り, 非正則事前分布でも問題はありません.
上のモデルでは, x
は\(N \times K\)行列の予測変数, beta
は\(K\)次元ベクトルの係数なので, x * beta
は\(N\)次元ベクトルの予測値です. \(N\)個のデータ項目のそれぞれに対応します. これら予測値は, \(N\)次元ベクトルy
にある結果変数に対応して並んでいますので, 上のようにモデル全体を行列演算を使って書くことができます. 値が1の列をx
に含めることにより, alpha
パラメータをなくすこともできるでしょう.
上のモデルは, ループを使った下のようなモデルと統計的に等価です. 上のモデルのサンプリング文は, より効率的な, ベクトルによる方法でコーディングしたところしか違いません.
model {
for (n in 1:N)
y[n] ~ normal(x[n] * beta, sigma);
}
Stanの行列のインデックスの仕組みでは, x[n]
は行列x
の行n
を取り出します. beta
は列ベクトルなので, 積x[n] * beta
はreal
型のスカラーです.
1 インタープリタのPythonやRとは違って, StanはC++に変換されてコンパイルされるので, ループも代入文も高速です. ベクトル化したコードがStanで速いのは次の理由によります. (a)導関数を計算するのに使われる式木を単純にできるので, 実質的な関数呼び出しを少なくできます. (b)上のモデルのlog(sigma)
のような計算は, ループ版では繰り返し実行されますが, ベクトル化すると1度だけ実行されて, その後は再利用されます.
以下にあるモデルの定式化では, 切片の係数alpha
はもうありません.
y ~ normal(x * beta, sigma);
そのかわり, 入力行列x
の最初の列が, 値が1の列であると仮定しています. この方法では, beta[1]
が切片の役割を果たしているのです. もし切片が, 傾きの項とは異なる事前分布を取っているなら, 違いがはっきりするでしょう. 乗算の回数が1つ減るので, 係数の変数を明示的に別にする形式よりもやや効率的でもあります. といっても, 速度にはたいした違いはでないでしょうから, これを選ぶ理由は明確さにあるでしょう.
この節では, 回帰の係数とスケールの事前分布をモデリングするときにどのような選択肢があるか説明します. 階層モデルにおける1変量のパラメータの事前分布は9.9節で議論し, 多変量のパラメータについては9.12節で議論します. また, モデルの識別性のために使う事前分布については9.11節で議論します.
スケールパラメータの事前分布の選択についての概要についてさらに知るにはGelman (2006)を参照してください. 罰則付き最尤推定値におけるスケールの事前分布の選択の概要についてはChung et al. (2013)を参照してください. 回帰係数の事前分布の選択に関する議論についてはGelman et al. (2008)を参照してください.
Stanはデフォルトでは, 宣言された制約で定まる範囲の値をすべて取りうる一様(あるいは「平坦」)事前分布をパラメータに設定します. したがって, 制約なしに宣言されたパラメータはデフォルトでは(\(-\infty\),\(\infty\))の一様事前分布が与えられます. 一方, 下限が0と宣言されたスケールパラメータでは, (0,\(\infty\))の非正則一様事前分布となります. 両者の事前分布とも, 取りうる範囲全体で積分すると1になるような密度関数には定式化する方法がないという意味で非正則です.
Stanでは, 非正則事前分布でモデルを定式化することができますが, サンプリングあるいは最適化がうまくいくためには, 与えられたデータによって事後分布が正則にならなければなりません. そのためには通常, 必要最小限のデータ量がなくてはなりませんが, 最小量のデータを与えることは推定のはじめの一歩として有用なことがあります. あるいは, 感度分析(すなわち, 事前分布が事後分布に及ぼす影響を検討する)の基準としても同様です.
一様事前分布は, それが定式化された軸に依存します. 例えば, スケールパラメータ\(\sigma > 0\)に(0,\(\infty\))という一様事前分布を与え, \(q(\sigma)=c\)(「密度」は正規化されていないものだけではなく, 正規化できないものにも使われるので, ここでは\(q\)を使います)とすることもできるでしょうし, 対数軸を使って, \(\log\sigma\)に(\(-\infty\),\(\infty\))という一様事前分布を与え, \(q(\log\sigma)=c\)とすることもできるでしょう. 対数変換に必要なヤコビアンの調整のため, これらは\(\sigma\)について別の事前分布となります. 変数変換とそれに必要なヤコビアンの調整についてもっと知りたいときは34.1節を参照してください.
Stanは, 制約付きで宣言された変数について, 制約された範囲の値を取りうる一様密度となるように, 必要なヤコビアンの調整を自動的におこないます. このヤコビアンの調整は最適化のときには行なわれませんが, これは適切な最尤推定値を生成するためです.
上限と下限の両方を設定することで, 正則な一様事前分布に従うような変数を宣言することも可能です. 以下はその例です.
real<lower=0.1, upper=2.7> sigma;
これはsigma
に, \(\mathsf{Uniform}(0.1,2.7)\)という事前分布を暗黙のうちに与えるでしょう.
制約すべてについて同じことが言えますが, sigma
の取りうるすべての値についてモデルでも同じとなっていることが重要です. 例えば, 下のコードではsigma
が正に制約されていますが, その一様事前分布では上下限を設定しています.
parameters {
real<lower=0> sigma;
...
model {
// *** 悪い例 *** : 制約より狭い範囲しか取りません
sigma ~ uniform(0.1, 2.7);
このサンプリング文はsigma
に(0.1, 2.7)の範囲を設定しています. この範囲は, 制約で宣言された範囲, すなわち(0, \(\infty\))よりも狭くなっています. このようにすると, Stanプログラムは, 初期化が困難になったり, サンプリング中にハングしたり, ランダムウォークに陥ったりする可能性があります.
範囲制限のついたパラメータの境界近くに推定値があることは通常, 事前分布がモデルに合っていないことを示しています. また, サンプリングまたは最適化の際に, アンダーフローやオーバーフローといった数値的な問題を起こす可能性もあります.
回帰係数に\(\mathsf{Normal}(0,1000)\)のような事前分布をつけたモデルは珍しくありません. 2事前分布のスケールが, 1000のように, 推定される係数よりも数桁大きい場合は, そのような事前分布は事実上まったく何の効果も持ちません.
BUGSの例題(Lunn et al., 2012)は全般に, スケールについてのデフォルトの事前分布を下のようにしていますが, これは使わないようにしましょう.
\[ \sigma^2 \sim \mathsf{InvGamma}(0.001, 0.001) \]
このような事前分布は, 妥当な事後分布の値の外側に, あまりに大きく集中した確率の山があります. 正規分布につける対称的で広い事前分布とは異なり, これにより事後分布をゆがめる深刻な影響が発生する可能性があります. 例題と議論はGelman (2006)を参照してください.
2この習慣はBUGSでは普通で, 例題(Lunn et al., 2012)のほとんどに見受けられます.
下限が0として宣言された変数に, 正規分布の事前分布を設定すると, Stanのモデルでは, 正則に切断された半正規分布の事前分布を設定するのと同じ効果を持ちます. 0での切断分布を指定する必要はありません. Stanで計算に必要なのは密度から割合までだけで, 確率として正規化する必要はないからです. そこで, 以下のように変数を宣言します.
real<lower=0> sigma;
そして, 事前分布を与えます.
sigma ~ normal(0,1000);
すると, sigma
には半正規分布の事前分布が与えられます. 技術的には以下のようになります.
\[ p(\sigma) = \frac{\mathsf{Normal}(\sigma \mid 0,1000)}{1 - \mathsf{NormalCDF}(0 \mid 0, 1000)} \propto \mathsf{Normal}(\sigma \mid 0, 1000) \]
しかしStanでは, 半正規分布を正規化するのに必要な, 正規分布の累積分布関数(CDF: cumulative distribution function)の計算を避けることができます. もし, 事前分布の位置あるいはスケールがパラメータであるか, あるいは切断点がパラメータであるならば, 切断の計算をせずに済ますことはできません. その場合は, 正規分布のCDF項が定数にならないからです.
普通は, 推定される変数のスケールについて研究者は何らかの知識を持っているでしょう. 例えば, 成人女性の身長の母平均について切片だけのモデルを推定するなら, 答えは1から3メートルの間のどこかにあるでしょう. こうした情報から弱情報事前分布を構成できます.
同様に, 予測変数が標準スケール(おおよそ平均が0で, 単位分散)のロジスティック回帰なら, 係数の絶対値が5より大きくなることはあまりないでしょう. この場合, \(\mathsf{Normal}(0,5)\)のような弱情報事前分布をそのような係数に設定するのは道理にかなっています.
計算機的にも統計学的にも, 推定を制御するのに弱情報事前分布は役に立ちます. 計算機的には, 解があると期待される量のまわりの曲率を増加させます. これにより, L-BFGSのような勾配に基づく方法でもハミルトニアンモンテカルロのサンプリングでも, 曲面の位置から遠く離れすぎたところに迷い込まないようになります. 統計学的には, 女性の平均身長のような問題で弱情報事前分布はより有効です. というのも, \(\mathsf{Normal}(0,1000)\)のような非常に幅の広い事前分布では, 事前分布の確率質量の大半が, 期待される答えの範囲外にあるようにされるからです. 小さなデータセットでは, そうした事前分布が推測値を覆い隠すことがありえます.
女性の身長の例についてもう一度考えてみましょう. 正則事前分布を定式化する方法の1つは, 上下限のあるスケールに一様事前分布を設定することです. 例えば, 女性の平均身長のパラメータは, 下限が1メートルで上限が3メートルと宣言することができるでしょう. 確かに答えはこの間にあるはずです.
同様に, スケールパラメータの事前分布の下限に0を, 上限に, 10,000のような非常に大きな数を設定する例を見ることも珍しくありません. 3これは, 幅の広い逆ガンマ分布を分散の事前分布に与えて推定するのと, おおまかに言って同じ問題をもたらします. 物理的に完全に制約があるというわけではないパラメーターは固定せずに, 情報事前分布を設定する方がよいでしょう. 女性の身長の場合なら, そのような事前分布は, メートルのスケールで\(\mathsf{Normal}(2,0.5)\)のようになると思われます. この場合, (1,3)の区間に確率質量の95%が集中しますが, 依然としてその範囲外の値も取りえます.
上下限のある事前分布を使う場合は, パラメータの推定値が上下限に, あるいはそれに非常に近い値になっていないか, 当てはめた事後分布を確認すべきです. そうなっていることは, 計算上の問題が発生しているだけではなく, モデルの定式化に問題があることを示しています. そのような場合, 設定した制約がないときにパラメータが当てはまると思われるところまで範囲を広げるか, あるいは境界の値を推定させないようにする事前分布を使うべきです(9.9節を参照).
3 これもBUGSの例題モデル(Lunn et al., 2012)でよくある戦略でした. もう1歩進めて, 数値的に0へとアンダーフローするのを防ぐために下限に0.001のような小さい数を設定することもよくありました.
外れ値に対応したいときの合理的な選択肢は, 取りうる値と期待される範囲のあたりに確率質量の大半が集中するものの, 裾にもかなりの確率質量がまだ残るような事前分布を使うことです. このような状況では通常, コーシー分布を事前分布に使うことが選択されます. そうすれば, 中央値のまわりに確率質量を集中させることができますが, 分散が無限大になるくらいに裾が重くなります.
とくに情報がないのであれば, 回帰係数のパラメータにはコーシー分布の事前分布がデフォルトとして非常に良い選択ですし(Gelman et al., 2008), スケールパラメータには半コーシー分布(Stanでは暗黙的にコードされます)がデフォルトとして良い選択です(Gelman, 2006).
理想的な場合には, 問題についての実質的な情報があって, 弱情報事前分布よりもいっそう強い事前分布を含めることができるでしょう. これは, 事前に実際に実験をして, 別のデータの事後分布として得られるかもしれませんし, メタアナリシスから得られるかもしれませんし, 単に分野の専門家から求められたというものかもしれません. より強度になったというだけで, 弱情報事前分布の長所はすべて当てはまります.
ギブズサンプリングとは異なり, 共役事前分布(すなわち, 事後分布が同族となるような事前分布)を設定する計算上の利点はStanのプログラムにはありません. 4ハミルトニアンモンテカルロは, 対数密度とその導関数だけで動いており, サンプリングも最適化も共役性を使っていません.
4 BUGSとJAGSはともに, ギブズサンプリングによる共役サンプリングをサポートしています. JAGSは共役の範囲を拡張しており, GLMモジュールで利用できます. Stanとは異なり, BUGSとJAGSはともに, 共分散行列や単体のような「制約のある」多変量の量については共役事前分布を使うように制限されています.
線形解析の標準的な手法では, ノイズ項\(\epsilon\)が正規分布するとモデリングします. Stanの視点から言うと, 正規分布のノイズは特別なものではありません. 例えば, ノイズ項にスチューデントのt分布を与えるとロバスト回帰に対応できます. Stanでコーディングするには, サンプリングの分布を以下のように変えます.
data {
...
real<lower=0> nu;
}
...
model {
for (n in 1:N)
y[n] ~ student_t(nu, alpha + beta * x[n], sigma);
}
自由度の定数nu
はデータとして指定します.
結果変数が2値のときは, ロジスティック回帰とプロビット回帰という近い関係にあるモデルのいずれかが使われるでしょう. 一般化線形モデルとしては両者は, (\(-infty\),\(infty\))という線形軸の予測値を, (0,1)という確率の値へと対応させるリンク関数だけが異なります. リンク関数はそれぞれ, ロジスティック関数と標準正規累積分布関数で, ともにシグモイド関数(すなわり, 両者ともS字型)です.
予測変数1つと切片とからなるロジスティック回帰モデルは以下のようにコーディングされます.
data {
int<lower=0> N;
vector[N] x;
int<lower=0,upper=1> y[N];
}
parameters {
real alpha;
real beta;
}
model {
y ~ bernoulli_logit(alpha + beta * x);
}
ノイズパラメータは, 直接指定されるのではなく, ベルヌーイ分布の定式化に組み込まれています.
ロジスティック回帰は一般化線形モデルの1種で, 結果変数が2値で, リンク関数が対数オッズ(ロジット)関数です. これは以下のように定義されます.
\[ \mathrm{logit}(\nu) = \log\left(\frac{\nu}{1 - \nu}\right) \]
リンク関数の逆関数がモデル中にあります.
\[ \mathrm{logit}^{-1}(u) = \frac{1}{1 + \exp(-u)} \]
上のモデルの定式化では, ベルヌーイ分布をロジットでパラメータ化したバージョンを使っています. その定義は以下です.
\[ \mathsf{BernoulliLogit}(y \mid \alpha) = \mathsf{Bernoulli}(y \mid \mathrm{logit}^{-1}(\alpha)) \]
この定式化ではまたベクトル化もおこなわれています. alpha
とbeta
とがスカラーでx
がベクトルなので, alpha + beta * x
はベクトルになるからです. このベクトル化された定式化は, 下のもっと非効率なバージョンと等価です.
for (n in 1:N)
y[n] ~ bernoulli_logit(alpha + beta * x[n]);
ロジットのベルヌーイ分布の部分を展開すると, モデルは等価なまま, より明示的になりますが, より非効率で, 算術的により不安定になります.
for (n in 1:N)
y[n] ~ bernoulli(inv_logit(alpha + beta * x[n]));
別のリンク関数も同様に使えるでしょう. 例えば, プロビット回帰は, 正規累積分布関数を使います. これは以下のように書かれます.
\[ \Phi(x) = \int_{-\infty}^{x}\mathsf{Normal}(y \mid 0, 1)dy \]
標準正規累積分布関数\(\Phi\)は, StanではPhi
関数として実装されています. Stanでプロビット回帰モデルをコーディングするには, ロジスティックモデルのサンプリング文を以下のように変えればよいでしょう.
y[n] ~ bernoulli(Phi(alpha + beta * x[n]));
Stanでは, 標準正規累積分布関数\(\Phi\)の高速な近似がPhi_approx
関数として実装されています. 近似プロビット回帰モデルは以下のようにコーディングされるでしょう.
y[n] ~ bernoulli(Phi_approx(alpha + beta * x[n]));
ロジスティック回帰の形式で, 結果変数が多値になるものもStanでそのままコーディングできます. 例えば, それぞれの出力の変数\(y_{n}\)について, 結果が\(K\)種類の値を取りうるとします. また, \(y_{n}\)に対応する予測変数の\(D\)次元ベクトル\(x_{n}\)があるとします. 係数の事前分布を\(\mathsf{Normal}(0,5)\)とした多項ロジットモデルは以下のようにコーディングされます.
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)
beta[k] ~ normal(0,5);
for (n in 1:N)
y[n] ~ categorical(softmax(beta * x[n]));
}
softmax
関数の定義は42.11節を参照してください. 最後の行をもっと効率的にすると以下のように書けます.
y[n] ~ categorical_logit(beta * x[n]);
categorical_logit
分布は, カテゴリカル分布に似ていますが, パラメータがロジットスケールになります(categorical_logit
の完全な定義は48.5節を参照してください).
最初のループをもっと効率的にするには, 行列beta
をベクトルに変換して最初のループをベクトル化するとよいでしょう.
to_vector(beta) ~ normal(0,5);
上のモデルのdata
ブロックは, サイズK
, N
, D
や結果変数の配列y
に制約をつけず定義しています. データ宣言での制約は, データが読み込まれた時点での(あるいは変換データが定義された時点での)エラーチェックのためのもので, サンプリングが始まる前に行なわれます. データ宣言での制約はまた, モデルの作者の意図をより明示的に示すもので, 可読性が高くなります. 上のモデルの宣言をもっと厳しくするとすると以下のようになります.
int<lower=2> K;
int<lower=0> N;
int<lower=1> D;
int<lower=1,upper=K> y[N];
これら制約の根拠ですが, カテゴリーの数K
は, カテゴリカル分布が使えるためには少なくとも2でなくてはなりません. データ項目の数N
は0となりえますが, 負ではいけません. Rとは違ってStanのfor
ループは常に前進するので, 1:N
というループの範囲は, N
が0に等しいときにはループの内部を実行しないことを保証します. 予測変数の数D
は, beta * x[n]
がsoftmax()
に適切な引数を生成するよう, 少なくとも1でなくてはいけません. カテゴリカル分布の結果変数y[n]
は, 離散サンプリングがうまく定義されるように1
からK
までになくてはなりません.
データ宣言の制約は任意です. 一方, parameters
ブロックで宣言されるパラメータの制約は任意ではありません. すべてのパラメータの値が制約を確実に満たすようにすることが求められます. transformed data
, transformed parameters
, generated quantities
での制約も任意です.
入力の各成分に定数を加えてもsoftmaxは不変ですので, このモデルは一般に, 係数についての適切な事前分布があるときにだけ識別されます.
別の選択肢は, \((K-1)\)次元のベクトルを使って, そのうちのひとつを0に固定することです. 11.2節で, ベクトル中に定数とパラメータとを混在させる方法を議論します. 多項ロジットの場合は, parameters
ブロックは, \((K-1)\)次元ベクトルを使って以下のように再定義されるでしょう.
parameters {
matrix[K - 1, D] beta_raw;
}
それから, モデルで使うパラメータに変換します. まず, transformed data
ブロックをparameters
ブロックの前に加えて, 零値からなる列ベクトルを定義します.
transformed data {
vector[D] zeros;
zeros <- rep_vector(0, D);
}
つづいて, これをbeta_raw
に付け加えて, 係数行列beta
をつくります.
transformed parameters {
matrix[K, D] beta;
beta <- append_col(beta_raw, zeros);
}
rep_vector
の定義は42.6節を, append_col
の定義は42.10節を参照してください.
これは, パラメータとして\(K\)次元ベクトルを使ったモデルとまったく同じというわけではありません. 事前分布が\((K-1)\)ベクトルにのみ適用されるようになったからです. 実用的には, これにより最尤法の解が違うものになり, 事前分布を0のまわりに中央化したときの事後分布もやや異なります. これは回帰係数でよく起こります.
パラメータのベクトル\(\beta\)を, 合計して0になる制約を満たすという意味で中央化して定義すると便利なことがよくあります.
\[ \sum_{k=1}^{K}\beta_{k} = 0 \]
このようなパラメータのベクトルは, 多項ロジット回帰のパラメータのベクトルを識別するのに使われたり(9.5節を参照), IRTモデルの能力パラメータや難易度パラメータ(ただしどちらか一方)に使われることがあります(9.10節を参照).
合計して0になる制約をパラメータのベクトルに課す方法は1つだけではありません. もっとも効率的なのは, 1から\(K-1\)番目までの合計の符号を反転させたものとして\(K\)番目の要素を定義する方法です.
parameters {
vector[K-1] beta_raw;
...
transformed parameters {
vector[K] beta; // 中央化
for (k in 1:(K-1)) {
beta[k] <- beta_raw[k];
}
beta[K] <- -sum(beta_raw);
...
このパラメータ化でbeta_raw
に事前分布を設定すると, 合計して0の制約をつけないパラメータ化でbeta
に同じ事前分布を設定したものと比較して, 事後分布はわずかに異なることになります. とくに, beta_raw
の各要素に単純な事前分布を設定すると, 制約のない\(K\)次元ベクトルbeta
の各要素に同じ事前分布を設定したのと異なる結果が得られます. たとえば, beta
の事前分布に\(\mathsf{Normal}(0,5)\)を設定すると, beta_raw
に同じ事前分布を設定したものと事後分布の最頻値は異なることになるでしょう.
もう1つの方法は効率の点では劣りますが, 対称な事前分布にできます. 単体であるパラメータにオフセットをつけて, スケーリングする方法です.
parameters {
simplex[K] beta_raw;
real beta_scale;
...
transformed parameters {
vector[K] beta;
beta <- beta_scale * (beta_raw - 1.0 / K);
...
beta_raw
は単体なので, 合計すると1になります. これにより, 要素ごとに\(1/K\)を減じると合計が0になることが保証されます(整数演算により0に丸められるのを防ぐため, 1 / K
ではなく, 1.0 / K
という式を使うことに注意しましょう). 単体の要素の大きさには限度がありますから, beta
が, 合計して0となるようなあらゆる値を取るために必要な自由度\(K\)を持つようにするためにはスケーリング因子が必要になります.
このパラメータ化では, ディリクレ分布の事前分布をbeta_raw
に設定することができます. おそらくは一様分布でしょう. そして, 通常は「縮小」のために, 別の事前分布をbeta_scale
に設定します.
\(\beta \sim \mathsf{Normal}(0,\sigma)\)のような事前分布を加えることは, パラメータのベクトル\(\beta\)について, \(\sum_{k=1}^{K}\beta_{k}=0\)となることはないものの, それに近くなるような, 1種の柔らかい中央化を設定することになるでしょう. スカラーの定数\(c\)についての要素ごとの和\(\beta+c\)と\(\beta\)とが同じ尤度を生成する場合のみ, この方法でおおまかに中央化されることが保証されます(IRTモデルではおそらく, 別のベクトル\(\alpha\)があって, \(\alpha-c\)と変換されます). これは, 対称な事前分布を得るためのまた別の方法です.
予測変数\(x_{n} \in \mathbb{R}^{D}\)に対する結果変数\(y_{n} \in {1,\dots,K}\)の順序回帰は, 単一の係数ベクトル\(\beta \in \mathbb{R}^{D}\)と, 切断点の数列\(c \in \mathbb{R}^{K-1}\)により決まります. ただし\(c\)は, \(c_{d} < c_{d+1}\)のように並んでいます. 線形予測子\(x_{n}\beta\)が\(c_{k-1}\)と\(c_{k}\)の間に入るなら, 離散値の結果変数は\(k\)となります. ここでは, \(c_{0} = -\infty\)かつ\(c_{K} = \infty\)と仮定されています. ノイズ項は回帰の形式によって決まります. ここでは, 回帰ロジスティック回帰と回帰プロビット回帰の例を示します.
順序ロジスティック回帰はStanでは, 切断点にordered
データ型を使い, 組込みのordered_logistic
分布によりコーディングできます.
data {
int<lower=2> K;
int<lower=0> N;
int<lower=1> D;
int<lower=1,upper=K> y[N];
row_vector[D] x[N];
}
parameters {
vector[D] beta;
ordered[K-1] c;
}
model {
for (n in 1:N)
y[n] ~ ordered_logistic(x[n] * beta, c);
}
切断点c
のベクトルはordered[K-1]
として宣言します. これにより, c[k]
がc[k+1]
よりも小さいことが保証されます.
切断点に独立に事前分布を割り当てれば, この制約は, 順序の制約を満たす点となるようにうまく同時事前分布を切断します. 都合の良いことに, Stanでは確率は割合で分かればよいので, 正規化項についての制約の効果を計算する必要がありません.
順序プロビットモデルは, 累積ロジスティック分布(inv_logit
)を累積正規分布(Phi
)に変えるだけで, まったく同様にコーディングできるでしょう.
data {
int<lower=2> K;
int<lower=0> N;
int<lower=1> D;
int<lower=1,upper=K> y[N];
row_vector[D] x[N];
}
parameters {
vector[D] beta;
ordered[K-1] c;
}
model {
vector[K] theta;
for (n in 1:N) {
real eta;
eta <- x[n] * beta;
theta[1] <- 1 - Phi(eta - c[1]);
for (k in 2:(K-1))
theta[k] <- Phi(eta - c[k-1]) - Phi(eta - c[k]);
theta[K] <- Phi(eta - c[K-1]);
y[n] ~ categorical(theta);
}
}
ロジスティックモデルも, Phi
をinv_logit
に入れ替えれば, この方法でコーディングできるでしょう. ただし, softmax変換に基づく組込みのエンコーディングの方がより効率的で, 数値的により安定です. Phi(eta - c[k])
の値を一度だけ計算して保存し, その後は保存しておいた値を再利用するようにすると, 少しだけ効率が良くなるでしょう.
もっとも単純なマルチレベルモデルは, \(L\)だけある離散カテゴリー(あるいはレベル)にデータがグループ化されるような階層モデルです. 極端な方法は, 全データを完全にプールして, 回帰係数\(\beta\)のベクトルを共通のものとして推定するというものでしょう. また反対側に極端な方法は, プールはせず, 各レベル\(l\)に固有の係数ベクトル\(\beta_{l}\)を割り当て, 他のレベルとは別に推定するというものでしょう. 階層モデルは中間の解法で, プールの程度は, データと, プールの量についての事前分布とで決まります.
2値の結果変数\(y_{n} \in {0,1}\)がすべて, レベル\(ll_{n} \in {1,\dots,L}\)と関連しているとします. 各結果変数はまた, 予測変数のベクトル\(x_{n} \in \mathbb{R}^{D}\)とも関連しているでしょう. 各レベル\(l\)は, 固有の係数ベクトル\(\beta_{l} \in \mathbb{R}^{D}\)をとります. 階層モデルでは, これもデータから推定される事前分布から係数\(\beta_{l,d} \in \mathbb{R}^{D}\)が抽出されます. この, 階層的に推定される事前分布がプールの量を決定します. 各レベルのデータが非常に似ているなら, 階層事前分布の分散が小さいことを反映して強いプールが行なわれるでしょう. レベル間でデータが違っていれば, 階層事前分布の分散が大きいを反映して弱いプールが行なわれるでしょう.
以下のモデルは, 回帰係数に階層事前分布を設定した階層ロジスティック回帰モデルをコーディングしたものです.
data {
int<lower=1> D;
int<lower=0> N;
int<lower=1> L;
int<lower=0,upper=1> y[N];
int<lower=1,upper=L> ll[N];
row_vector[D] x[N];
}
parameters {
real mu[D];
real<lower=0> sigma[D];
vector[D] beta[L];
}
model {
for (d in 1:D) {
mu[d] ~ normal(0,100);
for (l in 1:L)
beta[l,d] ~ normal(mu[d],sigma[d]);
}
for (n in 1:N)
y[n] ~ bernoulli(inv_logit(x[n] * beta[ll[n]]));
}
標準偏差のパラメータsigma
は, 下限値0の制約をつけて宣言されているので, 暗黙の一様事前分布\((0, \infty)\)を取ります. Stanでは, 事後分布が正則である限り, 非正則の事前分布を許します. とはいっても通常は, すべてのパラメータに情報事前分布, あるいは少なくとも弱情報事前分布をつけるのが有用です. 回帰係数とスケールの事前分布についてのおすすめは9.2節を参照してください.
可能なところでは, サンプリング文をベクトル化すると, 対数確率と導関数の評価が速くなります. 高速化の理由は, ループがなくなったからではなく, 対数確率と勾配計算の下位計算がベクトル化により共有されること, 勾配計算に必要な式木のサイズが減少することによります.
まず最初の最適化として, D
についてのfor
ループをベクトル化します.
mu ~ normal(0,100);
for (l in 1:L)
beta[l] ~ normal(mu,sigma);
beta
はベクトルの配列として宣言されていますので, 式beta[l]
はベクトルを示すことになります. beta
を行列として宣言することもできたでしょうが, ベクトルの配列(あるいは2次元配列)の方が行へのアクセスはより効率的です. 配列, ベクトル, 行列の間での効率性のトレードオフについては第25章にさらに情報があります.
このモデルは, ベルヌーイ分布内で逆ロジットを使用しているのを, ロジットでパラメータ化したベルヌーイ分布に置き換えることで, さらに高速化し, 算術的にもより安定させることができます.
for (n in 1:N)
y[n] ~ bernoulli_logit(x[n] * beta[ll[n]]);
bernoulli_logit
の定義は47.2節を参照してください.
RやBUGSとは異なり, ループや, 配列へのアクセスおよび代入は, Stanでは直接C++に変換されるので高速です. ほとんどの場合, コンテナに配置したり代入したりするコストは, 対数確率と勾配の計算をベクトル化することによる効率の増加でおつりが来ます. ですので, サンプリング文をループさせていた元の定式化よりも下のバージョンの方が高速です.
{
vector[N] x_beta_ll;
for (n in 1:N)
x_beta_ll[n] <- x[n] * beta[ll[n]];
y ~ bernoulli_logit(x_beta_ll);
}
局所変数x_beta_ll
のため, 中括弧で, 新しいスコープを導入しています. あるいは代わりに, model
ブロックの最初で変数を宣言しても構いません.
上のように局所変数への代入を使うと, モデルが読みにくくなる場合があります. そのような場合には, まず読みやすいバージョンでモデルを開発, デバッグし, 単純な定式化でデバッグし終わってはじめて最適化の作業にかかるというようにするのがおすすめです.
事前分布の事前分布は「超事前分布」とも呼ばれます. 超事前分布も, 下位レベルのパラメータの事前分布と同様に扱われるべきです. すなわち, 利用可能なだけの事前分布の情報がすべて使われるべきです. 超事前分布は, ほんの少数の下位レベルのパラメータにしか適用されないことが多いので, 事後分布が正則であることと, 事前分布の裾の重さに統計的にも計算的にも過度に敏感ではないことの両方が確かかどうか注意を払う必要があります.
階層モデルの設定における最尤推定(MLE)の基本的な問題は, 階層事前分布の分散が小さくなって, 階層事前分布の平均のまわりに値が集まり, 全体の密度が限度なく大きくなることです. 例として, \(x_{n} \in \mathbb{R}^{K}\)についての\(y_{n} \in \mathbb{R}\)の単純な階層線形回帰(事前分布の平均を固定)を考えます. 定式化は以下のとおりです.
\[ \begin{array}{rl}y_{n} &\sim \mathsf{Normal}(x_{n}\beta, \sigma)\\ \beta_{k} &\sim \mathsf{Normal}(0, \tau)\\ \tau &\sim \mathsf{Cauchy}(0, 2.5) \end{array} \]
この場合で, \(\tau \rightarrow 0\)かつ\(\beta_{k} \rightarrow 0\)となるとき, 事後密度
\[ p(\beta, \tau, \sigma \mid y, x) \propto p(y \mid x, \beta, \tau, \sigma) \]
は限度なく大きくなります. 図21.1にNealのじょうご密度の図がありますが, これと同様の挙動を示します.
この場合明らかに, \(\beta\), \(\tau\), \(\sigma\)に最尤推定値はありません. したがって, 事後の最頻値を推測に使うなら, モデルを変えなければなりません. Chung et al. (2013)が勧めているのは, 以下のように事前分布にガンマ分布を使用する方法です.
\[ \tau \sim \mathsf{Gamma}(2, 1/A) \] (訳注: 原文では'\(\sigma\)'ですが, '\(\tau\)'の誤りと思われます)
\(A\)には, \(A=10\)のようにかなり大きな値を与えます.
項目反応理論(Item-response theory: IRT)は, 何人かの生徒がそれぞれ, 1つ以上のテスト問題群に答えるという状況をモデリングします. このモデルは, 生徒の能力と, 問題の難易度というパラメータに基づいています. より明瞭なモデルでは, 問題の識別性と, 当て推量で正解になる確率についてのパラメータもあります. 階層IRTモデルの入門用教科書についてはGelman and Hill (2007)を参照してください. また, さまざまなIRTモデルのBUGSによるコーディングについてはCurtis (2010)を参照してください.
IRTモデルに渡されるデータは, 全生徒が全問題に解答するとは限らないことを考慮すると, 以下のように宣言されるでしょう.
data {
int<lower=1> J; // 生徒の数
int<lower=1> K; // 問題の数
int<lower=1> N; // 観測の数
int<lower=1,upper=J> jj[N]; // 観測nの生徒
int<lower=1,upper=K> kk[N]; // 観測nの問題
int<lower=0,upper=1> y[N]; // 観測nの正誤
}
この宣言では, 生徒-問題の組み合わせが全部でN
あり, 1:N
の各n
が, 2値の観測値y[n]
のインデックスで, y[n]
は, 生徒jj[n]
が問題kk[n]
に正答したかどうかを示します.
超パラメータの事前分布は, この節の後では簡単のためハードコーディングします. とはいえ, Stanではもっと柔軟にデータとしてコーディングできます.
1PL項目反応モデルは, Raschモデルともいわれ, 問題についての1つのパラメータ(1P)を持ち, ロジスティックリンク関数(L)を使います.
モデルのパラメータは以下のように宣言されます.
parameters {
real delta; // 平均の生徒の能力
real alpha[J]; // 生徒jの能力 - 平均能力
real beta[K]; // 問題kの難易度
}
パラメータalpha[j]
は生徒j
の能力の係数, beta[k]
は問題k
の難易度の係数です. ここで使われている非標準のパラメータ化では, 切片項delta
も含みます. これは, 平均的な生徒の平均的な問題への反応をあらわします. 5モデル自体は以下のようになります.
model {
alpha ~ normal(0,1); // 情報のある, 真値の事前分布
beta ~ normal(0,1); // 情報のある, 真値の事前分布
delta ~ normal(.75,1); // 情報のある, 真値の事前分布
for (n in 1:N)
y[n] ~ bernoulli_logit(alpha[jj[n]] - beta[kk[n]] + delta);
}
このモデルはロジットでパラメータ化したベルヌーイ分布を使っています.
\[ \mathsf{bernoulli\_logit}(y \mid \alpha) = \mathsf{bernoulli}(y \mid \mathsf{logit}^{-1}(\alpha)) \]
このモデルを理解する鍵は, bernoulli_logit
分布の中の項です. 以下の式に従います.
\[ \Pr[y_{n} = 1] = \mathrm{logit}^{-1}(\alpha_{jj[n]} - \beta_{kk[n]} + \delta) \]
このモデルでは, 事前分布を設定しないと加法的な識別可能性の問題が発生します. 例えば, \(\alpha_{j}\)と\(\beta_{k}\)の両方に\(\xi\)を加えると, 予測値が同じになります. \(\alpha\)と\(\beta\)に平均を0とした事前分布を設定すると, パラメータが識別できます. 識別可能性の問題と, 識別可能にする他の手法についてはGelman and Hill (2007)を参照してください.
テスト用に, Stanとともに配布されているIRT 1PLモデルは, Rでデータをシミュレートするために使われる実際のデータ生成過程に合うような情報のある事前分布を使っています(このシミュレーションのコードはモデルと同じディレクトリに入っています). 実際に利用するにはほとんどの場合は現実的ではありませんが, Stanの推定が有効であることは分かります. 事前分布の裾を重くして簡単な感度分析を行なうと, 400人の生徒に100問の問題で, 25%だけランダムに欠測するとしても, 事後分布は事前分布にかなり敏感です. 実際に使用するときには, 他のパラメータと同様に事前分布も階層的にすべきです. これは次の節で記述します.
5 Gelman and Hill (2007)は\(\delta\)項を, 生徒の能力の分布における位置のパラメータと等価に扱っています.
前の節で記述した単純な1PLモデルをこの節では, 問題にどのくらいのノイズがあるかをモデリングする識別パラメータを加え, 問題の難易度と識別パラメータにマルチレベルの事前分布を加えることで一般化します. モデルのパラメータは以下のように宣言されます.
parameters {
real mu_beta; // 平均の生徒の能力
real alpha[J]; // jの能力 - 平均
real beta[K]; // kの難易度
real<lower=0> gamma[K]; // kの識別性
real<lower=0> sigma_beta; // 難易度のスケール
real<lower=0> sigma_gamma; // 識別性のスケール
}
モデルの定義を見た後の方がパラメータはよくわかるでしょう.
model {
alpha ~ normal(0,1);
beta ~ normal(0,sigma_beta);
gamma ~ lognormal(0,sigma_gamma);
mu_beta ~ cauchy(0,5);
sigma_alpha ~ cauchy(0,5);
sigma_beta ~ cauchy(0,5);
sigma_gamma ~ cauchy(0,5);
for (n in 1:N)
y[n] ~ bernoulli_logit(gamma[kk[n]]
* (alpha[jj[n]] - (beta[kk[n]] + mu_beta)));
}
1PLモデルに似ていますが, 追加のパラメータgamma[k]
で, 問題k
の識別能力をモデリングしています. gamma[k]
が1より大きければ, ランダムに正答になる可能性は低くなり, より正誤(反応)が能力を如実に反映するようになります. パラメータgamma[k]
は正値に制約されています. これは, 能力が低い生徒の方により簡単な問題はないとするものです. そのような問題を耳にしないわけではありませんが, IRTモデルが利用されるような試験ではほとんどの場合, そのような問題は除かれる傾向にあります.
生徒の能力alpha
に標準正規分布の事前分布を与えるようなモデルに, ここではパラメータ化しています. これは, このパラメータの位置とスケールの両方を識別可能にするためです. そうしなければ, その両方が識別不可能になるでしょう. 識別可能性については24章でさらに議論します. このモデルでは, 難易度と識別性のパラメータbeta
とgamma
のスケールはパラメータとし, 階層的に定めています. beta
とgamma
には以下のように階層的ではない, 弱情報事前分布を与えてもよいでしょう.
beta ~ normal(0,5);
gamma ~ lognormal(0,2);
ポイントは, alpha
がスケールと位置を決定し, beta
とgamma
は変動できるようにしていることです.
パラメータbeta
には, ここでは中央化しないパラメータ化を行なっています. パラメータmu_beta
がbeta
の平均の位置を与えています. あるいはまた, 以下のようにもできるでしょう.
beta ~ normal(mu_beta, sigma_beta);
および
y[n] ~ bernoulli_logit(gamma[kk[n]] * (alpha[jj[n]] - beta[kk[n]]));
中央化しないパラメータ化は階層モデルではより効率的になる傾向にあります. 中央化しない再パラメータ化についてもっと知るには27.6節を参照してください.
切片の項mu_beta
はそれ自身は階層的にモデリングできません. そのため, 弱情報事前分布\(\mathsf{Cauchy}(0,5)\)を与えています. 同様に, スケールの項sigma_alpha
およびsigma_beta
, sigma_gamma
には半コーシー分布の事前分布を与えています. 半コーシー分布の切断は暗黙のものです. 明示的な切断は必要ありません. これは, 対数確率は割合だけ計算すればよく, スケールの変数は宣言により(0,\(\infty\))に制約されているからです.
(階層)事前分布の応用の1つに, パラメータ群のスケールあるいは位置, またはその両方を識別させるということがあります. 例えば, 前の節で議論したIRTモデルでは, 位置とスケールの両方に識別不能性がありました. 一様事前分布を使うと, スケールと位置の両方の項でパラメータは変動するでしょう. これが推定にもたらす問題については, 24.1節に簡単な例がありますので, 参照してください.
生徒の能力のような1群の係数に標準正規分布(すなわち\(\mathsf{Normal}(0,1)\))の事前分布をを与えると, 識別不能性は解決されます. 生徒の能力に標準正規分布の事前分布を与えると, IRTモデルは識別され, 生徒の能力のパラメータについての1群の推定値が事後分布により生成されます. その標本平均は0に近く, 標本分散は1に近い値となるでしょう. 問題の難易度と識別性のパラメータには, 広がった, 理想的には階層的な事前分布を与えるべきです. そうすると, 生徒の能力のパラメータとの相対的な位置とスケールにより, これらパラメータは識別されるでしょう.
事前分布により識別可能になる別の例として, 線形回帰における共線性の場合があります. 線形回帰では, 2つの予測変数が共線的(すなわち一方が他方の線形関数となっている)ならば, 事後分布ではそれらの係数の相関係数は1(または-1)でしょう. これは識別不能になります. 係数の事前分布を正規分布とすることで識別できるようになる場合があります. そのような事前分布を使うと, 例えば, 全く同じ値をもつ説明変数が2つある場合に(共線性があることは明らかです), 最も尤度が高くなるような2つの係数の値は, 片方の説明変数だけを使って推定して得た値の半分の値となります.
ロジスティック回帰では, 結果変数の値が1で予測変数が正のときや, 結果変数の値が0で予測変数が負のときには, こうした予測変数の係数の最尤推定値は無限に発散します. 係数に事前分布を与えることでこうした発散を制御することができます. これにより, 推定値を0に向けて「縮小」することで, 事後分布についてモデルは識別可能になるでしょう.
同様の問題は非正則平坦事前分布からのサンプリングでも発生します. このときのサンプラーは非常に大きな値を抽出しようとするでしょう. 事前分布を与えることで, 事後分布は有限の値のまわりに集中し, サンプリングはうまくいくでしょう.
階層回帰モデルでは(他の状況でもありますが), いくつかの個体レベルの変数に階層事前分布を割り当てることがあります. 例えば, 複数の変動する切片や傾きを含むモデルでは, 多変量の事前分布を割り当てることでしょう.
例として, 人を個体レベルとして, 収入を結果変数に, 教育水準と年齢を予測変数とし, 州などの地理区分を群としましょう. 切片のほか, 教育水準と年齢の効果も州ごとに変わりうるとします. さらに, 州ごとの収入や失業水準の平均といった, 州レベルの予測変数もあるとしましょう.
Gelman and Hill (2007)の13章・17章では, \(N\)個体が\(J\)群にまとめられる階層モデルについて議論しています. 各個体は, 長さ\(K\)の列ベクトル\(x_{n}\)からなる予測変数を持ちます. 記法の統一のため, \(x_{n,1} = 1\)として, 切片を「1に固定された予測変数」にかかる係数として扱います. 群への所属をコード化するため, 個体\(n\)は群\(jj[n] \in 1:J\)に属するとしています. 各個体\(n\)はまた, 実数値の観測結果\(y_{n}\)を持ちます.
このモデルは, 群によって切片と係数が変動する線形回帰となりますので, \(\beta_{j}\)は, 群\(j\)についての\(K\)次元ベクトルの係数です. 個体\(n\)についての尤度関数は以下のとおりです.
\[y_{n} \sim \mathsf{Normal}(x_{n}\beta_{jj[n]}, \sigma) \quad \text{すべての} n \in 1:N \text{について}\]
GelmanとHillは, 係数ベクトル\(\beta_{j}\)を, 平均ベクトル\(\mu\)と共分散行列\(\Sigma\)を持つ多変量分布から抽出されるとモデリングしています.
\[\beta_{j} \sim \mathsf{MultiNormal}(\mu, \Sigma) \quad\text{すべての} j \in 1:J \text{について}\]
以下, GelmanとHillのフルモデルについて議論しますが, このモデルでは\(\mu\)をモデリングするのに群レベルの予測変数を使っています. ここでは, \(\mu\)は単純なベクトルのパラメータと仮定します.
階層モデリングでは, 群レベルの平均ベクトル\(\mu\)と共分散行列\(\Sigma\)自身に事前分布を与えなくてはなりません. 群レベルの平均ベクトルには, 独立した係数に合理的な弱情報事前分布を与えることができます.
\[ \mu_{j} \sim \mathsf{Normal}(0, 5) \]
もちろん, 係数\(\beta_{j,k}\)の期待値について知識があれば, その情報を\(\mu_{k}\)の事前分布に取り入れることができます.
共分散行列の事前分布についてGelmanとHillは, 対角成分の標準偏差のスケールを分離した逆Wishart分布を使うことを提案しています. それを選ぶ理由は主として, 多変量の尤度関数に対して共役であり, Gibbsサンプリングが単純になるという利便性にあります.
Stanでは, 多変量の事前分布に共役の制限はありませんし, 実際のところやや違った手法をお勧めしています. GelmanとHillと同様, 事前分布をスケールと行列に分解しますが, 実際の変数のスケールと相関行列に基づいた, もっと自然な方法でできます. 具体的には以下のように定義します.
\[ \Sigma = \mathrm{diag\_matrix}(\tau)\Omega\mathrm{diag\_matrix}(\tau) \]
ここで\(\Omega\)は相関行列で, \(\tau\)は係数のスケールのベクトルです.
スケールのベクトル\(\tau\)の要素には, スケールの事前分布として合理的ならどのようなものでも与えることができますが, 下の, 小さいスケールの半コーシー分布のような弱情報のものをお勧めします.
\[\tau_{k} \sim \mathsf{Cauchy}(0, 2.5) \quad\text{すべての} k \in 1:J \text{について, かつ} \tau_{k} > 0\]
事前平均については, 群間の係数の変動のスケールについて情報があるなら, \(\tau\)の事前分布に取り入れるべきでしょう. 交換可能な係数が多数あるときは, \(\tau\)自体の要素自体に(おそらく切片は除いて)階層事前分布が与えらえるでしょう.
最後に, 相関行列\(\Omega\)には, 形状パラメータ\(\nu > 1\)のLKJ事前分布を与えることをお勧めします.
\[ \Omega \sim \mathsf{LKJcorr}(\nu) \]
LKJ相関分布は60.1節で定義していますが, モデリングの基本的なアイデアは, \(\nu\)が大きくなるにつれ, 事前分布が単位相関行列のまわりに集中するようになる(すなわち\(\beta_{j}\)の要素間の相関を低くする)ということです. \(\nu=1\)のとき, LKJ相関分布は相関行列に対して一様分布(訳注: 原文では'identity distribution'ですが, 'uniform distribution'の誤りと思われます)を設定するのと等価になります. したがって, LKJ事前分布は, パラメータ\(\beta_{j}\)間の相関について期待する量を制御するのに用いることがあります.
GelmanとHillのモデルを完成させるため, 各群\(j \in 1:J\)には, 群レベルの予測変数\(u_{j}\)の\(L\)次元の行ベクトルを与えるとします. すると\(\beta_{j}\)の事前平均は, \(L\)次元の係数ベクトル\(gamma\)を使って, それ自体を回帰としてモデリングできます. 群レベルの係数の事前分布は以下のようになります.
\[ \beta_{j} \sim \mathsf{MultiNormal}(u_{j}\gamma, \Sigma) \]
群レベルの係数\(gamma\)には, それ自体に弱情報事前分布を独立に与えてもよいでしょう.
\[ \gamma_{l} \sim \mathsf{Normal}(0, 5) \]
いつもと同様に, 群レベルの平均についての情報は事前分布に取り入れるべきでしょう.
群レベルの係数についての多変量の事前分布と, 群レベルの事前平均を与えた完全な階層モデルのStanのコードは以下のように定義されます.
data {
int<lower=0> N; // 個体の数
int<lower=1> K; // 個体の予測変数の数
int<lower=1> J; // 群の数
int<lower=1> L; // 群の予測変数の数
int<lower=1,upper=J> jj[N]; // 個体が属している群
matrix[N,K] x; // 個体の予測変数
row_vector[L] u[J]; // 群の予測変数
vector[N] y; // 結果変数
}
parameters {
corr_matrix[K] Omega; // 事前分布の相関
vector<lower=0>[K] tau; // 事前分布のスケール
matrix[L,K] gamma; // 群の係数
vector[K] beta[J]; // 群ごとの個体の係数
real<lower=0> sigma; // 予測誤差のスケール
}
model {
tau ~ cauchy(0,2.5);
Omega ~ lkj_corr(2);
to_vector(gamma) ~ normal(0, 5);
{
row_vector[K] u_gamma[J];
for (j in 1:J)
u_gamma[j] <- u[j] * gamma;
beta ~ multi_normal(u_gamma, quad_form_diag(Omega, tau));
}
{
vector[N] x_beta_jj;
for (n in 1:N)
x_beta_jj[n] <- x[n] * beta[jj[n]];
y ~ normal(x_beta_jj, sigma);
}
}
超事前分布の共分散行列は, 2次形式でコード中で暗黙に定義されています. これは, 相関行列Omega
とスケールのベクトルtau
の方が出力を調べる目的にはより自然だからです. Sigma
を出力するためには, 変換パラメータとして定義します. 関数quad_form_diag
は, quad_form_diag(Sigma,tau)
がdiag_matrix(tau) * Sigma * diag_matrix(tau)
と等価になるように定義されています. ここで, diag_matrix_(tau)
は, 対角成分がtau
となり, それ以外が0の行列を返します. ただし, quad_form_diag
を使うバージョンの方が高速のはずです. 特別な行列演算についてさらに知るには42.2節を参照してください.
上のStanプログラムのコードでは, 予測変数x[n]
と群レベルの係数beta[jj[n]]
を使って, x_beta_jj[n]
という平均を記述するベクトルを組み立てていました. x_beta_jj
が局所変数として宣言できるように, その周りに中括弧でブロックを定義しました. このベクトル化したコードは, より単純な, ベクトル化していない次のコードと等価です.
for (n in 1:N)
y[n] ~ normal(x[n] * beta[jj[n]], sigma);
上にあるStanプログラムのコードではまた, 結果変数の平均と多変量正規分布の平均とについてベクトル(の配列)を組み立てています. 解いて微分する必要のある線形系の数を減らすことで, 非常に顕著な高速化が可能になります.
{
matrix[K,K] Sigma_beta;
Sigma_beta <- quad_form_diag(Omega, tau);
for (j in 1:J)
beta[j] ~ multi_normal((u[j] * gamma)', Sigma_beta);
}
この例では, 共分散行列Sigma_beta
は, 二次形式の計算を\(J\)回繰り返さなくても良いように局所変数として定義されています. このベクトル化は, 次の節のコレスキー因子による最適化と組み合わせることもできます.
多変量正規密度と, 相関行列に対するLKJ事前分布はともに, 行列パラメータを因子分解する必要があります. これは, 前の節のようにベクトル化すると, 各密度について1度だけ行われることが保証されます. 効率性の点でも数値的な安定性の点でももっと良い解法は, 中央化しないパラメータ化の多変量版を使って, 相関行列のコレスキー因子により直接的にモデルをパラメータ化することです. 前の節のモデルについて, プログラム中の完全な行列の事前分布の部分を, 等価なコレスキー分解した事前分布に置き換えると以下のようになります.
parameters {
matrix[K,J] z;
cholesky_factor_corr[K] L_Omega;
...
transformed parameters {
matrix[J,K] beta;
beta <- u * gamma + (diag_pre_multiply(tau,L_Omega) * z)';
}
model {
to_vector(z) ~ normal(0,1);
L_Omega ~ lkj_corr_cholesky(2);
...
新しいパラメータL_Omega
は, 元の相関行列Omega
のコレスキー因子です. つまり, 以下のようになっています.
Omega = L_Omega * L_Omega'
事前分布のスケールのベクトルtau
は変化ありません. さらに, コレスキー因子にスケールを左から掛ける(pre-multiplying)ことで, 最終的な共分散行列のコレスキー因子が生成されます.
Sigma_beta
= quad_form_diag(Omega,tau)
= diag_pre_multiply(tau,L_Omega) * diag_pre_multiply(tau,L_Omega)'
ここで, a
を対角成分とした対角行列を作って左から掛ける複合演算は以下のように定義されています.
diag_pre_multiply(a,b) = diag_matrix(a) * b
新しい変数z
は行列として宣言されており, 独立な標準正規分布の事前分布を与えられています. ここで, to_vector
は行列をベクトルにする演算で, ベクトル化された引数に1変量の正規密度を与えるのに使うことができます. 共分散行列のコレスキー因子にz
を掛けて, 平均(u*gamma)'
を加えることで, 元のモデルと同じ分布となるbeta
が生成されます.
データ宣言は省略しますが, 前と同じモデルを最適化したものは以下のようになります.
parameters {
matrix[K,J] z;
cholesky_factor_corr[K] L_Omega;
vector<lower=0>[K] tau; // 事前分布のスケール
matrix[L,K] gamma; // 群の係数
real<lower=0> sigma; // 予測誤差のスケール
}
transformed parameters {
matrix[J,K] beta;
beta <- u * gamma + (diag_pre_multiply(tau,L_Omega) * z)';
}
model {
vector[N] x_beta_jj;
for (n in 1:N)
x_beta_jj[n] <- x[n] * beta[jj[n]]';
y ~ normal(x_beta_jj, sigma);
tau ~ cauchy(0,2.5);
to_vector(z) ~ normal(0,1);
L_Omega ~ lkj_corr_cholesky(2);
to_vector(gamma) ~ normal(0,5);
}
Stanのモデルは, モデル中の任意の未知量の値を「予測」するのに使うことができます. 予測が将来についてなら「フォアキャスト」と呼ばれますし, 気候の再現や宇宙論であるように, 過去についてなら「バックキャスト」と呼ばれることがあります(「フォア」の対義語についての書き手の感覚により, 「アフトキャスト」や「ハインドキャスト」, 「アンテキャスト」とも呼ばれます).
以下の線形回帰は単純な例ですが, まさに最初の例と同じ設定になっています. すなわち, N
個の観測値y
と, 長さN
の予測変数のベクトルx
を使って, 係数beta
を推定します. このモデルのパラメータと, 観測値のモデルは前とまったく同じです.
予測のためには, 予測の数N_new
とその予測変数の行列x_new
を与える必要があります. 予測値自身はパラメータy_new
としてモデリングされています. 新しい結果変数のベクトルy_new
と予測変数の行列x_new
がありますが, 予測値のモデル文は観測値とまったく同じです.
data {
int<lower=1> K;
int<lower=0> N;
matrix[N,K] x;
vector[N] y;
int<lower=0> N_new;
matrix[N_new, K] x_new;
}
parameters {
vector[K] beta;
real<lower=0> sigma;
vector[N_new] y_new; // 予測値
}
model {
y ~ normal(x * beta, sigma); // 観測モデル
y_new ~ normal(x_new * beta, sigma); // 予測モデル
}
可能であれば, generated quantities
ブロックを使うのが予測値を生成するのに最も効率的な方法です. これにより正則なモンテカルロ(マルコフ連鎖モンテカルロではありません)の推定値が得られます. これはiterationごとに, はるかに効率的にサンプルを作ることができます.
...上のデータ...
parameters {
vector[K] beta;
real<lower=0> sigma;
}
model {
y ~ normal(x * beta, sigma);
}
generated quantities {
vector[N_new] y_new;
for (n in 1:N_new)
y_new[n] <- normal_rng(x_new[n] * beta, sigma);
}
ここでは, データは前と同じですが, パラメータy_new
が新しく生成量(generated quantity)として宣言されています. また, 予測モデルはmodel
から取り除かれ, 正規分布から擬似乱数を使って抽出されるようになっています.
ほとんどの回帰の設定では, 1変量の観測値(スカラー値や論理値, カテゴリカル値, 順序値, 計数値など)をモデリングします. 多項回帰も, カテゴリカル回帰の繰り返しにすぎません. これとは対照的に, 各観測値が多変量の場合の回帰をこの節では議論します. 複数の結果変数を回帰の設定で関連づけるために, それらの誤差項に共分散構造を与えます.
この節では2つの場合について考えます. 連続の多変量の量についての見かけ上無関係な回帰と, 論理値の多変量の量についての多変量プロビット回帰です.
最初に考えるモデルは, 計量経済学の「見かけ上無関係な」回帰(seemingly unrelated regression: SUR)です. いくつかの線形回帰が予測変数を共有し, 独立な誤差ではなく共分散誤差構造を使います(Zellner, 1962; Greene, 2011).
このモデルは回帰として簡単に書けます.
\[ \begin{array}{rl} y_{n} &= x_{n}\beta + \epsilon_{n}\\ \epsilon_{n} &\sim \mathsf{MultiNormal}(0, \Sigma) \end{array} \]
ここで, \(x_{n}\)は\(J\)次元の予測変数の行ベクトル(\(x\)は\((N \times J)\)行列です), \(y_{n}\)は\(K\)次元の観測値のベクトル, \(\beta\)は回帰係数の\((K \times J)\)行列(ベクトル\(\beta_{k}\)は, 結果変数のベクトルの\(k\)番目の要素に関する係数を収容します), \(\Sigma\)は誤差を決める共分散行列です. いつものように切片は, 1の値の列として\(x\)の中に含めることができます.
基本的なStanのコードは単純です(とはいえ, 相関に対してLKJ事前分布を使用してもっと最適化したコードをその後で示します).
data {
int<lower=1> K;
int<lower=1> J;
int<lower=0> N;
vector[J] x[N];
vector[K] y[N];
}
parameters {
matrix[K,J] beta;
cov_matrix[K] Sigma;
}
model {
vector[K] mu[N];
for (n in 1:N)
mu[n] <- beta * x[n];
y ~ multi_normal(mu, Sigma);
}
効率性のため, 平均のベクトルの配列を前もって計算し, 同じ共分散行列は共有して, 多変量正規分布はベクトル化しています.
9.12節での助言にしたがって, 回帰係数の事前分布には正規分布の弱情報事前分布を, 相関にはLKJ事前分布を, 標準偏差には半コーシー分布の事前分布を設定しましょう. 共分散構造は, 効率性と算術的な安定性のため, コレスキー因子によりパラメータ化されます.
...
parameters {
matrix[K,J] beta;
cholesky_factor_corr[K] L_Omega;
vector<lower=0>[K] L_sigma;
}
model {
vector[K] mu[N];
matrix[K,K] L_Sigma;
for (n in 1:N)
mu[n] <- beta * x[n];
L_Sigma <- diag_pre_multiply(L_sigma, L_Omega);
to_vector(beta) ~ normal(0, 5);
L_Omega ~ lkj_corr_cholesky(4);
L_sigma ~ cauchy(0, 2.5);
y ~ multi_normal_cholesky(mu, L_Sigma);
}
共分散行列のコレスキー因子はここでは局所変数として再構築され, 相関行列のコレスキー因子によりスケーリングされてモデルで使われています. 行列beta
をベクトルに変換することにより1度にまとめて回帰係数に事前分布を設定しています.
必要なら, generated quantities
ブロックで, コレスキー因子から完全な相関または共分散行列を再構築してもよいでしょう.
多変量プロビットモデルは, 見かけ上無関係な回帰の結果変数に階段関数を適用することにより一連の論理値の変数を生成します.
観測値\(y_{n}\)は, 論理値(0を偽, 1を真とコーディングします)からなる\(D\)次元ベクトルです. \(y_{n}\)の値は, 見かけ上無関係な回帰モデル(前の節を参照)から抽出される潜在変数\(z_{n}\)によって決まります.
\[ \begin{array}{rl} z_{n} &= x_{n}\beta + \epsilon_{n}\\ \epsilon_{n} &\sim \mathsf{MultiNormal}(0, \Sigma) \end{array} \]
次に, 階段関数を適用して, 以下で定義される要素からなる論理値の\(D\)次元ベクトル\(y_{n}\)を生成します. (訳注: 原文では'\(K\)-vector \(z_n\)とありますが, 誤りと思われるので上のように修正しています. 以下の式でも\(k\)を\(d\)に修正しています. )
\[y_{n,d} = \mathrm{I}(z_{n,d} > 0)\]
ここで, I()は指示関数で, 引数が真なら1の, そうでなければ0の値をとります.
見かけ上無関係な回帰モデルの場合とは異なり, ここでは共分散行列\(\Sigma\)は単位標準偏差をとります(すなわち, 相関行列になります). 通常のプロビット回帰やロジスティック回帰と同様に, スケールが変動するとモデル(スケールではなく, 0についての切断点によってのみ定義されています)が識別不能になります(Greene (2011)を参照).
多変量プロビットモデルはStanでは, Albert and Chib (1993)によって導入されたトリックを使ってコーディングできます. この方法では, 基礎となる連続値のベクトル\(y_{n}\)が切断パラメータとしてコーディングされます. Stanでのこのモデルのコーディングの鍵は, 対応する\(y\)の値が0か1かにもとづいて, 潜在ベクトル\(z\)を2つに分けて宣言することです. さもないと, このモデルは前の節の見かけ上無関係な回帰モデルと同じになってしまいます.
最初に, 整数の2次元配列についての合計関数を導入します. これは, \(y\)の中にどれだけの1が合計してあるのかを計算するのに役立ちます.
functions {
int sum(int[,] a) {
int s;
s <- 0;
for (i in 1:size(a))
s <- s + sum(a[i]);
return s;
}
}
この関数はちょっとしたことですが, Stanには組み込まれていませんし, モデリングに集中できるように独自の関数として定義しておくと, このモデルの残りの部分をわかりやすくします.
データ宣言ブロックは, 見かけ上無関係な回帰ととてもよく似ていますが, 観測値y
は今回は, 0か1かに制約された整数です.
data {
int<lower=1> K;
int<lower=1> D;
int<lower=0> N;
int<lower=0,upper=1> y[N,D];
vector[K] x[N];
}
データの宣言の後にはtransformed data
ブロックがあります. これは, データの配列y
を真か偽かの成分に分けるというただ1つの目的のために必要になったものです. これにより, transformed parameters
ブロックでz
が容易に再構築できるようにインデックスの状態を把握します.
transformed data {
int<lower=0> N_pos;
int<lower=1,upper=N> n_pos[sum(y)];
int<lower=1,upper=D> d_pos[size(n_pos)];
int<lower=0> N_neg;
int<lower=1,upper=N> n_neg[(N * D) - size(n_pos)];
int<lower=1,upper=D> d_neg[size(n_neg)];
N_pos <- size(n_pos);
N_neg <- size(n_neg);
{
int i;
int j;
i <- 1;
j <- 1;
for (n in 1:N) {
for (d in 1:D) {
if (y[n,d] == 1) {
n_pos[i] <- n;
d_pos[i] <- d;
i <- i + 1;
} else {
n_neg[j] <- n;
d_neg[j] <- d;
j <- j + 1;
}
}
}
}
}
変数N_pos
とN_neg
には, 観測値y
の真(1)の数と偽(0)の数とが入ります. そのループではそれから, 真と偽の値についての一連のインデックスで4つの配列を埋めます.
パラメータは以下のように宣言されます.
parameters {
matrix[D,K] beta;
cholesky_factor_corr[D] L_Omega;
vector<lower=0>[N_pos] z_pos;
vector<upper=0>[N_neg] z_neg;
}
ここには, 回帰係数beta
と, 相関行列のコレスキー因子L_Omega
が含まれます. 共分散行列は単位スケール(すなわち相関行列です. 上を参照)なので, 今回はスケーリングはありません.
パラメータ宣言の重要なところは, 潜在実数変数z
が真のみの成分と, 偽のみの成分とに分解されていることです. そのサイズは, 便利なことにtransformed data
ブロックで計算されています. transformed data
ブロックの実際の仕事は, transformed parameters
ブロックでz
を再構築できるようにすることでした.
transformed parameters {
vector[D] z[N];
for (n in 1:N_pos)
z[n_pos[n], d_pos[n]] <- z_pos[n];
for (n in 1:N_neg)
z[n_neg[n], d_neg[n]] <- z_neg[n];
}
ここまで来るとモデルは単純で, 見かけ上無関係な回帰とかなりよく似ています.
model {
L_Omega ~ lkj_corr_cholesky(4);
to_vector(beta) ~ normal(0, 5);
{
vector[D] beta_x[N];
for (n in 1:N)
beta_x[n] <- beta * x[n];
z ~ multi_normal_cholesky(beta_x, L_Omega);
}
}
モデルがこのように単純になったのは, z
についてのAlbertとChib式の制約のおかげです.
最後に, 相関行列自体が必要なら, generated quantities
ブロックで戻すことができます.
generated quantities {
corr_matrix[D] Omega;
Omega <- multiply_lower_tri_self_transpose(L_Omega);
}
もちろん, 前の節の見かけ上無関係な回帰でも同じことができるでしょう.
時系列データは, 時間軸に沿って得られるデータです. この章では2種類の時系列モデルを紹介します. 1つは, 自己回帰および移動平均モデルといった回帰に似たモデル, もう1つは隠れマルコフモデルです.
18章ではガウス過程を紹介しますが, これも時系列(と空間)データに使えるでしょう.
正規ノイズの1次自己回帰モデル(AR(1))では, 各点\(y_{n}\)は次式のように生成される系列\(y\)にまとめられます.
\[y_{n} \sim \mathsf{Normal}(\alpha + \beta y_{n-1}, \sigma)\]
すなわち, \(y_{n}\)の期待値は\(\alpha + \beta y_{n-1}\)で, ノイズの大きさは\(\sigma\)です.
傾き(\(\beta\)), 切片(\(\alpha\)), ノイズのスケール(\(\sigma\))のパラメータに非正則平坦一様分布を設定するなら, AR(1)モデルのStanプログラムは以下のようになります.
data {
int<lower=0> N;
vector[N] y;
}
parameters {
real alpha;
real beta;
real<lower=0> sigma;
}
model {
for (n in 2:N)
y[n] ~ normal(alpha + beta * y[n-1], sigma);
}
最初の観測データ点y[1]
はここではモデル化されていません. これは条件となるものが何もないからです. そのかわり, y[1]
はy[2]
の条件となっています. このモデルではまたsigma
に非正則事前分布を使っていますが, もしy
の時間的変化のスケールに情報があるのなら, 問題なく情報事前分布を加えることができます. あるいは, y
のスケールに大ざっぱな知識があるのであれば弱情報事前分布で推定を良くすることもできます.
おそらく少し読みにくくはなりますが, 上のモデルをとても効率良く記述する方法がベクトルのスライシングです. 上のモデルは1行で書けます.
model {
tail(y, N - 1) ~ normal(alpha + beta * head(y, N - 1), sigma);
}
tail
演算は, y
の末尾からN - 1
個の要素を取り出すもので, head
演算は最初のN - 1
個を取り出すものです. head
の要素にbeta
を掛けるのにはベクトル計算を使っています.
回帰係数とノイズのスケールには, さまざまな別の分布族の正則事前分布を設定することもできるでしょう. 正規分布ノイズのモデルは, スチューデントのt分布はじめ, 上下限のない分布に変えることができます. 複数の観測系列があるようなら, このモデルは階層的にもできるでしょう.
強制的に定常AR(1)過程として推定するなら, 傾きの係数beta
には以下のように上下限に制約をつけてもよいかもしれません.
real<lower=-1,upper=1> beta;
実際には, こうした制約はおすすめしません. データが定常でないなら, モデルのあてはめのときにそれがわかるというのが最善です. beta
の値が零近辺に集まるような事前分布を設定することで, 定常パラメーターが推定しやすくなるでしょう.
このモデルの次数を拡張するのも簡単です. 例えば, 2次の係数gamma
を入れて, 以下のモデル文のようにAR(2)モデルをコーディングできるでしょう.
for (n in 3:N)
y[n] ~ normal(alpha + beta*y[n-1] + gamma*y[n-2], sigma);
次数自体もデータとして与えるような一般モデルは, 係数を配列に入れ, 線形予測子をループ内で計算させるようにしてコーディングできます.
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);
}
}
計量経済学と財政学の時系列モデルでは不等分散を仮定するのが普通です(すなわち, 系列を定義するノイズ項のスケールが時間的に変化してもよいとします). そのようなモデルで最も単純なのがARCH(AugoRegressive Conditional Heteroscedasticity, 自己回帰条件付き不等分散)モデルです(Engle, 1982). 自己回帰モデルAR(1)では, 系列の平均が時間的に変化しますが, ノイズ項は固定されたままです. ARCH(1)モデルでは, これとは異なり, ノイズ項のスケールが時間的に変化する一方で平均項は固定されたままです. もちろん, 平均もスケールも時間的に変化するとモデルを定義することもできるでしょう. 計量経済学の文献には多種多様な時系列モデリングの選択肢があります.
ARCH(1)モデルは典型的には以下の一連の式で示されます. ここで, \(r_{t}\)は時点\(t\)における収益の観測値, \(\mu\), \(\alpha_{0}\), \(\alpha_{1}\)は未知の回帰係数パラメーターです.
\[\begin{array}{rl}r_{t} &= \mu + a_{t} \\ a_{t} &= \sigma_{t} \epsilon_{t} \\ \epsilon_{t} &\sim \mathsf{Normal}(0, 1) \\ \sigma_{t}^{2} &= \alpha_{0} + \alpha_{1}a_{t-1}^{2}\end{array}\]
ノイズ項\(\sigma_{t}^2\)が正であることを保証するため, \(\alpha_{0}, \alpha_{1} > 0\)と, スケール係数は正に制約されています. 時系列の定常性を保証するため, \(\alpha_{1} < 1\)と, 傾きは1未満に制約されています. 1ARCH(1)モデルはStanでは以下のようにそのままコーディングできます.
data {
int<lower=0> T; // 時点の数
real r[T]; // 時点tにおける収益
}
parameters {
real mu; // 平均収益
real<lower=0> alpha0; // 誤差の切片
real<lower=0,upper=1> alpha1; // 誤差の傾き
}
model {
for (t in 2:T)
r[t] ~ normal(mu, sqrt(alpha0 + alpha1 * pow(r[t-1] - mu,2)));
}
このモデルのループは, 時点\(t = 1\)における収益をモデル化しないように定義されています. 次節のモデルで, \(t = 1\)における収益をモデル化する方法をお見せします. このモデルは, ベクトル化してより効率的にすることができません. 次節のモデルでベクトル化の例を紹介します.
1実際には, この制約を外してみて, 非定常な係数の組み合わせの方がデータによく当てはまるかどうかを試すのが有用なこともあります. あるいはまた, 当てはめから外れているトレンドがあるなら明らかに非定常でしょうから, モデルにトレンド項を加えることもあります.
一揃いの変数について, 分散がすべて同じなら, 等分散ということなります. 一方, 分散がすべて同じというわけでないなら, 不等分散ということになります. 不等分散の時系列モデルでは, ノイズ項が時間的に変化してもよいとします.
基本的なGARCH(Generalized AutoRegressive Conditional Heteroscedasticity, 一般化自己回帰条件付き不等分散)モデルであるGARCH(1,1)はARCH(1)モデルを拡張したもので, 一期前の時点\(t-1\)での収益の平均との差の2乗を, 時点\(t\)のボラティリティの予測変数に含みます.
\[\sigma_{t}^{2} = \alpha_{0} + \alpha_{1}a_{t-1}^{2} + \beta_{1}\sigma_{t-1}^{2}\]
スケール項が正であることと時系列が定常であることを保証するため, 係数については, \(\alpha_{0}, \alpha_{1}, \beta_{1} > 0\), かつ傾きについて\(\alpha_{1} + \beta_{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);
}
ボラティリティ回帰の再帰的定義の最初を決めるために, \(t = 1\)におけるノイズのスケールとして, 非負の値を持つsigma1
をデータ宣言に含めます.
制約はそのままパラメータ宣言でコーディングされています. この宣言は, alpha1
の値がbeta1
に依存するという制約があるので, この順序どおりにする必要があります.
非負値の配列である変換パラメータ(transformed parameter)sigma
は各時点のスケールの値を格納するのに使われます. これらの値の定義はtransformed parameters
ブロックにあり, 回帰もここで定義されるようにしました. 切片alpha0
, 1期前の収益と平均との差の2乗に対する傾きalpha1
, 1期前のノイズスケールの2乗に対する傾きbeta1
がここにあります. 最後に, Stanでは正規分布には(分散パラメータではなく)スケール(偏差)パラメータが必要なので, 回帰全体をsqrt
関数の中に入れています.
transformed parameters
ブロックに回帰を置くことにより, モデルは, ベクトル化されたサンプリング文1行にまで減りました. r
とsigma
の長さはT
ですので, すべてのデータが直接モデル化されています.
移動平均モデルは, 過去の誤差を将来の結果の予測変数として使います. 次数\(Q\)の移動平均モデルMA(\(Q\))には, 全体的な平均パラメータ\(\mu\)と, 過去の誤差項についての回帰係数\(\theta_{q}\)があります. 時点\(t\)における誤差を\(\epsilon_{t}\)として, 結果\(y_{t}\)についてのモデルは次のように定義されます.
\[y_{t} = \mu + \theta_{1}\epsilon_{t-1} + \dots + \theta_{Q}\epsilon_{t-Q} + \epsilon_{t}\]
結果\(y_{t}\)についての誤差項\(\epsilon_{t}\)は正規分布としてモデル化されています.
\[\epsilon_{t} \sim \mathsf{Normal}(0, \sigma)\]
正則なベイズモデルでは, \(\mu\), \(\theta\), \(\sigma\)にはすべて事前分布を与える必要があります.
MA(2)モデルはStanでは以下のようにコーディングできます.
data {
int<lower=3> T; // 観測値の数
vector[T] y; // 時点Tにおける観測値
}
parameters {
real mu; // 平均
real<lower=0> sigma; // 誤差のスケール
vector[2] theta; // ラグの係数
}
transformed parameters {
vector[T] epsilon; // 誤差項
epsilon[1] <- y[1] - mu;
epsilon[2] <- y[2] - mu - theta[1] * epsilon[1];
for (t in 3:T)
epsilon[t] <- ( y[t] - mu
- theta[1] * epsilon[t - 1]
- theta[2] * epsilon[t - 2] );
}
model {
mu ~ cauchy(0,2.5);
theta ~ cauchy(0,2.5);
sigma ~ cauchy(0,2.5);
for (t in 3:T)
y[t] ~ normal(mu
+ theta[1] * epsilon[t - 1]
+ theta[2] * epsilon[t - 2],
sigma);
}
誤差項\(\epsilon_{t}\)は, 観測値とパラメータを使って変換パラメータ(transformed parameter)として定義されています. (尤度を定義する)サンプリング文の定義もこれと同じ定義を使っていますが, \(n > Q\)の\(y_{n}\)にのみ適用可能です. この例では, パラメータにはコーシー事前分布(\(\sigma\)には半コーシー分布)を与えています. もっとも, ほかの事前分布も同じくらい簡単に使えます.
modelブロック中のサンプリング文をベクトル化すると, このモデルはもっと速くできるでしょう. ループの代わりにドット乗算を使って\(\epsilon_{t}\)の計算をベクトル化しても高速化できるでしょう.
サンプリング確率をベクトル化した一般的なMA(\(Q\))モデルは以下のように定義できるでしょう.
data {
int<lower=0> Q; // 過去のノイズ項の数
int<lower=3> T; // 観測値の数
vector[T] y; // 時点tにおける観測値
}
parameters {
real mu; // 平均
real<lower=0> sigma; // 誤差のスケール
vector[Q] theta; // 誤差の係数, ラグ -t
}
transformed parameters {
vector[T] epsilon; // 時点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(AutoRegressive Moving-Average, 自己回帰移動平均)モデルは, 自己回帰モデルと移動平均モデルの予測変数を結合させたものです. 履歴が1状態のARMA(1,1)モデルは, Stanでは以下のようにコーディングできます.
data {
int<lower=1> T; // 観測値の数
real y[T]; // 観測結果
}
parameters {
real mu; // 平均の係数
real phi; // 自己回帰の係数
real theta; // 移動平均の係数
real<lower=0> sigma; // 誤差のスケール
}
model {
vector[T] nu; // 時点tでの予測値
vector[T] err; // 時点tでの誤差
nu[1] <- mu + phi * mu; // 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); // 事前分布
phi ~ normal(0,2);
theta ~ normal(0,2);
sigma ~ cauchy(0,5);
err ~ normal(0,sigma); // 尤度
}
データは他の時系列回帰と同様に宣言されており, パラメータはコード中に説明があります.
modelブロックでは, 局所変数のベクトルnu
が予測値を, err
が誤差を格納しています. これらの計算は, 前節で記述した移動平均モデルの誤差と同様です.
定常過程にするため, 弱情報事前分布を設定しています. 尤度は誤差項だけを含み, この例では効率的にベクトル化されています.
このようなモデルでは, 計算した誤差項を調べるのが必要なことがよくあります. Stanでは, 変換パラメータ(transformed parameter)としてerr
を宣言することで簡単に対応できるでしょう. その場合も, 上のモデルでの定義と同様です. nu
は局所変数のままでもよいのですが, ここではtransformed parameters
ブロックに移しましょう.
Wayne Foltaは, 局所変数のベクトルを使わないモデルのコーディングを提案してくれました. 以下がそれです.
model {
real err;
mu ~ normal(0,10);
phi ~ normal(0,2);
theta ~ normal(0,2);
sigma ~ cauchy(0,5);
err <- y[1] - mu + phi * mu;
err ~ normal(0,sigma);
for (t in 2:T) {
err <- y[t] - (mu + phi * y[t-1] + theta * err);
err ~ normal(0,sigma);
}
}
このARMAモデルのアプローチは, Stanではどのように局所変数(この場合はerr
)を再利用できるか示す良い例となっています. Foltaのアプローチは, 2つ以上の誤差項を局所変数に格納し, ループ内で再び代入することで, 高次の移動平均モデルにも拡張できるでしょう.
両方のコーディングとも大変高速です. 元のコーディングは正規分布をベクトル化できるという利点がありますが, 使用メモリがやや多くなります. 中間点は, err
だけをベクトル化することでしょう.
MA部分の固有多項式の解が単位円の中にあるなら, MAおよびARMAモデルは識別可能ではありません. その場合, 以下の制約をつける必要があります. 2
real<lower = -1, upper = 1> theta;
このモデルから生成される合成データを用いて, 上の制約をつけずにモデルを走らせると, [-1,1]の範囲外に(theta
, phi
)の最頻値ができることがあります. これは事後分布の多峰性問題となり, またNUTSのtreedepthが非常に大きく(10を超えることもままあります)なります. 制約をつけることにより, 事後分布がより正確になり, treedepthが劇的に減少します. そのため, シミュレーションがかなり速くなります(典型的には1桁を大きく超えます).
さらに, プロセスが本当に非定常なら別ですが, そうは考えられないのなら, 定常性を保証するため以下の制約をつける価値があります.
read<lower = -1, upper = 1> phi;
2この小節は, Jonathan GilliganのGitHubのコメントを少し編集したものです. https://github.com/stan-dev/stan/issues/1617#issuecomment-160249142 を参照してください.
確率的ボラティリティモデルは, 離散時間の潜在確率過程に従って, 証券購入のオプションのような資産収益のボラティリティ(すなわち分散)を扱います(Kim et al., 1998). データは, \(T\)個の等間隔時点における原資産に対する平均修正(すなわち中央化)収益\(y_{t}\)からなります. Kimらは, 以下の回帰に似た式を使って典型的な確率ボラティリティモデルを定式化しています. ここで, 潜在パラメータ\(h_{t}\)は対数ボラティリティを, パラメータ\(\mu\)は平均対数ボラティリティを, \(\phi\)はボラティリティ項の持続性をしめします. 変数\(\epsilon_{t}\)は時点\(t\)における資産収益に対するホワイトノイズショック(すなわち乗法的誤差)で, \(\delta_{t}\)は時点\(t\)におけるボラティリティに対するショックを表します.
\[\begin{array}{rl}y_{t} &= \epsilon_{t}\exp(h_{t}/2) \\ h_{t+1} &= \mu + \phi(h_{t}-\mu)+\delta_{t}\sigma \\ h_{1} &\sim \mathsf{Normal}\left(\mu, \frac{\sigma}{\sqrt{1-\phi^{2}}}\right) \\ \epsilon_{t} &\sim \mathsf{Normal}(0, 1), \delta_{t} \sim \mathsf{Normal}(0, 1)\end{array}\]
最初の行を変形すると, \(\epsilon_{t} = y_{t}\exp(-h_{t}/2)\)となり, \(y_{t}\)のサンプリング分布は以下のように書けます.
\[y_{t} \sim \mathsf{Normal}(0, \exp(h_{t}/2))\]
\(h_{t+1}\)についての再帰式には, \(\delta_{t}\)のスケーリングとサンプリングを組み合わせることができて, 次のサンプリング分布が得られます.
\[h_{t+1} \sim \mathsf{Normal}(\mu + \phi(h_{t} - \mu), \sigma)\]
この定式化はそのままコーディングできて, 以下のStanモデルになります.
data {
int<lower=0> T; // 時点(等間隔)の数
vector[T] y; // 時点tにおける平均修正収益
}
parameters {
real mu; // 平均対数ボラティリティ
real<lower=-1,upper=1> phi; // ボラティリティの持続性
real<lower=0> sigma; // ホワイトノイズショックのスケール
vector[T] h; // 時点tにおける対数ボラティリティ
}
model {
phi ~ uniform(-1,1);
sigma ~ cauchy(0,5);
mu ~ cauchy(0,10);
h[1] ~ normal(mu, sigma / sqrt(1 - phi * phi));
for (t in 2:T)
h[t] ~ normal(mu + phi * (h[t - 1] - mu), sigma);
for (t in 1:T)
y[t] ~ normal(0, exp(h[t] / 2));
}
Kimらの定式化と比較すると, Stanのモデルではパラメータ\(\phi\), \(\sigma\), \(\mu\)に事前分布を与えています. ショック項\(\epsilon_{t}\)と\(\delta_{t}\)はモデル中には明示的には現れないことに注意してください. とはいえ, generated quantitiesブロックで効率的に計算することは可能でしょう.
このような確率的ボラティリティモデルの事後分布で事後分散が大きくなるのは普通のことです. 例えば, \(\mu = -1.02\), \(\phi = 0.95\), \(\sigma = 0.25\)として上のモデルで500データ点のシミュレーションを行なうと, 95%事後区間は\(\mu\)が(-1.23,-0.54), \(\phi\)が(0.82,0.98), \(\sigma\)が(0.16,0.38)となります.
このモデルで生成される, 秒あたりの有効サンプルを1桁以上高速化するのは比較的単純です. まず, 収益\(y\)についてのサンプリング文は簡単にベクトル化できます.
y ~ normal(0, exp(h / 2));
これにより繰り返しは高速化されますが, 有効サンブルサイズは変化しません. 根本的なパラメータ化と対数確率関数は変わっていないからです. 標準化ボラティリティによる再パラメータ化と, それからリスケーリングにより, 連鎖の混ざり具合は改善されます. これには, h
の代わりに, 標準化パラメータh_std
を宣言する必要があります.
parameters {
...
vector[T] h_std; // 時点tにおける標準化対数ボラティリティ
この時, 元のh
の値はtransformed parameters
ブロックで定義します.
transformed parameters {
vector[T] h; // 時点tにおける対数ボラティリティ
h <- h_std * sigma; // h ~ normal(0,sigma)とした
h[1] <- h[1] / sqrt(1 - phi * phi); // h[1]をリスケール
h <- h + mu;
for (t in 2:T)
h[t] <- h[t] + phi * (h[t-1] - mu);
}
最初の代入では, h_std
を\(\mathsf{Normal}(0,\sigma)\)という分布になるようにリスケールして, これを一時的にh
に代入しています. 2番目の代入では, h[1]
の事前分布がh[2]
からh[T]
までの事前分布とは異なるようにh[1]
をリスケールしています. その次の代入ではmu
にオフセットをつけており, これによりh[2]
からh[T]
までが\(\mathsf{Normal}(\mu,\sigma)\)という分布になります. この移動はh[1]
のリスケーリングの後に行なう必要があることに注意してください. 最後のループは, h[2]
からh[T]
までがphi
とmu
と比較して適切にモデル化されるように移動平均に加算します.
最後の改良として, h[1]
のサンプリング文と, h[2]
からh[T]
のサンプリングのループを, 1行のベクトル化した標準正規分布のサンプリング文に置き換えます.
model { ...
h_std ~ normal(0,1);
元のモデルでは, 数百から時には数千回の繰り返しが収束に必要となることがありますが, 再パラメータ化したモデルでは数十回の繰り返しで信頼できる収束に至ります. 連鎖の混ざり具合も劇的に改善され, 繰り返しあたりの有効サンプルサイズも多くなります. 最後に, 各繰り返しの時間は元のモデルのおおよそ4分の1になります.
隠れマルコフモデル(HMM: Hidden Markov Model)は, \(T\)個の出力変数\(y_{t}\)からなる系列を, これに並行する, 潜在カテゴリカル状態変数\(z_{t} \in \{1,\dots,K\}\)の系列を条件として生成します. 与えられた\(z_{t-1}\)について, \(z_{t}\)が他の変数と条件付き独立であるように, この「隠れ」状態変数はマルコフ連鎖を形成すると仮定します. このマルコフ連鎖は推移行列\(\theta\)によりパラメータ化されます. ここで, \(\theta_{k}\)はK次元単体(\(k \in \{1,\dots,K\}\)です. 状態\(z_{t-1}\)から状態\(z_{t}\)への推移確率は次式のようになります.
\[z_{t} \sim \mathsf{Categorical}(\theta_{z[t-1]})\]
時点\(t\)における出力\(y_{t}\)は潜在状態\(z_{t}\)に基づいて条件付き独立に生成されます.
この節では, 出力\(y_{t} \in \{1,\dots,V\}\)に単純なカテゴリカルモデルを適用してHMMを記述します. 潜在状態\(k\)についてのカテゴリカル分布はV次元単体\(\phi_{k}\)によりパラメータ化されます. 時点\(t\)における出力の観測値\(y_{t}\)は, 時点\(t\)における隠れ状態のインジケータ\(z_{t}\)に基づいて生成されます.
\[y_{t} \sim \mathsf{Categorical}(\phi_{z[t]})\]
つまり, 混合成分のインジケータが潜在マルコフ連鎖を形成するような離散混合分布モデルをHMMは形成しています.
隠れ状態が既知の状況では, パラメータ\(\theta\)および\(\phi\)の当てはめのため, 以下のナイーブモデルを使うことができます. 3
data {
int<lower=1> K; // カテゴリーの数
int<lower=1> V; // 単語(word)の数
int<lower=0> T; // 時点の数
int<lower=1,upper=V> w[T]; // 単語(word)
int<lower=1,upper=K> z[T]; // カテゴリー
vector<lower=0>[K] alpha; // 推移(transit)確率の事前確率
vector<lower=0>[V] beta; // 単語vを出力(emit)する確率の事前確率
}
parameters {
simplex[K] theta[K]; // 推移(transit)確率
simplex[V] phi[K]; // 単語vを出力(emit)する確率
}
model {
for (k in 1:K)
theta[k] ~ dirichlet(alpha);
for (k in 1:K)
phi[k] ~ dirichlet(beta);
for (t in 1:T)
w[t] ~ categorical(phi[z[t]]);
for (t in 2:T)
z[t] ~ categorical(theta[z[t - 1]]);
}
\(\theta_{k}\)と\(\phi_{k}\)には明示的なディリクレ事前分布を与えています. この2文を除くと, 有効な単体全体についての一様事前分布が暗黙のうちに使われることでしょう.
3このプログラムは, Stan例題モデルのリポジトリで入手できます. https://github.com/stan-dev/example-models/tree/master/misc/gaussian-process を参照してください.
動くことは動きますが, 上のようにHMMを記述しても完全ではありません. 開始状態\(z_{1}\)がモデル化されていないからです(添字は2から\(T\)までです). データを長い過程の部分系列と考えるなら, \(z_{1}\)の確率は, マルコフ連鎖の定常状態確率に合わせるべきでしょう. この場合, データには明確な終了がなく, 系列が\(z_{T}\)で終了する確率をモデル化する必要はありません.
もうひとつ別の概念として, HMMは有限長の系列のモデルとして考えることもできます. 例えば, 自然言語の文には明確な開始の分布(通常は大文字)と, 終了の分布(通常は何らかの句読点)とがあります. 文の境界をモデル化する最も単純な方法は, 新しい潜在状態\(K+1\)を加え, パラメータのベクトル\(\theta_{K+1}\)によりカテゴリカル分布から最初の状態を生成し, 状態\(K+1\)への推移が文の終了時にのみ起こり, それ以外では起こらないように推移を制限することです.
上に紹介したナイーブなHMM推定モデルは, カテゴリカル分布についてのループを単一の多項分布に置き換えることで劇的に速くすることができます. 4データは前もって宣言しますが, ここではtransformed data
ブロックで, 推移行列と出力行列を推定する十分統計量を計算することにします.
transformed data {
int<lower=0> trans[K,K];
int<lower=0> emit[K,V];
for (k1 in 1:K)
for (k2 in 1:K)
trans[k1,k2] <- 0;
for (t in 2:T)
trans[z[t - 1], z[t]] <- 1 + trans[z[t - 1], z[t]];
for (k in 1:K)
for (v in 1:V)
emit[k,v] <- 0;
for (t in 1:T)
emit[z[t], w[t]] <- 1 + emit[z[t], w[t]];
}
入力についてのループに基づくモデルの尤度成分は以下のように多項分布に置き換わります.
model {
...
for (k in 1:K)
trans[k] ~ multinomial(theta[k]);
for (k in 1:K)
emit[k] ~ multinomial(phi[k]);
}
正規出力確率で連続値のHMMも, 同様に十分統計量を計算することで高速化できるでしょう.
4このモデルはStan例題モデルリポジトリにあります. http://mc-stan.org/documentation を参照してください.
ディリクレ-多項HMMでは, ディリクレ分布が多項分布の共益事前分布なので, 事後分布は解析的に計算できます. 以下の例5では, Stanのモデルがどのように事後分布を解析的に定義できるかを示します. Stan言語ではこれが可能です. というのは, データが与えられたもとで, パラメータの条件付き確率に比例する量をモデルが定義しさえすればよいからです. これには, (正規化されていない)同時確率か, (正規化されていない)条件付き確率か, あるいはその間の量を定義すればよいのです.
このモデルは, 前のモデルと同じデータとパラメータを用いますが, transformed data
ブロックで事後ディリクレパラメータを計算するようにしています.
transformed data {
vector<lower=0>[K] alpha_post[K];
vector<lower=0>[V] beta_post[K];
for (k in 1:K)
alpha_post[k] <- alpha;
for (t in 2:T)
alpha_post[z[t-1],z[t]] <- alpha_post[z[t-1],z[t]] + 1;
for (k in 1:K)
beta_post[k] <- beta;
for (t in 1:T)
beta_post[z[t],w[t]] <- beta_post[z[t],w[t]] + 1;
}
事後分布は以下のように解析的に書くことができます.
model {
for (k in 1:K)
theta[k] ~ dirichlet(alpha_post[k]);
for (k in 1:K)
phi[k] ~ dirichlet(beta_post[k]);
}
5このプログラムはStan例題モデルリポジトリにあります. http://mc-stan.org/documentation を参照してください.
潜在状態が既知であるデータがまったくない完全に教師なしの方法でもHMMは推定ができます. その結果の事後分布は極端に多峰になるのが典型的です. 中間の解法は, 半教師付き推定を使うことです. これは, 教師付きデータと教師なしデータとの組み合わせに基づいています. この推定戦略をStanで実装するには, 未知の状態系列に対する出力系列の確率の計算が必要です. これは周辺化の問題で, HMMでは, 前向きアルゴリズムと呼ばれる方法で計算されます.
Stanでは, 前向きアルゴリズムは以下のようにコーディングされます. 6まず, 教師なしデータのためデータ変数を2つ追加で宣言します.
data {
...
int<lower=1> T_unsup; // 教師なしアイテムの数
int<lower=1,upper=V> u[T_unsup]; // 教師なし単語(word)
...
教師付きデータのモデルは変更ありません. 教師なしデータは, 以下のように前向きアルゴリズムをStanで実装して扱います.
model {
...
{
real acc[K];
real gamma[T_unsup,K];
for (k in 1:K)
gamma[1,k] <- log(phi[k,u[1]]);
for (t in 2:T_unsup) {
for (k in 1:K) {
for (j in 1:K)
acc[j] <- gamma[t-1,j] + log(theta[j,k]) + log(phi[k,u[t]]);
gamma[t,k] <- log_sum_exp(acc);
}
}
increment_log_prob(log_sum_exp(gamma[T_unsup]));
}
前向きの値gamma[t,k]
は, 時点t
までの入力u[1],...,u[t]
と, 潜在状態が時点t
でk
に等しいことの対数周辺確率であるとして定義されます. このとき, その前の潜在状態は周辺化消去されます. gamma
の最初の行(gamma[1,k]
)は, 潜在状態k
が最初の出力u[1]
を生成する対数確率で初期化されています. 前と同じように, 最初の潜在状態の確率自体はモデル化されません. 以降の時点t
と出力j
についてacc[j]
の値が設定されます. acc[j]
の値は次の3つの対数確率の和です. それは時点t-1
の潜在状態がj
である対数確率, 時点t-1
に状態j
だったのが時点t
に状態k
に推移する対数確率, 出力u[t]
が状態k
から生成される対数確率です. log_sum_exp
演算は単に, 状態j
の各事前確率を掛け合わせるだけです. ただし, 対数スケールにおいて算術的に安定な方法で行います.
中括弧で, 局所変数acc
とgamma
のスコープを示しています. もっと前に宣言することもできたのですが, 使用される場所の近くに宣言を置いておく方がわかりやすいでしょう.
推移パラメータ\(\theta_{k,k'}\)および出力パラメータ\(\phi_{k,v}\), それに観測系列\(u_{1},\dots,u_{T} \in {1,\dots,V}\)が与えられたとき, 観測された出力\(u\)を生成した可能性が最も高い状態系列は, Viterbi(動的プログラミング)アルゴリズムにより計算されます.
ViterbiアルゴリズムはStanでは, 以下のようにgenerated quantities
ブロックでコーディングできます. この場合の予測値は, 観測値の配列u[1], ..., u[T_unsup]
のもとでの最も可能性の高い状態系列y_star[1], ..., y_star[T_unsup]
です. この系列は, 推移確率theta
と出力確率phi
により決定されるので, 事後のサンプルによって異なることがあります.
generated quantities {
int<lower=1,upper=K> y_star[T_unsup];
real log_p_y_star;
{
int back_ptr[T_unsup,K];
real best_logp[T_unsup,K];
real best_total_logp;
for (k in 1:K)
best_logp[1,K] <- log(phi[k,u[1]]);
for (t in 2:T_unsup) {
for (k in 1:K) {
best_logp[t,k] <- negative_infinity();
for (j in 1:K) {
real logp;
logp <- best_logp[t-1,j]
+ log(theta[j,k]) + log(phi[k,u[t]]);
if (logp > best_logp[t,k]) {
back_ptr[t,k] <- j;
best_logp[t,k] <- logp;
}
}
}
}
log_p_y_star <- max(best_logp[T_unsup]);
for (k in 1:K)
if (best_logp[T_unsup,k] == log_p_y_star)
y_star[T_unsup] <- k;
for (t in 1:(T_unsup - 1))
y_star[T_unsup - t] <- back_ptr[T_unsup - t + 1,
y_star[T_unsup - t + 1]];
}
}
中括弧のブロックで, 3つの変数back_ptr
, best_logp
, best_total_logp
を局所変数にしています. そのため, これらは出力には含まれません. 変数y_star
は, 入力系列u
のもとで最も高い確率を持つラベル系列を保持します. 中間量は, 前向きアルゴリズムでは合計確率でしたが, それとは異なり, 時点t
までの系列についての, 時点t
で最終の出力カテゴリーがk
である確率の最大値best_logp[t,k]
からなります. また, リンク元へのバックポインタがあり, 最終の時点t
での対数確率の最大値からバックポインタをたどることで, 最も可能性の高い状態系列が得られます.
この推論は, 半教師付きモデルへの当てはめに使えるのと同様に, 教師なし出力u
でも使えます. 上のコードは, 教師なしの当てはめと同じモデルファイルでも使うことができます. これは, モデルをトレーニングするのに半教師付き法でデータを使うという, ベイズの推定法です. これは「ズル」ではありません. u
についての元の状態は決して観測されないからです. 他のパラメータすべてを使って推定されるだけです.
もし出力u
が半教師付き推定では使われず, 単に予測のもととなるだけなら, カット演算を使ってBUGSモデリング言語で記述したものと結果は同じです. すなわち, モデルはu
とは独立に当てはめられており, そして, u
を生成したものとして最も可能性の高い状態を見つけるのにパラメータが使われています.
ベイズ推定では欠測データに対してごく一般的な手法を使えます. すなわち, 欠測データの項目すべてが, 事後分布で推定されるパラメータとして表されます(Gelman et al., 2013). 欠測データが明示的にモデル化されないのであれば, たいていの回帰モデルの予測変数でそうですが, 結果は, 欠測している予測変数を表すパラメータに非正則事前分布を設定することになります.
観測データと欠測データを配列に混ぜる方法はStanに取り入れるのは難しいことがあります. これは一部には, 離散の未知量をモデル化する方法がStanではトリッキーなことがあることによりますし, また一部には, ほかのいくつかの統計言語(例えば, RやBUGS)とは違ってStanでは, 観測量と未知量はモデル中の別の場所で定義する必要があることによります. そのため, Stanプログラムでは, データ構造中にある観測部分と欠測部分とを組み合わせて一緒にするコードを含める必要がある場合があります. 例はこの章でこの後から紹介します.
Stanでは, data
とtransformed data
ブロックで宣言された変数を既知量として, parameters
ブロックで宣言された変数を未知量として扱います.
正規分布に従う観測値に欠測データを含む例1は以下のようにコーディングできるでしょう.
data {
int<lower=0> N_obs;
int<lower=0> N_mis;
real y_obs[N_obs];
}
parameters {
real mu;
real<lower=0> sigma;
real y_mis[N_mis];
}
model {
for (n in 1:N_obs)
y_obs[n] ~ normal(mu,sigma);
for (n in 1:N_mis)
y_mis[n] ~ normal(mu,sigma);
}
観測データ点と欠測データ点の数は, 非負の整数の変数N_obs
とN_mis
にデータとしてコーディングされています. 観測データは, 配列のデータの変数y_obs
として与えられています. 欠測データは配列のパラメータy_mis
としてコーディングされています. 通常のパラメータは推定されるので, 位置mu
とスケールsigma
もパラメータとしてコーディングされています. このモデルをもっとうまく書く方法はベクトル化することです. そうすると本体は次のようになります.
y_obs ~ normal(mu,sigma);
y_mis ~ normal(mu,sigma);
このモデルには, 観測データと欠測データにそれぞれ1つのループがあります. これはモデル指定としていささか冗長ですが, そのおかげで, Stanでの欠測データ問題としては, 次の節で記述するもっと一般的なテクニックよりもはるかに効率的なサンプリングが可能になります.
1もっと意味のある推定の例は, 予測変数を使った観測された観測値と欠測の観測値の回帰を含むものでしょう. 観測された観測値に対応する予測変数も欠測の観測値に対応する予測変数も既知で, data
ブロックで指定されているものです.
多変量の確率関数で, 一部の結果やパラメータだけが観測されているといったような状況では, 既知量(データ)と未知量(パラメータ)とを混ぜたベクトルを作る必要があります. Stanでこれをおこなうのは, transformed parameters
ブロックでベクトルあるいは配列をつくり, それに代入することで可能です.
以下の例は, 2変数の共分散行列を含むもので, 分散は既知ですが, 共分散は既知ではありません.
data {
int<lower=0> N;
vector[2] y[N];
real<lower=0> var1; real<lower=0> var2;
}
transformed data {
real<upper=0> min_cov;
real<lower=0> max_cov;
max_cov <- sqrt(var1 * var2);
min_cov <- -max_cov;
}
parameters {
vector[2] mu;
real<lower=min_cov,upper=max_cov> cov;
}
transformed parameters {
matrix[2,2] sigma;
sigma[1,1] <- var1; sigma[1,2] <- cov;
sigma[2,1] <- cov; sigma[2,2] <- var2;
}
model {
for (n in 1:N)
y[n] ~ multi_normal(mu,sigma);
}
分散は, 変数var1
およびvar2
にデータとして定義されていますが, 共分散は, 変数cov
にパラメータとして定義されています. 2×2共分散行列sigma
は変換パラメータ(transformed parameter)として定義されています. 分散は, 2つの対角要素に代入され, 共分散は, 2つの非対角要素に代入されます.
共分散の宣言につけた制約により, 結果の共分散行列sigma
が正定値であることが保証されます. 分散の積の, 正負の平方根が上下限となりますが, この値はtransformed data
として定義されるので, 計算されるのは1回だけです.
はじめの節の欠測データの例は, 次の節の, 部分的に既知のパラメータの例の方法と同じように, データとパラメータとを混ぜた配列としてプログラミングすることもできるでしょう. 正しく動くでしょうが, 計算には無駄が多くなります. parameters
あるいはtransformed parameters
ブロックで宣言される各パラメータは自動微分の変数を使います. 単純なデータの変数と比較すると, 記憶容量と勾配計算の時間の点でこれはより高くつきます. さらに, そのコピーが余分な容量と余分な時間を使います.
Rick Farouniは, Stan users groupで, 単位対角の共分散行列についてCholesky因子をつくる方法を尋ねました. これは, ベイズ因子分析(Aguilar and West 2000)で使われます. 対角下の要素をパラメータとして宣言し, それから変換パラメータ(tranformed parameter)とした行列全体に値を埋めることで, これを行なうことができます.
data {
int<lower=2> K;
}
transformed data {
int<lower=1> K_choose_2;
K_choose_2 <- (K * (K - 1)) / 2;
}
parameters {
vector[K_choose_2] L_lower;
}
transformed parameters {
cholesky_factor_cov[K] L;
for (k in 1:K)
L[k,k] <- 1;
{
int i;
for (m in 2:K) {
for (n in 1:(m - 1)) {
L[m,n] <- L_lower[i];
L[n,m] <- 0;
i <- i + 1;
}
}
}
}
事前分布は, L_lower
に直接置くのが最も便利です. もうひとつの方法は, Cholesky因子L
全体への事前分布でしょう. L_lower
からL
への変換では値は変わらないので, (訳注: 確率変数の変換に伴う)ヤコビアンの調整を必要としないからです(必要としないにもかかわらず, パーサから警告が出ます. これはパーサがコード解析をして変換が線形であると推測するほど十分に賢くないためです. ). 共分散行列L * L'
全体に事前分布を置くのは, Jacobian調整が必要となるでしょうから, まったく便利ではないでしょう. 正確な調整は, 共分散行列を扱う34.1節の小節で示します.
測定値に切断や打ち切りがあるデータは, Stanでは以下のそれぞれの確率モデルのようにコーディングできます.
Stanで切断を使える分布は, 1変量分布で, 対応する対数累積分布関数(cumulative distribution function: CDF)と対数相補累積分布関数(complementary cumulative distribution function: ccdf)があるものに限られています. 切断分布とcdf, ccdfについてもっと知るには, 5.3節の切断分布の項を参照してください.
切断データとは, ある下限より上か, 上限より下か, あるいは上下限の間にある測定値しか報告されないようなデータです.
切断データはStanでは切断分布を使ってモデリングできるでしょう. 例えば, 切断データ\(y_{n}\)は, \(y_{n}<300\)となるように切断点\(U=300\)が上限となっているとします. Stanではこのデータは, 観測値が切断正規分布に従うとして以下のようにモデリングできます.
data {
int<lower=0> N;
real U;
real<upper=U> y[N];
}
parameters {
real mu;
real<lower=0> sigma;
}
model {
for (n in 1:N)
y[n] ~ normal(mu,sigma) T[,U];
}
このモデルでは, 上限U
をデータとして宣言し, この制約にあてはまるようにy
のデータに制約をつけています. これは, サンプリングが始まる前, データがモデルに読み込まれたときにチェックされます.
このモデルは, スケールと位置のパラメータに暗黙に非正則平坦事前分布を使っています. これらには, サンプリング文(~)を使ってmodel
ブロック中で事前分布を与えることもできます.
切断分布では, サンプリングされた変量が切断の範囲外の値になったときには, 確率は0になります. したがって対数確率は\(-\infty\)と評価されるでしょう. 例えば, 変量y
が次の文でサンプリングされているとします.
for (n in 1:N)
y[n] ~ normal(mu,sigma) T[L,U];
このとき, y[n]
の値がL
の値より小さいか, U
の値よりも大きければ, サンプリング文は確率0の推定値を生成します. ユーザー定義の切断では, このように切断範囲外では0とするように明示的に処理しなければいけません.
変数が切断範囲外にそれるのを防ぐには, 適切な制約が必要です. 例えば, y
が上のモデルのパラメータならば, L
とU
の値の間になるように宣言で制約をつけるべきです.
parameters {
real<lower=L,upper=U> y[N];
...
上のモデルで, L
とU
がパラメータでy
がデータなら, 適切に制約をつけて, データがその間にあり, L
の値がU
の値よりも小さくなるようにしなくてはなりません(もし両者が等しければ, パラメータの範囲は1点にまで小さくなり, サンプラーで使われているハミルトニアン力学は破綻します). 以下の宣言は, 境界がうまく設定されることを保証します.
parameters {
real<upper=min(y)> L; // L < y[n]
real<lower=fmax(L,max(y))> U; // L < U; y[n] < U
実数の組み合わせについては, max
ではなくfmax
関数を使うことに注意してください.
切断点が未知のときは, パラメータとして扱うことでこれを推定することができます. 既知の切断点に関する前の節のモデルから, 変数宣言を少し再構成することで, これが可能になります.
data {
int<lower=1> N;
real y[N];
}
parameters {
real<upper = min(y)> L;
real<lower = max(y)> U;
real mu;
real<lower=0> sigma;
}
model {
L ~ ...;
U ~ ...;
for (n in 1:N)
y[n] ~ normal(mu,sigma) T[L,U];
}
ここでは, 下方の切断点L
が, y
の最小値以下と宣言されています. また, 上方の切断点U
はy
の最大値以上と宣言されています. この宣言はデータに依存していますが, データが両切断点の間にあるという制約しか課していません. ただし, N
がint<lower=1>
という型で宣言されていますので, 少なくとも1つのデータ点があるはずです. L
がU
よりも小さいという制約は, 空ではないデータによって間接的に保証されるのです.
下限L
と上限U
の事前分布については省略していますが, このモデルにおいてL
がmin(y)
のまわりに強く集中したりU
がmax(y)
のまわりに強く集中したりしないように, 情報のある事前分布を設定すべきでしょう.
打ち切りデータとは, 点の値が大きすぎたり, 小さすぎたり, あるいはその両方の場合で, 値が不明になるようなデータです. 切断データと異なるのは, 打ち切られたデータ点の数が分かっていることです. 教科書に載っている例としては, 135kgより重い値が報告されない家庭用の秤があります.
打ち切りデータをモデリングする方法の1つは, 打ち切りデータを, 打ち切られる範囲のあるという制約をつけた欠測データとして扱うことです. Stanでは配列や行列に未知の値を許しませんから, 打ち切りとなる値は明示的に表さなくてはなりません. 以下は, 右側打ち切りの場合です.
data {
int<lower=0> N_obs;
int<lower=0> N_cens;
real y_obs[N_obs];
real<lower=max(y_obs)> U;
}
parameters {
real<lower=U> y_cens[N_cens];
real mu;
real<lower=0> sigma;
}
model {
y_obs ~ normal(mu,sigma);
y_cens ~ normal(mu,sigma);
}
打ち切りデータの配列y_cens
はパラメータとして宣言されていますので, 位置とスケールのパラメータ, mu
およびsigma
とともにサンプリングされます. 打ち切りデータの配列y_cens
は, real<lower=U>
という型の値と宣言されていますので, 打ち切りデータに補完される値はすべてU
よりも大きくなります. 打ち切りを補完したデータでは, モデル中の最後のサンプリング文を実行すると, 位置とスケールのパラメータに影響が出ます.
位置とスケールを推定するときに打ち切りとなる値を無視するのは良くありませんが, 補完値が必要な場合だけではありません. 補完せずに, 打ち切りとなる値を積分消去することもできます. 打ち切られた各データ点は以下の確率を持ちます.
\[\Pr[y>U]=\int_{U}^{\infty}\mathsf{Normal}(y \mid \mu, \sigma)dy = 1 - \Phi\left(\frac{y - \mu}{\sigma}\right)\]
ここで, \(\Phi()\)は標準正規分布の累積分布関数です. \(M\)個の打ち切り観測があるとき, 合計の確率は対数軸で以下のようになります.
\[\log\prod_{m=1}^{M}\Pr[y_{m}>U] = \log\left(1 - \Phi\left(\frac{y - \mu}{\sigma}\right)\right)^{M} = M \mathsf{normal\_ccdf\_log}(y, \mu, \sigma)\]
ここで, \(\textsf{normal\_ccdf\_log}\)は相補CDFの対数です(Stanに実装されている各分布には<distr>_ccdf_log
があります).
以下の右側打ち切りモデルでは, 打ち切り点が既知であることを仮定しており, データとして宣言しています.
data {
int<lower=0> N_obs;
int<lower=0> N_cens;
real y_obs[N_obs];
real<lower=max(y_obs)> U;
}
parameters {
real mu;
real<lower=0> sigma;
}
model {
y_obs ~ normal(mu,sigma);
increment_log_prob(N_cens * normal_ccdf_log(U,mu,sigma));
}
y_obs
の観測値については, 打ち切りのない, 正規分布のサンプリングを行なうモデルが使われています. 打ち切りデータの項目については, 正規分布の対数相補累積分布(訳注: 原文では'log cumulative normal probability'ですが, モデルにあわせて修正しました)の確率を計算して, それを対数確率に直接加算されています.
左側打ち切りデータでは, 相補CDFの代わりにCDF(normal_cdf_log
)を使わなくてはなりません. もし打ち切り点の変数(L
)が未知なら, その宣言はdata
からparameters
ブロックに移動すべきです.
data {
int<lower=0> N_obs;
int<lower=0> N_cens;
real y_obs[N_obs];
}
parameters {
real<upper=min(y_obs)> L;
real mu;
real<lower=0> sigma;
}
model {
L ~ normal(mu,sigma);
y_obs ~ normal(mu,sigma);
increment_log_prob(N_cens * normal_cdf_log(L,mu,sigma));
}
結果変数が有限混合分布となるモデルは, 結果変数が複数の分布のうちのひとつから抽出され, どの分布から抽出されるかは, 混合させるカテゴリカルな分布により制御されると仮定します. 混合モデルは通常, 多峰の分布となり, その山は混合成分の最頻値に近くなります. 混合分布モデルはいくつかの方法でパラメータ化できます. 以下の節でそれを記述します.
混合分布モデルをパラメータ化する方法の1つは, 結果変数を負担する混合成分を示す潜在カテゴリカル変数を使うことです. 例えば, \(K\)個の正規分布があり, その位置は\(\mu_{k} \in \mathcal{R}\), スケールは\(\sigma_{k} \in (0, \infty)\)とします. ここで, これらを割合\(\theta\)で混合させるとします. \(\theta_{k} \ge 0\)かつ\(\sum_{k=1}^{K}\theta_{k}=1\), すなわち\(\theta\)は\(K\)次元単体です. 各結果変数\(y_{n}\)には潜在変数\(z_{n} \in {1,\dots,K}\)があり, これは\(\theta\)によりパラメータ化されるカテゴリカル分布に従うとします.
\[z_{n} \sim \mathsf{Categorical}(\theta)\]
変数\(y_{n}\)は, 混合成分\(z_{n}\)のパラメータに従って分布します.
\[y_{n} \sim \mathsf{Normal}(\mu_{z[n]},\sigma_{z[n]})\]
離散パラメータ\(z_{n}\)があるので, Stanではこのモデルは直接扱うことができませんが, 次の節で記述するように\(z\)パラメータを総和で消去することにより\(\mu\)と\(\sigma\)をサンプリングできます.
前の節であらましを見た混合正規分布モデルは, 総和により離散パラメータをモデルから消去することによりStanで実装できます. \(Y\)が\(K\)個の正規分布の混合分布で, 正規分布のが位置\(\mu_{k}\), スケール\(\sigma_{k}\), \(K\)次元単位単体に従う割合\(\theta\)で混合される場合は, 次式のように表されます.
\[p_{Y}(y \mid \theta,\mu,\sigma) = \sum_{k=1}^{K}\theta_{k}\mathsf{Normal}(\mu_{k},\sigma_{k})\]
指数の和の対数関数は, 対数軸での混合分布を定義するのに使われます. 2つの入力値の場合は以下のように定義されます.
\[\mathrm{log\_sum\_exp}(a,b) = \log(\exp(a)+\exp(b))\]
\(a\)と\(b\)が確率を対数軸にしたものなら, \(\exp(a)+\exp(b)\)は線形軸での両者の和になります. そして外側の対数で, 結果を対数軸に戻します. まとめると, log_sum_expは対数軸で線形の加算を行なうということになります. Stanの組込みlog_sum_exp
関数を使う理由は, 指数計算でのアンダーフローやオーバーフローを防ぐことができるということにあります. 結果変数は以下のように計算されます.
\[\log(\exp(a)+\exp(b))=\max(a,b)+\log(\exp(a-\max(a,b))+\exp(b-\max(a,b)))\]
(訳注:原文ではlog()の中の'+'が','になっています. )
この式の評価では, \(a-\max(a,b)\)か\(b-\max(a,b)\)のいずれかが0になり, 他方は負になります. これにより, 主項でオーバーフローやアンダーフローが発生する可能性をなくし, 演算での算術精度を可能な限り確保します.
例として, \(\mathsf{Normal}(-1, 2)\)と\(\mathsf{Normal}(3, 1)\)とが確率\(\theta=(0.3,0.7)^{\top}\)で混ざる混合分布は, Stanでは以下のように実装できます.
parameters {
real y;
}
model {
increment_log_prob(log_sum_exp(log(0.3)
+ normal_log(y,-1,2),
log(0.7)
+ normal_log(y,3,1)));
}
対数確率の項は以下のように導出されます.
\[\begin{array}{ll}\log p_{Y}(y\mid\theta,\mu,\sigma) &= \log(0.3\times\mathsf{Normal}(y \mid -1,2)+0.7\times\mathsf{Normal}(y\mid 3,1))\\ &= \log(\exp(\log(0.3\times\mathsf{Normal}(y\mid -1,2)))\\\ &\qquad+\exp(\log(0.7\times\mathsf{Normal}(y\mid 3,1))))\\ &=\mathrm{log\_sum\_exp}(\log(0.3)+\log\mathsf{Normal}(y\mid -1,2),\\ &\hspace{68pt}\log(0.7)+\log\mathsf{Normal}(y\mid 3,1)) \end{array}\]
混合分布を表現する枠組みが与えられたところで, 推定の準備に移りましょう. ここでは, 位置, スケール, 混合成分が未知の量です. さらに, 混合成分の数も一般化してデータとして指定したものが以下のモデルになります.
data {
int<lower=1> K; // 混合成分の数
int<lower=1> N; // データ点の数
real y[N]; // 観測値
}
parameters {
simplex[K] theta; // 混合確率
real mu[K]; // 混合成分の位置
real<lower=0> sigma[K]; // 混合成分のスケール
} model {
real ps[K]; // 成分密度の対数の一時変数
sigma ~ cauchy(0,2.5);
mu ~ 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));
}
}
このモデルには, \(K\)個の混合成分と\(N\)個のデータ点があります. 混合割合のパラメータtheta
は\(K\)次元単位単体として宣言されており, 成分の位置のパラメータmu
とスケールのパラメータsigma
はともに大きさK
の配列として定義されています. スケールの配列sigma
の値は非負に制約されており, このモデルでは弱情報事前分布を与えています. モデル中で, 大きさK
の局所配列変数ps
が宣言されており, 混合成分からの寄与を積算するのに使われています.
これは例題なので, 位置とスケールは単純な事前分布から抽出されていますが, Stanでサポートされることなら何でもできるでしょう. 混合成分は階層的にモデリングすることさえ可能でしょう.
作業の中心は, データ点n
についてのループ中にあります. 各点について, \(\theta_{k}\times\mathsf{Normal}(y_{n}\mid\mu_{k},\sigma_{k})\)の対数が計算され, 配列ps
に加えられます. それから, それらの値の指数の和の対数だけ対数確率を増加させます.
Stanでは(今のところ)混合分布モデルを観測値のレベルでベクトル化する方法はありません. この節は読者に注意を行なうものです. 単純にベクトル化しようとしてはいけません. そのようにすると, 結果として別のモデルとなってしまいます. 観測値のレベルでの正しい混合は以下のように定義されます.
for (n in 1:N)
increment_log_prob(log(lambda)
+ normal_log(y[n], mu[1], sigma[1]),
log1m(lambda)
+ normal_log(y[n], mu[2], sigma[2]));
下も等価です.
for (n in 1:N)
increment_log_prob(log_mix(lambda,
normal_log(y[n], mu[1], sigma[1]),
normal_log(y[n], mu[2], sigma[2])));
この定義では, 各観測値\(y_{n}\)が混合成分のいずれかから得られると仮定しています. 以下のように定義されます.
\[p(y \mid \lambda,\mu,\sigma)=\prod_{n=1}^{N}(\lambda\times\mathsf{Normal}(y_{n}\mid\mu_{1},\sigma_{1})+(1-\lambda)\times\mathsf{Normal}(y_{n}\mid\mu_{2},\sigma_{2})\]
上のモデルと, 下の(間違った)モデルのベクトル化の試みを対比してみてください.
increment_log_prob(log(lambda)
+ normal_log(y, mu[1], sigma[1]),
log1m(lambda)
+ normal_log(y, mu[2], sigma[2]));
下も等価です.
increment_log_prob(log_mix(lambda,
normal_log(y, mu[1], sigma[1]),
normal_log(y, mu[2], sigma[2])));
この2番目の定義は, 観測値の系列\(y_{1},\dots,y_{n}\)全体が一方の成分か, 他方の成分かから得られていることを意味します. 別の密度を定義しているのです.
\[p(y\mid\lambda,\mu,\sigma)=\lambda\times\prod_{n=1}^{N}\mathsf{Normal}(y_{n}\mid\mu_{1},\sigma_{1})+(1-\lambda)\times\prod_{n=1}^{N}\mathsf{Normal}(y_{n}\mid\mu_{2},\sigma_{2})\]
ゼロ過剰モデルとハードルモデルはともに, ポアソン分布と, ベルヌーイ確率質量関数との混合分布で, 結果変数が0になる確率をより柔軟にモデリングできます. ゼロ過剰モデルは, Lambert (1992)により定義されたもので, 零値の結果変数に対して追加の確率質量を与えるものです. 一方, ハードルモデルは零値と非零値の結果変数の純粋な混合分布として定式化されます.
ゼロ過剰モデルとハードルモデルは, ポアソン分布以外の離散分布についても定式化できます. ゼロ過剰はStanでは連続分布でうまくいきません. これは, 導関数の問題によるもので, 特に, 回帰係数の事前分布として正規分布をゼロ過剰にするようなときに, 連続分布に対して点の質量を加算する方法がありません.
以下のゼロ過剰ポアソン分布の例について考えます. ここではパラメータtheta
を使って, 確率\(\theta\)で0が抽出され, 確率\(1-\theta\)で\(\mathsf{Poisson}(\lambda)\)から抽出されるとしています. 確率関数は以下のとおりです.
\[p(y_{n}\mid\theta,\lambda)=\begin{cases}\theta+(1-\theta)\times\mathsf{Poisson}(0\mid\lambda) & y_{n}=0のとき \\(1-\theta)\times\mathsf{Poisson}(y_{n}\mid\lambda) & y_{n} > 0のとき\end{cases}\]
対数確率関数はStanでは以下のようにそのまま実装できます.
data {
int<lower=0> N;
int<lower=0> y[N];
}
parameters {
real<lower=0,upper=1> theta;
real lambda;
}
model {
for (n in 1:N) {
if (y[n] == 0)
increment_log_prob(log_sum_exp(bernoulli_log(1,theta),
bernoulli_log(0,theta)
+ poisson_log(y[n],lambda)));
else
increment_log_prob(bernoulli_log(0,theta)
+ poisson_log(y[n],lambda));
}
}
log_sum_exp(lp1,lp2)
関数は対数確率を線形軸で加算します. log(exp(lp1) + exp(lp2))
と同じですが, より算術的に安定し, 高速です.
increment_log_prob
関数の内部でif_else
構文を使うようにしたいと思うかもしれませんが, if_else_(c,e1,e2)
はc
の値に関わらずe1
とe2
の両方を評価するので, これはおすすめできません.
ハードルモデルはゼロ過剰モデルに似ていますが, もっと柔軟で, 零値の結果変数が過剰のときのみならず少なすぎるときも扱うことができます. ハードルモデルの確率質量関数は以下のように定義されます.
\[p(y_{n}\mid\theta,\lambda)=\begin{cases}\theta & y=0のとき \\(1-\theta)\frac{\mathsf{Poisson}(y\mid\lambda)}{1-\mathsf{PoissonCDF}(0\mid\lambda)} & y > 0のとき\end{cases}\]
ここで\(\mathsf{PoissonCDF}\)は, ポアソン分布の累積分布関数です. ハードルモデルはStanではさらに直接的にプログラミングできます. 明示的な混合分布とする必要はありません.
(y[n] == 0) ~ bernoulli(theta);
if (y[n] > 0)
y[n] ~ poisson(lambda) T[1,];
ポアソン分布のあとの[1,]
は1未満の値が切断されることを示しています. これについては12.1節を参照してください. 変数y[n]
が2回「サンプリング」されていますが, 対数確率関数への効果は(対数軸での)定義に従っています.
Julian Kingは, 以下のように記述するとモデルが高速化するかもしれないと指摘しました.
\[\log\mathsf{PoissonCDF}(0\mid\lambda)=\log(1-\exp(-\lambda))\]
こうすると, 切断ポアソン分布は以下のようにコーディングされます.
if (y[n] > 0) {
y[n] ~ poisson(lambda);
increment_log_prob(-log1m_exp(-lambda));
}
前もって計数値をまとめておくことにより, 密度を変えることなく実行速度をおおいに高速化することもできるという1例を示します. 例えば, y[n] == 0
の場合の数を数え, 変換データ量として格納しておきます. これは,
model {
...
for (n in 1:N)
(y[n] == 0) ~ bernoulli(theta);
を
transformed data {
int N_zero;
N_zero <- 0;
for (n in 1:N)
if (y[n] == 0)
N_zero <- N_zero + 1;
}
model {
...
N_zero ~ binomial(N, theta);
と, 変えればできます.
また別の高速化の方法として, 計数値が非零(すなわちy[n] > 0
)の場合に, その数を新しく定義した配列に格納し, それからベクトル化したポアソン分布のサンプリング文を使って, 非零の計数値の数を増加分に掛けるというものがあります. これはややトリッキーです. Stanでは宣言の時に大きさが必要ですので, 零と非零の場合の数を計算する関数をこのときに使う必要があるためです.
統計モデルで使われる量のほとんどは測定で得られたものです. こうした測定にはほとんどの場合, 何らかの誤差があります. 測定された量に対して測定誤差が小さければ, モデルには通常あまり影響しません. 測定された量に対して測定誤差が大きいときや, 測定された量について非常に精密な関係を推定する可能性があるときは, 測定誤差を明示的に取り入れたモデルが役に立ちます. 測定誤差の一種には丸め誤差もあります.
メタアナリシスは統計的には, 測定誤差モデルと非常によく似ています. メタアナリシスでは, 複数のデータセットから導かれた推定が, それら全体についての推定にまとめられます. 各データセットについての推定は, 真のパラメータの値から, 一種の測定誤差があるものとして扱われます.
ベイズ統計の手法では, 真の量を欠測データとして扱うことにより, 測定誤差を直接的に定式化できます(Clayton, 1992; Richardson and Gilks, 1993). これには, 真の値から測定値がどのように導かれるのかというモデルが必要です.
測定誤差のある回帰を考える前にまず, 予測変数\(x_n\)と結果変数\(y_n\)を含む, \(N\)回の観測データの線形回帰モデルを考えましょう. Stanでは, 傾きと切片のある, \(x\)についての\(y\)の線形回帰は以下のようにモデリングされます.
data {
int<lower=0> N; // 観測回数
real x[N]; // 予測変数(共変量)
real y[N]; // 結果変数(変量)
}
parameters {
real alpha; // 切片
real beta; // 傾き
real<lower=0> sigma; // 結果変数のノイズ
} model {
y ~ normal(alpha + beta * x, sigma);
alpha ~ normal(0,10);
beta ~ normal(0,10);
sigma ~ cauchy(0,5);
}
ここで, 予測変数\(x_n\)の真値が既知ではないとします. ただし, 各\(n\)について, \(x_n\)の測定値\(x_{n}^\mathrm{meas}\)は分かっています. 測定誤差をモデリングできるならば, 測定値\(x_{n}^\mathrm{meas}\)は, 真値\(x_n\)に測定ノイズを加算したものとモデリングできます. 真値\(x_n\)は欠測データとして扱われ, モデル中の他の量と同時に推定されます. 非常に単純な方法としては, 測定誤差が既知の偏差\(\tau\)で正規分布すると仮定する方法があります. 以下のような, 測定誤差が一定の回帰モデルになります.
data {
...
real x_meas[N]; // xの測定値
real<lower=0> tau; // 測定ノイズ
}
parameters {
real x[N]; // 未知の真値
real mu_x; // 事前分布の位置
real sigma_x; // 事前分布のスケール
...
}
model {
x ~ normal(mu_x, sigma_x); // 事前分布
x_meas ~ normal(x, tau); // 測定モデル
y ~ normal(alpha + beta * x, sigma);
...
}
回帰係数のalpha
とbeta
, 回帰ノイズのスケールsigma
は前と同じですが, データではなくパラメータとしてx
が新たに宣言されています. データはx_meas
になり, 真のx
の値からスケールtau
のノイズを含めて測定されているとなっています. そしてこのモデルは, 真値x[n]
についての測定誤差x_meas[n]
が偏差tau
の正規分布に従うと指定しています. さらに真値x
にはここでは階層事前分布が与えられています.
測定誤差が正規分布でない場合には, もっと複雑な測定誤差モデルを指定することもできます. 真値の事前分布も複雑にできます. 例えば, Clayton (1992)は, 既知の(測定誤差のない)リスク要因\(c\)に対する, 未知の(ただしノイズ込みで測定された)リスク要因\(x\)についての暴露モデルを紹介しています. 単純なモデルでは, 共変量\(c_n\)とノイズ項\(\upsilon\)から\(x_n\)を回帰するというようになるでしょう.
\[x_{n} \sim \mathsf{Normal}(\gamma^{\top}c,\upsilon)\]
これはStanでは, ほかの回帰とまったく同様にコーディングできます. もちろん, さらにほかの暴露モデルも使えます.
測定誤差でよくあるのは, 測定値を丸めることに由来するものです. 丸めのやり方はたくさんあります. 重さをもっとも近いミリグラムの値に丸めたり, もっとも近いポンドの値に丸めたりします. もっとも近い整数に切り下げるという丸めもあります.
Gelman et al. (2013)の演習3.5(b)に以下の例題があります.
3.5 ある物体の重さを5回はかるとします. 測定値はもっとも近いポンドの値に丸められ, 10, 10, 12, 11, 9となりました. 丸める前の測定値は正規分布するとして, \(\mu\)と\(\sigma^2\)には無情報事前分布を使います. (b) 測定値が丸められていることを考慮して, \((\mu, \sigma^2)\)についての正しい事後分布を求めなさい.
\(y_n\)の丸められていない測定値を\(z_n\)とします. すると, 述べられている問題は以下の尤度を仮定することになります.
\[z_{n} \sim \mathsf{Normal}(\mu,\sigma)\]
丸めの過程により, \(z_n \in (y_n - 0.5, y_n + 0.5)\)となります. 離散値の観測値\(y\)の確率質量関数は, 丸められていない測定値を周辺化消去することにより与えられ, 以下の尤度が得られます.
\[p(y_{n}\mid\mu,\sigma)=\int_{y_{n}-0.5}^{y_{n}+0.5}\mathsf{Normal}(z_{n}\mid\mu,\sigma)\mathrm{d}z_{n}=\Phi\left(\frac{y_{n}+0.5-\mu}{\sigma}\right)-\Phi\left(\frac{y_{n}-0.5-\mu}{\sigma}\right)\]
この問題についてのGelmanの解答では, 分散\(\sigma^2\)について対数スケールで一様分布の無情報事前分布を使いました. このときの事前密度は(ヤコビアンの調整により)以下のようになります.
\[p(\mu,\sigma^2) \propto \frac{1}{\sigma^2}\]
\(y=(10,10,12,11,9)\)を観測した後の事後分布は, ベイズの定理により計算できます.
\[\begin{array}{ll}p(\mu,\sigma^2\mid y) &\propto p(\mu,\sigma^2)p(y\mid\mu,\sigma^2)\\ &\propto \frac{1}{\sigma^2}\prod_{n=1}^{5}\left(\Phi\left(\frac{y_{n}+0.5-\mu}{\sigma}\right)-\Phi\left(\frac{y_{n}-0.5-\mu}{\sigma}\right)\right) \end{array}\]
Stanのコードは単純に数学的定義に従っており, 確率関数をそのまま定義したものを, 割合にするまでの例となっています.
data {
int<lower=0> N;
vector[N] y;
}
parameters {
real mu;
real<lower=0> sigma_sq;
}
transformed parameters {
real<lower=0> sigma;
sigma <- sqrt(sigma_sq);
}
model {
increment_log_prob(-2 * log(sigma));
for (n in 1:N)
increment_log_prob(log(Phi((y[n] + 0.5 - mu) / sigma)
- Phi((y[n] - 0.5 - mu) / sigma)));
}
別のやり方として, 丸められていない測定値\(z_n\)についての潜在パラメータを使ってモデルを定義する方法もあるでしょう. この場合のStanのコードは, 制約\(z_n \in (y_n - 0.5, y_n + 0.5)\)を満たしつつ\(z_n\)の尤度をそのまま使います. Stanでは, ベクトル(あるいは配列)の要素についての上下限は不変でなくてなりませんので, そのパラメータは丸め誤差\(y - z\)として宣言され, \(z\)はtransformed parameter
として定義されます.
data {
int<lower=0> N;
vector[N] y;
}
parameters {
real mu;
real<lower=0> sigma_sq;
vector<lower=-0.5, upper=0.5>[5] y_err;
}
transformed parameters {
real<lower=0> sigma;
vector[N] z;
sigma <- sqrt(sigma_sq);
z <- y + y_err;
}
model {
increment_log_prob(-2 * log(sigma));
z ~ normal(mu, sigma);
}
丸められていない測定値\(z\)を明示したこのモデルは, \(z\)を周辺化消去した前のモデルと, \(\mu\)と\(\sigma\)について同じ事後分布を生成します. どちらのやり方とも連鎖が良く混ざりますが, 潜在パラメータを使うバージョンは, iterationあたりの有効サンプルの点で2倍ほど効率的です. また, 丸められていないパラメータの事後分布も得られます.
メタアナリシスは, いくつかの学校での指導プログラムの利用や, いくつかの臨床試験での薬を使った治療といった, いくつかの研究からのデータをプールすることを目的としています.
ベイズの枠組みはメタアナリシスには特に便利です. というのは, 興味ある真の量をノイズ込みで測定したものとして, 各先行研究を扱うことができるからです. このとき, モデルはそのまま2つの部分から成ります. つまり, 興味ある真の量についての事前分布と, 解析する研究のそれぞれについての測定誤差タイプのモデルです.
治療群と対照群について対応のある二項分布のデータが得られている研究が全部で\(M\)個あるところから問題のデータが得られているとします. 例えばデータは, イブプロフェン投与下での外科手術後の痛みの軽減や(Warn et al., 2002), ベータブロッカー投与下での心筋梗塞後の死亡率(Gelman et al, 2013, Section 5.6)といったものでしょう.
この臨床データは\(J\)個の臨床試験から成り立っています. それぞれの臨床試験について, 治療群に割り付けられた人数は\(n^t\), 対照群に割り付けられた人数は\(n^c\), 治療群で改善した(成功した)人数は\(r^t\), 対照群で改善した(成功した)人数は\(r^c\)です. このデータはStanでは以下のように宣言できます. 1
data {
int<lower=0> J;
int<lower=0> n_t[J]; // 治療例の数
int<lower=0> r_t[J]; // 治療群での成功数
int<lower=0> n_c[J]; // 対照例の数
int<lower=0> r_c[J]; // 対照群での成功数
}
1 Stanの整数の制約は, r_t[h]
\(le\) n_t[j]
という制約を表現できるほど強力ではありません. しかしこの制約は, transformed data block
でチェックすることができるでしょう.
この臨床試験データは, そのままでは2項分布のフォーマットですが, 対数オッズ比を考えると, 限度のない軸に変換できるでしょう.
\[y_{j}=\log\left(\frac{r^{t}_{j}/(n^{t}_{j}-r^{t}_{j})}{r^{c}_{j}/(n^{c}_{j}-r^{c}_{j})}\right)=\log\left(\frac{r^{t}_{j}}{n^{t}_{j}-r^{t}_{j}}\right)-\log\left(\frac{r^{c}_{j}}{n^{c}_{j}-r^{c}_{j}}\right)\]
対応する標準誤差です.
\[\sigma_{j}=\sqrt{\frac{1}{r^t_j}+\frac{1}{n^t_j-r^t_j}+\frac{1}{r^c_j}+\frac{1}{n^c_j-r^c_j}}\]
対数オッズと標準誤差はtransformed parameters
ブロックで定義できますが, 整数除算にならないように注意が必要です(39.1節参照).
transformed data {
real y[J];
real<lower=0> sigma[J];
for (j in 1:J)
y[j] <- log(r_t[j]) - log(n_t[j] - r_t[j])
- (log(r_c[j]) - log(n_c[j] - r_c[j]);
for (j in 1:J)
sigma[j] <- sqrt(1.0/r_t[j] + 1.0/(n_t[j] - r_t[j])
+ 1.0/r_c[j] + 1.0/(n_c[j] - r_c[j]));
}
いずれかの成功数が0だったり, 試行回数と同じだったりすると, この定義には問題が発生します. そうなる場合には, 2項分布で直接モデリングするか, 正則化しない標本対数オッズを使うのではなく, 別の変換を使う必要があります.
変換したデータができたら, 二つのメタアナリシスの標準型を使うことができます. 最初は, いわゆる「固定効果」モデルで, 全体的なオッズ比について単一のパラメータを仮定します. このモデルはStanでは以下のようにコーディングされます.
parameters {
real theta; // 全体的な治療の効果, 対数オッズ
}
model {
y ~ normal(theta,sigma);
}
y
についてのサンプリング文はベクトル化されており, 以下と同じ効果があります.
for (j in 1:J)
y[j] ~ normal(theta,sigma[j]);
このモデルにはtheta
の事前分布を含めるのが普通ですが, y
が固定されており, \(\mathsf{Normal}(y\mid\theta,\sigma) = \mathsf{Normal}(\theta\mid y,\sigma)\)ですので, モデルを正則にするためにどうしても必要というわけではありません.
治療の効果が臨床試験によって変動しうる, いわゆる「ランダム効果」をモデリングするには, 階層モデルを使うことができます. パラメータには, 試験ごとの治療の効果と, 階層事前分布のパラメータを含め, ほかの未知の量をともに推定します.
parameters {
real theta[J]; // 試験ごとの治療の効果
real mu; // 平均の治療の効果
real<lower=0> tau; // 治療の効果の偏差
}
model {
y ~ normal(theta,sigma);
theta ~ normal(mu,tau);
mu ~ normal(0,10);
tau ~ cauchy(0,5);
}
ベクトル化したy
のサンプリング文は変化ないように見えますが, パラメータtheta
はベクトルになっています. theta
のサンプリング文もベクトル化されており, 超パラメータmu
とtau
はそれ自身が, データのスケールと比較して幅の広い事前分布を与えられています.
Rubin (1981)は, 8つの学校での大学進学適性試験(Scholatic Aptitude Test, SAT)の指導の処理効果に関して, 各学校での標本処理効果と標準誤差に基づいて, 階層ベイズでメタアナリシスを行なっています. 2
2 Gelman et al. (2013) 5.5節のこのデータについてのモデルは, Stan example modelリポジトリ, http://mc-stan.org/documentation にデータとともに入っています.
Smith et al. (1995)とGelman et al. (2013, Section 19.4)には, 二項データそのままに基づいたメタアナリシスがあります. Warn et al. (2002)は, 二項データの変換の際に対数オッズ比の代替法を使うと, モデリングにどのような影響があるかを考察しています.
試験に特有の予測変数が利用できるならば, 試験ごとの治療の効果\(\theta_j\)として回帰モデルに直接含めることができます.
Stanは離散パラメータのサンプリングをサポートしていません. そのため, BUGSやJAGSのモデルで, 離散パラメータ(すなわち, 離散値をとる確率変数のノード)のあるものをそのまま移植することはできません. それでも, 離散パラメータを周辺化消去することにより, 上下限のある離散パラメータを含むたくさんのモデルをコーディングすることができます. 1この章では, 潜在離散パラメータを含むモデルのうち広く使われているものいくつかをコーディングする方法を紹介します. この後の17章では, クラスタリングモデルについて, 潜在離散パラメータを含むモデルについてさらに検討します.
1この計算は, 期待値最大化(EM: Expectation Maximization)アルゴリズムに含まれるものに似ています(Dempster et al., 1977).
離散パラメータを周辺化消去するには, 同時確率関数の何らかの代数計算が必要になりますが, この計算のうれしい副産物が, 周辺化された変数の事後期待値です. その変数がモデル中で関心のある量であることが多いですし, また事後期待値は, 離散パラメータのサンプリングによって推定された期待値ではなく, とりうる値すべてについての期待値が用いられます. そのため, 分布の裾もしっかりと探られるほか, 個々のiterationベースでも, より効率的なサンプリングが可能になります.
期待値最大化(EM: Expectation Maximization)アルゴリズムはじめ, 標準的な最適化アルゴリズムは多くが, 最尤推定アルゴリズムを記述する応用統計学の論文として公表されています. Stanでモデルをコーディングするのに必要な周辺化は, まさにこのような論文に由来します.
最初の例は, 1851年から1962年の, イギリスの炭鉱災害のモデルです. 2
2このデータの出典は(Jarrett, 1979)ですが, この論文自体は, 前からあるデータ集を訂正した短信です.
Fonnesbeck et al. (2013)の3.1節では, 年\(t\)における災害発生数\(D_{t}\)(訳注: 原文は'disaster rate'ですが, 原典では'number of disasters'となので, 「災害発生数」と思われます)のポアソンモデルが紹介されています. このモデルには, 初期発生率(\(e\))と後期発生率(\(l\))の2つの発生率パラメータがあり, ある時点\(s\)で切り替わるとされています. フルモデルは, 潜在離散パラメータ\(s\)を使うと以下のようになります.
\[\begin{array}{ll}e &\sim \mathrm{Exponential}(r_e) \\ l &\sim \mathrm{Exponential}(r_l) \\ s &\sim \mathrm{Uniform}(1,T) \\ D_t &\sim \mathrm{Poisson}(t < s\;?\;e : l)\end{array}\]
最後の行では条件演算子(3値演算子ともいう)を使っています. これは, Cおよびそれに類する言語から借りてきたものです. 条件演算子は, Rのifelse
と同じ挙動を示しますが, より簡潔な記法を使い, 疑問符(?)とコロン(:)で3つの引数を区切ります. 条件演算子は以下のように定義されます.
\[c\;?\;x_1 : x_2 = \begin{cases} x_1 \quad c\text{が真(つまり非零)のとき} \\ x_2 \quad c\text{が偽(つまり零)のとき} \end{cases}\]
Stan自身は今のところ条件演算子をサポートしていませんが, いずれサポートする予定です.
このモデルをStanでコーディングするには, 離散パラメータ\(s\)を周辺化消去して, 確率関数\(p(e,l,D_{t})\)の対数を定義するモデルを作らなくてはなりません. フル同時確率は以下のようになります.
\[\begin{array}{ll}p(e,l,s,D) &= p(e)p(l)p(s)p(D \mid s,e,l) \\ &= \mathrm{Exponential}(e \mid r_{e}) \mathrm{Exponential}(l \mid r_{l}) \mathrm{Uniform}(s \mid 1, T) \\ & \quad \prod_{t=1}^{T}\mathrm{Poisson}(D_{t} \mid t<s\;?\;e : l)\end{array}\]
周辺化のため, 別のやり方で事前分布と尤度とに分解します.
\[p(e,l,D) = p(e,l)p(D \mid e,l)\]
ここで, \(s\)を周辺化した尤度は以下のように定義されます.
\[\begin{array}{ll}p(D \mid e,l) &= \sum_{s=1}^{T}p(s,D \mid e,l) \\ &= \sum_{s=1}^{T}p(s)p(D \mid s,e,l) \\ &= \sum_{s=1}^{T}\mathrm{Uniform}(s \mid 1,T)\prod_{t=1}^{T}\mathrm{Poisson}(D_{t} \mid t<s\;?\;e:l)\end{array}\]
Stanは対数スケールで処理をおこないますので, 対数尤度が必要です.
\[\begin{array}{ll}\log p(D \mid e,l) &= \mathrm{log\_sum\_exp}_{s=1}^{T}\left(\log\mathrm{Uniform}(s \mid 1,T)\right. \\ &\quad\left. + \sum_{t=1}^{T}\log\mathrm{Poisson}(D_{t} \mid t<s\;?\;e : l)\right)\end{array}\]
ここで, log_sum_exp関数は以下のように定義されます.
\[\mathrm{log\_sum\_exp}_{n=1}^{N}\alpha_{n} = \log\sum_{n=1}^{N}\exp(\alpha_{n})\]
log_sum_exp関数は, Stanではlog_sum_exp
として組み込まれていますので, これを使ってモデルをそのままコーディングできます. これにより, 算術的にも安定し, 混合分布モデルの計算も効率的になります.
変化点モデルのStanのプログラムは図15.1に示します. 変換パラメータ(transformed parameter) lp[s]
は\(\log p(s,D \mid e,l)\)の値を格納します.
このモデルは, デフォルト設定のNUTSによるMCMCで簡単に当てはめができます. 収束はとても速く, サンプリングでは, おおよそ2回の繰り返しあたり1個の有効サンプルが得られます. 相対的に小さなモデルなので(時間についての内部の2重ループはおおよそ20000ステップ), とても速いのです.
lp
は変換パラメータ(transformed parameter)として宣言されていますので, 各変化点についてiterationごとのlp
の値を得ることもできます. もしlp
の値に関心がないのであれば, 局所変数としてコーディングできます. この場合, iterationごとに値を保存することによるI/Oのオーバヘッドがなくなります.
data {
real<lower=0> r_e;
real<lower=0> r_l;
int<lower=1> T;
int<lower=0> D[T];
}
transformed data {
real log_unif;
log_unif <- -log(T);
}
parameters {
real<lower=0> e;
real<lower=0> l;
}
transformed parameters {
vector[T] lp;
lp <- rep_vector(log_unif, T);
for (s in 1:T)
for (t in 1:T)
lp[s] <- lp[s] + poisson_log(D[t], if_else(t < s, e, l));
}
model {
e ~ exponential(r_e);
l ~ exponential(r_l);
increment_log_prob(log_sum_exp(lp));
}
図15.1: 変化点モデル. 災害発生数D[t]
は, 変化点の前ではある発生率e
に, 変化点の後では別の発生率l
に従います. 変化点自身はs
であり, 本文の記述のように周辺化消去されています.
あるiterationにおけるlp[s]
の値は, そのiterationにおける初期発生率\(e\)と後期発生率\(l\)の値を用いて, \(\log p(s,D \mid e,l)\)により与えられます. 収束後はiterationごとに, 初期および後期災害発生率\(e\)および\(l\)が, 事後の\(p(e,l \mid D)\)からMCMCサンプリングにより抽出され, 関連するlp
が計算されます. lp
の値は, 各iterationにおけるその時点での\(e\)と\(l\)の値にもとづいて, \(p(s \mid e,l,D)\)を計算することで正規化できるでしょう. iteration全体を平均すると, 変化点が\(s\)であることの正規化されていない確率の推定値が得られます.
\[\begin{array}{ll}p(s \mid D) &\propto q(s \mid D) \\ &= \frac{1}{M}\sum_{m=1}^{M}\exp(\mathrm{lp}[m,s])\end{array}\]
ここで, lp
[\(m\),\(s\)]は, 事後抽出\(m\)における変化点\(s\)についてのlp
の値を表します. 抽出全体を平均すると, \(e\)と\(l\)の方が周辺化消去され, ある繰り返しでの\(e\)と\(l\)の値には結果は依存しなくなります. 最終的に正規化すると, 求めたい量, すなわちデータ\(D\)を条件とする, \(s\)が変化点であることの事後確率が得られます.
\[p(s \mid D) = \frac{q(s \mid D)}{\sum_{s'=1}^{T}q(s' \mid D)}\]
Stan 2.4のデフォルトのMCMC実装を使って計算した\(\log p(s \mid D)\)の値のグラフを図15.2に示します.
図15.2: 変化点の事後推定値. 左) lp
を使って解析的に計算した, 各年が変化点である対数確率. 右) lp
を使って生成した事後分布における変化点の抽出の頻度. 左のグラフは対数スケールで, 右のグラフは線形スケールです. 右側のグラフではサンプリングのために年の範囲が狭くなっていることに注意. \(s\)の事後平均はおよそ1891です.
generated quantities
ブロックを利用すると, 組込みの擬似乱数発生器を使って離散パラメータ値を抽出することができるでしょう. 例えば, lp
を上のように定義すると, 繰り返しごとのs
のランダムな値を抽出するプログラムは以下のようになります.
generated quantities {
int<lower=1,upper=T> s;
s <- categorical_rng(softmax(lp));
}
s
の抽出の事後分布のヒストグラムを図15.2の右側に示します.
期待値について計算するのと比べると, 離散サンプリングはとても非効率的です. とくに分布の裾ではそうですので, こうした手法は, 分布からの抽出が明示的に必要なときにだけ使うべきです. そうでないときは期待値を計算すべきでしょう. softmax(lp)
が与えられたときのs
の事後分布にもとづいてgenerated quantities
ブロックで計算できます.
s
について生成された離散サンプルは, 他のパラメータとの共分散を計算するのにも使えます. このサンプリング手法は単純ですが, 期待される共分散をlp
を使って計算するのが(同じ程度の精度を得るのに必要な繰り返しがはるかに少ないという意味で)より統計学的に効率的です.
複数の変化点を持たせるのも原理的には難しいことはありません. 問題は計算が増えることだけです. 1変化点では1次だった周辺化消去が, 2変化点では2次になり, 3変化点では3次, というふうになります. 変化点が2つのときは, 期間のパラメータは3つ, e
, m
, l
となり, 変化点についての2つのループと, 時間全体についての1つのループを設定します. 変化点が2つなので, 対数密度は行列に格納されます.
matrix[T,T] lp;
lp <- rep_matrix(log_unif,T);
for (s1 in 1:T)
for (s2 in 1:T)
for (t in 1:T)
lp[s1,s2] <- lp[s1,s2]
+ poisson_log(D[t], if_else(t < s1, e, if_else(t < s2, m, l)));
この行列は, log_sum_exp
に渡す前にto_vector
でベクトルに戻すことができます.
生態学の野外調査法で広く応用されているものに, 動物を捕獲(または視認)し, (タグなどで)標識して, それから放すという方法があります. このプロセスはさらに1回以上繰り返され, 多くの場合は調査継続中の集団(訳注:'population'は, 生態学では「個体群」と訳すのが普通ですが, 遺伝学では「集団」と訳しますし, その方がわかりやすいでしょうから, ここでは「集団」と訳しています. )に対して行なわれます. 結果のデータは, 集団サイズを推定するのに使うことができます.
最初の小節では, 潜在離散パラメータを含まないごく単純な捕獲再捕獲モデルを記述します. その後の小節では, 動物の死についての潜在離散パラメータを含むCormack-Jolly-Seberモデルを記述します.
もっとも単純な場合では, 1段階の標識再捕獲調査から以下のデータが得られます.
目的の推定対象は以下です.
上の説明と話が違うのですが, このモデルでは\(N\)を連続パラメータとしましょう. 集団は有限でなくてはならないといっても, それを表すパラメータはその限りではありません. このパラメータは, 集団サイズの推定値を実数値で求めるのに使います.
Lincoln-Petersen法(Lincoln, 1930; Petersen, 1896)を集団サイズの推定に使います.
\[\hat{N} = \frac{MC}{R}\]
この集団推定値は, 再捕獲された動物の数が二項分布に従うという確率モデルに基づいたものでしょう.
\[ R \sim \mathrm{Binomial}(C, M/N) \]
ここでは, 2回目に捕獲された動物の数の合計(\(C\))と, 再捕獲率\(M/N\)を与えています. 再捕獲率は, 全個体数\(N\)のうち, 最初に標識された個体の割合に等しいとしています.
Lincoln-Petersen推定量を確率的にしたものは, 図15.3のようにStanではそのままコーディングできます. このときのLincoln-Petersen推定値はこのモデルでは最尤推定値(MLE)です.
data {
int<lower=0> M;
int<lower=0> C;
int<lower=0,upper=min(M,C)> R;
}
parameters {
real<lower=(C - R + M)> N;
}
model {
R ~ binomial(C, M / N);
}
図15.3: Lincoln-Petersen推定量の確率的定式化. 1段階標識再捕獲調査からのデータに基づいて集団サイズを推定します. ありえない値を効率的に除くため, Nの下限は必要です.
最尤推定値が確実にLincoln-Petersen推定値になるように, \(N\)には非正則一様事前分布が使われています. 可能であれば, 調査している集団についての知識に基づいて, もっと情報のある事前分布を使うこともできるでしょう(そしてそうすべきです).
このモデルでトリッキーなところは, 集団サイズ\(N\)の下限\(C-R+M\)でしょう. これより小さい値では, 再捕獲された\(C\)匹の動物から\(R\)だけのサンプルを抽出することができないので, この下限より小さな値は取りえないのです. 変換された(制約のない)空間でのパラメータに制限がかからないようにして, サンプリングと最適化が制約なく行なわれるのを確実にするため, この下限を実装することは必要です. \(C\)について宣言されたこの下限は, 次の変数変換を暗黙のうちに伴います: \(f:(C-R+M, \infty) \rightarrow (-\infty, +\infty)\), \(f\)は\(f(N) = \log(N-(C-R+M))\)と定義されます. 下限を宣言する変数に使われる変換についてさらに知るには34.2節を参照してください.
Cormack-Jolly-Seber (CJS)モデル(Cormack, 1964; Jolly, 1965; Seber, 1965)は, 死亡により集団が時間的に変化する開放集団モデルです. ここで紹介している例は(Schofield, 2007)から多くを引いてきています.
基本データは以下のとおりです.
各個体は少なくとも1回は捕獲されていると仮定されます. これは, 最初に捕獲された後という条件付きでしか個体が情報に寄与しないからです.
このモデルには2個のベルヌーイパラメータがあります.
これらパラメータには一様事前分布を与えますが, 実際には情報を使って事前分布を狭くするべきでしょう.
CJSモデルはまた, 潜在離散パラメータ\(z_{i,t}\)を使います. これは, 各個体\(i\)が時点\(t\)で生存しているかどうかを示すものです.
\[z_{i,t} \sim \mathrm{Bernoulli}(z_{i,t-1}\;?\;\phi_{t-1} : 0)\]
(訳注:原文では0と\(\phi_{t-1}\)が逆になっていますが, 条件演算子の定義からするとこちらの方が正しいはずです. )
この条件によりゾンビが発生することを防ぎます. つまり, 動物はいったん死んだらならば, ずっと死んだままになります. データの分布は\(z\)を条件として単純に以下のように表されます.
\[y_{i,t} \sim \mathrm{Bernoulli}(z_{i,t}\;?\;p_{t} : 0)\]
(訳注:これも上の式と同様です. )
この条件により, 死亡した動物は捕獲されることがないという制約がつけられます.
この小節では, 3回の捕獲調査を行なった場合について, 個体の捕獲プロファイルの別を計数するというモデルの実装を紹介します. このモデルは, どの動物も同じ捕獲率と同じ生存率を持つという交換可能性を仮定しています.
潜在離散パラメータ\(z<sub>i,t</sub>\)の周辺化を簡単にするため, Stanのモデルでは, ある時点\(t\)で生存していた個体(死亡していれば, 再捕獲率は0です)がもう2度と捕獲されない確率\(\chi_{t}\)をうまく使います. この量は再帰的に定義されます.
\[\begin{array}{ll}\chi_{t} = \begin{cases} 1 &\quad t = Tのとき \\ (1 - \phi_{t}) + \phi_{t}(1 - p_{t+1})\chi_{t+1} &\quad t < Tのとき \end{cases}\end{array}\]
ある個体が最終調査時に捕獲された場合, このとき, もう捕獲調査はありませんので, もう捕獲されない確率は1になります. そのため, これが再帰のベースになります. \(\chi_{t+1}\)から, 再帰的に\(\chi_{t}\)を定義しますが, この場合には2つの確率が含まれます. (1)確率(\(1-\phi_{t}\))で, 次回調査時点まで生存しない, (2)確率\(\phi_{t}\)で, 次回調査時点まで生存するが, 次回調査時点では確率(\(1-p_{t+1}\))で捕獲されず, かつ, 確率\(\chi_{t+1}\)で, 時点\(t+1\)で生存していた後に2度と捕獲されない.
3回の捕獲調査の場合, ある個体についての捕獲/非捕獲のプロファイルが3つあることになります. これは2値の数字として以下のように自然に符号化されます.
プロファイル | 捕獲/1 2 3 | 確率 |
---|---|---|
0 | - - - | n/a |
1 | - - + | n/a |
2 | - + - | \(\chi_{2}\) |
3 | - + + | \(\phi_{2}\phi_{3}\) |
4 | + - - | \(\chi_{1}\) |
5 | + - + | \(\phi_{1}(1-p_{2})\phi_{2}p_{3}\) |
6 | + + - | \(\phi_{1}p_{2}\chi_{2}\) |
7 | + + + | \(\phi_{1}p_{2}\phi_{2}p_{3}\) |
履歴0, すなわち動物が1度も捕獲されない場合は観測不可能です. 捕獲された動物だけが観測されるからです. 履歴1, すなわち最終回にのみ動物が捕獲される場合は, CJSモデルでは情報は得られません. 捕獲/非捕獲の状態から情報が得られるのは, 前の捕獲を条件としたときだけだからです. 残りの場合には, 最後の列に示したように尤度への寄与があります.
\(\chi\)を使って直接こうした確率を定義することで, 動物が時点\(t\)で生きているかどうかを示す潜在2値パラメータは必要なくなります. この\(\chi\)の定義は, CJSモデルでの尤度(すなわち潜在離散パラメータを周辺化消去した)の定義では典型的です(Schofield, 2007, 9ページ).
Stanのモデルでは, パラメータ\(\phi\)と\(p\)に基づいて変換パラメータ(transformed parameter)として\(\chi\)を定義しています. model
ブロックでは, 各履歴についてその回数に応じて対数確率が加算されます. 2番目のステップは, ベルヌーイ分布の観測値を二項分布に, あるいはカテゴリカルな観測値を多項分布にまとめるのと似ています. ただし, このStanのプログラムでは組込みの確率関数を使わず, increment_log_prob
を使って直接コーディングしています.
data {
int<lower=0> history[7];
}
parameters {
real<lower=0,upper=1> phi[2];
real<lower=0,upper=1> p[3];
}
transformed parameters {
real<lower=0,upper=1> chi[2];
chi[2] <- (1 - phi[2]) + phi[2] * (1 - p[3]);
chi[1] <- (1 - phi[1]) + phi[1] * (1 - p[2]) * chi[2];
}
model {
increment_log_prob(history[2] * log(chi[2]));
increment_log_prob(history[3] * (log(phi[2]) + log(p[3])));
increment_log_prob(history[4] * (log(chi[1])));
increment_log_prob(history[5]
* (log(phi[1]) + log1m(p[2])
+ log(phi[2]) + log(p[3])));
increment_log_prob(history[6]
* (log(phi[1]) + log(p[2])
+ log(chi[2])));
increment_log_prob(history[7]
* (log(phi[1]) + log(p[2])
+ log(phi[2]) + log(p[3])));
}
generated quantities {
real<lower=0,upper=1> beta3;
beta3 <- phi[2] * p[3];
}
図15.4: Cormack-Jolly-Seber標識再捕獲モデルのStanプログラム. 3回の捕獲調査での観測のありなしという観測履歴ごとに個体の数をまとめています.
パラメータ\(\phi_{2}\)と\(p_{3}\), すなわち時点2での生存率(訳注:原文ではprobability of deathとなっていますが, \(\phi\)の定義は「生存率」です)と時点3での捕獲率とは識別可能ではありません. これは, 時点3で捕獲されなかったことの説明にこの両方ともが使えるからです. これらの積\(\beta_{3}=\phi_{2}p_{3}\)は識別可能です. Stanのモデルではbeta3
を生成量(generated quantity)として定義しています. 識別不可能なパラメータは, Stanのサンプラーの適応(adaptation)に問題を引き起こします. パラメータに制限があって, 正則一様分布が使われていたため, このモデルでは適応に大きな問題は発生しませんでしたが, 識別可能なパラメータとなるように定式化した方が良いでしょう. そのためには, \(p\)と\(\phi\)のパラメータについて階層モデルとして定式化するのもひとつの方法でしょう.
この小節では, 前の小節で紹介したような集合的なものではなく, 個体レベルで動くバージョンのCormack-Jolly-Seber (CJS)モデルを紹介します. また, これによりモデルは, 任意の調査回数に対応することができるようになります. データは, 捕獲調査の回数\(T\), 個体数\(I\), 個体\(i\)が時点\(t\)で観測されたかを示す論理値フラグ\(y_{i,t}\)からなります. Stanでは以下のように記述されます.
data {
int<lower=2> T;
int<lower=0> I;
int<lower=0,upper=1> y[I,T];
}
個体レベルモデルの利点に, 生存率や捕獲率に影響する個体の「ランダム効果」を含めることが可能になります. そのほか, \(T\)回の捕獲があった場合\(2^{T}\)個の観測履歴を展開した組み合わせを使わなくて良いという点もあります.
この個体CJSモデルは, いくつかの関数定義を含むように書かれています. はじめの2つは, transformed data
ブロックで使われており, 動物が捕獲された最初および最後の調査時点を計算します. 3
3別の方法として, この値をモデルの外で計算して, Stanのモデルに前処理済みデータとして与えることもできるでしょう. さらに別のコーディングとして, 捕獲イベントのときにその時点と捕獲された個体とを記録するという疎なものもあります.
functions {
int first_capture(int[] y_i) {
for (k in 1:size(y_i))
if (y_i[k])
return k;
return 0;
}
int last_capture(int[] y_i) {
for (k_rev in 0:(size(y_i) - 1)) {
int k;
k <- size(y_i) - k_rev;
if (y_i[k])
return k;
}
return 0;
}
...
}
この2つの関数は, transformed data
ブロックで各個体の最初と最後の捕獲時点を定義するのに使われます. 4
4入力された配列の個体が1度も捕獲されていないときは, 両方の関数とも0を返します. このモデルでは, どの確率計算もより前の捕獲を条件としているので, 捕獲されていない個体はモデルの推定に無関係です. 通常はこうしたデータは除かれるのですが, このプログラムでは, 対数確率関数への寄与はなくとも, そうしたデータが含まれてもよいようになっています.
transformed data {
int<lower=0,upper=T> first[I];
int<lower=0,upper=T> last[I];
vector<lower=0,upper=I>[T] n_captured;
for (i in 1:I)
first[i] <- first_capture(y[i]);
for (i in 1:I)
last[i] <- last_capture(y[i]);
n_captured <- rep_vector(0,T);
for (t in 1:T)
for (i in 1:I)
if (y[i,t])
n_captured[t] <- n_captured[t] + 1;
}
transformed data
ブロックではn_captured[t]
も定義しています. これは, 時点t
における捕獲数の合計値です. n_captured[t]
変数は, 整数配列ではなくベクトルとして定義されています. これにより, 各時点での集団推定値をモデル化するためにgenerated quantities
ブロックで要素ごとのベクトル操作を使えるようになります.
パラメータと変換パラメータは前のとおりですが, ある個体が時点\(t\)で生きていたときにその2度と捕獲されない確率として, ベクトルchi
を全部計算する関数が定義されています.
parameters {
vector<lower=0,upper=1>[T-1] phi;
vector<lower=0,upper=1>[T] p;
}
transformed parameters {
vector<lower=0,upper=1>[T] chi;
chi <- prob_uncaptured(T,p,phi);
}
prob_uncaptured
の定義はfunctions
ブロックに置きます.
functions {
...
vector prob_uncaptured(int T, vector p, vector phi) {
vector[T] chi;
chi[T] <- 1.0;
for (t in 1:(T - 1)) {
int t_curr;
int t_next;
t_curr <- T - t;
t_next <- t_curr + 1;
chi[t_curr] <- (1 - phi[t_curr])
+ phi[t_curr]
* (1 - p[t_next])
* chi[t_next];
}
return chi;
}
}
この関数は\(\chi_{t}\)の数学的定義をそのままなぞったものです. 再帰を繰り返しに展開し, T
から1までchi
の要素を定義しています.
前もって計算させた量を与えられたもとで, model
ブロックではCJSモデルの対数尤度関数をそのままエンコードしています. すべてのパラメータの事前分布はデフォルトの一様事前分布とし, パラメータp
およびphi
と, p
およびphi
から定義される変換パラメータchi
とが与えられたもとでの観測q
の対数確率をモデルは単純にエンコードしています.
model {
for (i in 1:I) {
if (first[i] > 0) {
for (t in (first[i]+1):last[i]) {
1 ~ bernoulli(phi[t-1]);
y[i,t] ~ bernoulli(p[t]);
}
1 ~ bernoulli(chi[last[i]]);
}
}
外側のループは全個体についてのもので, 1度も捕獲されていない個体i
は飛ばすという条件がついています. 1度も捕獲されていないことのチェックには, 1度も捕獲されていない個体では初回および最終捕獲の関数が0を返すという決まりにより, first
が0になることを利用しています.
内側のループは個体i
について, 個体が確率phi[t-1]
で生存することに基づいて対数確率をまず加算します. 結果が1で固定されているのは, 最初と最後の捕獲の間にはその個体は生存していることが確実だからです(ゾンビはいません). このループは最初の捕獲の後から始まります. これはCJSモデルでは全情報が最初の捕獲を条件としているからです.
内側のループでは, 個体i
の時点t
における観測捕獲状態y[i,t]
は, 時点t
における捕獲率p[t]
に基づいたベルヌーイ分布に従います.
内側のループの後には, ある動物が時点last[i]
で観測されてから2度と確認されない確率を入れています. これは, last[i]
が, 動物i
が観測された最後の調査時点と定義されているからです.
前の小節で記述した集合モデルと同じく, このモデルでもphi[T-1]
とp[T]
は識別可能ではありません. 識別可能なのはその積beta
です. そこで, genetated quantityとしてbeta
を定義し, 収束の監視と報告の対象とします.
generated quantities {
real beta;
...
beta <- phi[T-1] * p[T];
...
}
パラメータp[1]
もモデル化されていませんし, 0から1の一様分布となるだけでしょう. もっとうまくモデルをつくるなら, 階層的成分や, 時系列成分を含めるようにするとよいかもしれません. その場合, p[1]
は未知の初期状態となり, phi[T-1]
とp[T]
はともに識別可能となるでしょう.
generated quantitiesでは, 各時点t
における集団サイズの平均の推定値も計算します. 方法は, 単純な標識再捕獲モデルと同様で, 時点t
における捕獲個体数を, 時点t
における捕獲確率で割って求めます. ここでは, generated quantities
ブロックで, vector
の要素ごとの除算演算子(./)を使用します.
generated quantities {
...
vector<lower=0>[T] pop;
...
pop <- n_captured ./ p;
pop[1] <- -1;
}
このモデルは, 個体すべてが同じ捕獲率と持つというふうにモデル化されていますが, 一般化は簡単にできるでしょう. その場合, 個体レベルの入力値を予測変数として使って, これに基づくロジスティック回帰を使います.
かけはなれた仕事のように見えますが, アイテムをカテゴリーに分けて評価/符号化/注釈するのと, 病気か病気でないかを診断検査することとの間には共通する特徴があり, 両者の統計的特性は同じようにモデル化できます.
さまざまな感度と特異度の条件で診断検査を行なうとします. 感度とは, 検査をうける人が疾患ありの時に正しく検査が陽性となる確率です. 特異度は, 検査をうける人が疾患なしの時に正しく検査が陰性となる確率です. 乳癌の検査方法であるマンモグラムと穿刺生検を例にします. マンモグラムの感度は高く, 特異度は低いのですが, これは偽陽性が多いことを意味します. 一方, 穿刺生検は反対で, 感度は低く特異度は高いので, 偽陰性が多くなります.
こうした調査ではいくつかの推定対象があります. 疫学調査では, 集団内でマラリアのようなある種の感染症に罹っている患者数に関心があるでしょう. 検査法を開発する研究では, 新しい検査法の診断正答率に関心があるでしょう. 健康管理従事者が行なう検査では, ある患者の病状に関心があるでしょう.
データに符号をつける(評価や注釈も同様)仕事を与えられることはよくあります. 例えば, 論文誌や助成金の審査員は提出物を評価しますし, 政治学の研究では, キャンペーンコマーシャルが攻撃的広告か否かを符号化したりするでしょう. 自然言語処理では, ツイートが全体的な感情に照らしてポジティブかネガティブか注釈をつけるでしょうし, X線画像を見る歯科医は患者が虫歯かどうかを分類します. このような場合, データに符号をつける人は診断検査の役を演じており, すべての場合に共通するような推定対象があります. すなわち, データにつけた符号の正答率とバイアス, 符合をつけたアイテムの真のカテゴリー, データ中のアイテムのさまざまなカテゴリーはそれぞれいくつあるか, といったことです.
この小節では, カテゴリー評価だけに絞って, 離散パラメータを周辺化消去してStanでモデリングすることを考えます.
Dawid and Skene (1979)は, データ符号化にノイズのある測定モデルを導入して, 医師による記録から患者の履歴についてわかることを符号化するという疫学的状況に応用しています. 同じモデルは診断法についても使用できます.
データは, \(J\)評価者(診断検査), \(I\)アイテム(患者), \(K\)カテゴリー(状態)の注釈からなり, \(y_{i,j} \in 1:K\)は, アイテム\(i\)について評価者\(j\)がつけた評価です. ある状態についての診断検査の状況では, 評価者は診断法であり, 多くは\(K = 2\), つまり疾患があるかないかの信号の値になります. 5
全評価者が全アイテムを1度だけ評価するとは限らないという状況にDawid and Skeneのモデルを拡張するのは比較的素直にできます.
5診断法では順序尺度となることも多くあります. 腫瘍学的診断での癌の段階や, 歯科診断での虫歯の深刻度といったものです. Dawid and Skeneのモデルはそのままでも使えますし, 順序ロジスティック回帰として, 潜在連続値の評価とカットポイントを使い, 順序をつけて評価するように自然に一般化してもよいでしょう.
このモデルは3つのパラメータを持ちます. 最初のひとつは離散的です.
あるアイテムの真のカテゴリーは, アイテムの出現率をもとに単純なカテゴリカル分布で生成されると仮定します.
\[z_{i} \sim \mathsf{Categorical}(\pi)\]
アイテム\(i\)についての評価者\(j\)の評価\(y_{i,j}\)は, カテゴリー\(z_{i}\)のアイテムに対する評価者\(i\)(訳注: \(j\)が正しい?)のカテゴリカルな応答としてモデル化します. 6
6添字の\(z[i]\)は, \(z_{i}\)を読みやすくしたものです.
\[y_{i,j} \sim \mathsf{Categorical}(\theta_{j,\pi_{z[i]}})\]
Dawid and Skeneは\(\theta\)と\(\pi\)の最尤推定値を提供しています. これにより, 各\(z_{i}\)の確率の推定値を生成できます.
Dawid and Skeneの最尤モデルをまねるため, パラメータ\(\theta_{j,k}\)と\(\pi\)には\(K\)次元単体についての一様事前分布を与えます. 一般化してディリクレ事前分布にするのも簡単です.
\[\pi \sim \mathsf{Dirichlet}(\alpha)\]
および
\[\theta_{j,k} \sim \mathsf{Dirichlet}(\beta_{k})\]
\(\alpha\)と\(\beta\)は固定された値を持つハイパーパラメータです. \(\theta_{j,k}\)の事前分布は\(k\)によって変わることができるようにしないといけません. そうすると例えば, 偶然よりも良い注釈をする注釈者が, ランダムまたは反対に注釈する注釈者よりも高い事前確率を持てるように\(\beta_{k,k}\)を大きくできるようになります.
\(J\)だけ評価者がいるので, \(\beta\)に階層事前分布をつけて, 評価者の正答率とバイアスの推定値を部分的に合算するようにモデルを拡張するのは自然なことでしょう.
真のカテゴリーのパラメータ\(z\)は離散的ですので, 同時事後分布から周辺化消去して, Stanでサンプリングあるいは最尤推定ができるようにします. 同時事後分布の因子は以下のようになります.
\[p(y,\theta,\pi) = p(y \mid \theta, \pi)p(\pi)p(\theta)\]
ここで, \(p(y \mid \theta,\pi)\)は, 次式から\(z\)を周辺化消去して得られます.
\[p(z, y \mid \theta,\pi) = \prod_{i=1}^{I}\left(\mathsf{Categorial}(z_{i} \mid \pi) \prod_{j=1}^{J}\mathsf{Categorical}(y_{i,j} \mid \theta_{j,z[i]})\right)\]
この式はアイテムごとの形式にできます.
\[p(y \mid \theta,\pi) = \prod_{i=1}^{I}\sum_{k=1}^{K}\left(\mathsf{Categorial}(z_{i} \mid \pi) \prod_{j=1}^{J}\mathsf{Categorical}(y_{i,j} \mid \theta_{j,z[i]})\right)\]
欠測データモデルでは, 内側の積の部分で観測されたかどうかのラベルだけを使うようにすればよいでしょう.
Dawid and Skene (1979)は, まったく同じ式を彼らの式(2.7)で導出しています. これは, 彼らの期待値最大化(EM)アルゴリズムにおけるEステップに必要となるものです. Stanでは対数スケールでの周辺化確率関数が必要になります.
\[\log p(y \mid \theta,\pi) = \sum_{i=1}^{I}\log\left(\sum_{k=1}^{K}\exp\left(\log\mathsf{Categorial}(z_{i} \mid \pi) + \sum_{j=1}^{J}\log\mathsf{Categorical}(y_{i,j} \mid \theta_{j,z[i]})\right)\right)\]
この式はStanの組込みlog_sum_exp
関数を使ってそのままコーディングできます.
Dawid and SkeneモデルのStanプログラムを図15.5に示します. NUTSを使うStanのモデルは, ばらばらの初期値から素早く収束し, よく混合されます. 離散パラメータをギブズサンプリングするように実装した同等のモデルではそうはいきません. 適切な弱情報事前分布として, \(\alpha_{k} = 3\), \(\beta_{k,k} = 2.5K\), \(\beta_{k,k'} = 1\)(\(k \neq k'\)のとき)としています. \(\alpha\)と\(\beta_{k}\)を単位ベクトルにして, 最適化を適用すると, Dawid and Skene (1979)の期待値最大化(EM)アルゴリズムと同じ結果が得られるでしょう.
data {
int<lower=2> K;
int<lower=1> I;
int<lower=1> J;
int<lower=1,upper=K> y[I,J];
vector<lower=0>[K] alpha;
vector<lower=0>[K] beta[K];
}
parameters {
simplex[K] pi;
simplex[K] theta[J,K];
}
transformed parameters {
vector[K] log_q_z[I];
for (i in 1:I) {
log_q_z[i] <- log(pi);
for (j in 1:J)
for (k in 1:K)
log_q_z[i,k] <- log_q_z[i,k]
+ log(theta[j,k,y[i,j]]);
}
}
model {
pi ~ dirichlet(alpha);
for (j in 1:J)
for (k in 1:K)
theta[j,k] ~ dirichlet(beta[k]);
for (i in 1:I)
increment_log_prob(log_sum_exp(log_q_z[i]));
}
図15.5: Dawid and Skene (1979)の評価(あるいは診断精度)モデルのStanプログラム. このモデルは, 離散パラメータ\(z\)を周辺化消去し, 非正規化条件付き確率\(\log q(z_{i} = k \mid \theta,\pi)\)をlog_q_z[i,k]
に格納しています.
log_q_z[i]
は変換パラメータ(transformed parameter)として定義されます. これは, \(p(z_{i} \mid \theta,\pi)\)の対数(正規化されていません)をコード化したものです. 繰り返しごとに, その繰り返しでの\(\theta\)と\(\pi\)との値を条件とした値が得られます. softmax関数をlog_q_z[i]
に適用することで, 事後の\(z_{i}\)の確率質量関数に相当する単体が得られます. これを各繰り返しについて平均することで, それぞれの\(z_{i}\)についての事後確率分布が得られます.
Stanは, まばらなデータ構造も不ぞろいなデータ構造も直接はサポートしていません. しかし, プログラミングの努力をいくらか払うことで両方とも扱うことができます. 43章では, 特別な目的のための疎行列と密ベクトルとの積を紹介しており, 適用可能なところではこれを使うことができますが, この章ではもっと一般的なデータ構造を対象とします.
まばらなデータ構造のコーディングは難しくなく, 行列状のデータ構造をデータベース状のデータ構造に変換するのと同じです. 例えば, 9.11節で議論したIRTモデルのまばらなデータを考えます. \(J\)人の生徒と\(K\)問の問題があり, すべての生徒がすべての問題を解くとすると, データは, \(J \times K\)の解答の配列として宣言するのが実用的です.
data {
int<lower=1> J;
int<lower=1> K;
int<lower=0,upper=1> y[J,K];
...
model {
for (j in 1:J)
for (k in 1:K)
y[j,k] ~ bernoulli_logit(delta[k] * (alpha[j] - beta[k]));
...
すべての生徒がすべての問題に答えているわけではないなら, この密な配列のコーディングはもはや動作しません. Stanは未定義の値をサポートしていないからです. 図16.1は, \(J=3\)で\(K=4\)の場合の例で, 欠測の応答はRと同じくNAとしています. StanにはRのNA値のサポートはありませんので, このデータ構造を直接使うことはできません. かわりに, データベースのような「長い形式」に変換する必要があります. そのためには, 値とは別に, \(j\)および\(k\)のインデックスを示す列を持たせます. 例えば, \(jj\)と\(kk\)をインデックスのために使うと(Gelman and Hill, 2007による), データ構造は, 図16.1の右側の例のようにコーディングできます. この例では, \(y_{1,1}=0\), \(y_{1,2}=1\)などとなっており, 最後は\(y_{3,2}=1\)です. ここにない項目はすべて未定義です.
\[y = \left[\begin{array}{cccc} 0 & 1 & \mathrm{NA} & 1 \\ 0 & \mathrm{NA} & \mathrm{NA} & 1 \\ \mathrm{NA} & 0 & \mathrm{NA} & \mathrm{NA} \end{array}\right] \quad \begin{array}{ll|l}jj & kk & y \\ \hline 1 & 1 & 0 \\ 1 & 2 & 1 \\ 1 & 4 & 1 \\ 2 & 1 & 0 \\ 2 & 4 & 1 \\ 3 & 2 & 0 \end{array}\]
図16.1: まばらな配列をStanでコーディングした例. 左側は, R由来のNA記法を使った疎行列\(y\)の定義です(これはStanではサポートされていません). 右側は, 同じ疎行列\(y\)をデータベース状にエンコーディングしたもので, これはStanで直接扱うことができます. 最初の2列, \(jj\)と\(kk\)はインデックスを示し, 最後の列\(y\)がその値を示します. 例えば, 右側のデータベース状のデータ構造の5行目は\(y_{2,4}=1\)を示します.
定義されている\(y\)の数を\(N\)とすると, ここでは\(N=6\)です. データとモデルは以下のように定式化できます.
data { ...
int<lower=1> N;
int<lower=1,upper=J> jj[N];
int<lower=1,upper=K> kk[N];
int<lower=0,upper=1> y[N];
...
model {
for (n in 1:N)
y[n] ~ bernoulli_logit(delta[kk[n]]
* (alpha[jj[n]] - beta[kk[n]]));
...
欠測値がない場合には, この2種のモデルの定式化は完全に同じ対数事後密度を生成します.
不ぞろいな配列とは, 長方形になっておらず, 項目によってサイズが違うような配列のことです. この種の構造は, 配列ごとに観測回数が違うときに出てきます.
不ぞろいなデータ構造を扱う一般的な方法は, 前の節で議論したように, 完全なデータベース状のデータ構造に移行することです. より簡潔に, 線形の配列に何らかのインデックスをつける方法もあります.
例えば, 3群があって, それぞれの観測回数が異なるようなデータ構造を考えます.
とても単純な, 切片が変化するモデルを仮定します. ベクトル化した記法を使うと, 尤度は以下のようになります(訳注: 原文では積(\(\prod\))になっていますが, 対数にしているので和(\(\sum\))のはずです).
\[\sum_{n=1}^{3}\log\mathsf{Normal}(y_{n}\mid\mu_{n},\sigma)\]
これを直接Stanでエンコードする方法はありません.
まばらなデータの例のように, 完全なデータベース型の構造を使うことができるでしょうが, これは非効率です. 不要なインデックスがスペースを無駄にしますし, ベクトルによる密度の演算ができません. このデータをコーディングするもっと良い方法は, 単一のリストに値を持たせ, 各部分配列のサイズを示すデータ構造を別に用意するというものです. 図16.2の右側にこれを示しています. このコーディングでは, 値のための配列を1つだけ, それから各列のサイズのためにもう1つ別の配列を使っています.
\[\begin{minipage}[c]{0.35\textwidth} $y_1 = \left[1.3 \ \ 2.4\ \ 0.9\right]$ \\ $y_2 = \left[-1.8\ \ -0.1 \right]$ \\ $y_3 = \left[12.9\ \ 18.7\ \ 42.9\ \ 4.7\right]$ \end{minipage} \begin{minipage}[c]{0.60\textwidth} $z = [1.3\ \ 2.4\ \ 0.9\ \ -1.8\ \ -0.1\ \ 12.9\ \ 18.7\ \ 42.9\ \ 4.7]$ \\ $s = \{3\ \ 2\ \ 4 \}$ \end{minipage}\]
図16.2: 不ぞろいな配列をStanでコーディングした例. 左側は, サイズの異なる3行(\(y_1\)はサイズ3, \(y_2\)はサイズ2, \(y_3\)はサイズ4)からなる不ぞろいなデータ構造\(y\)の定義です. 右側は, Stanでこのデータをコーディングする方法の例です. 単一のベクトル\(y\)を使ってすべての値を保持し, 整数値の別の配列\(s\)に, 群ごとの行のサイズを持たせています. この例では\(y_{1}=z_{1:3}\), \(y_{2}=z_{4:5}\), \(y_{3}=z_{6:9}\)です.
するとこのモデルは, スライシング演算を使って, 以下のようにコーディングできます.
data {
int<lower=0> N; // 観測回数
int<lower=0> K; // 群の数
vector[N] y; // 観測値
int s[K]; // 群のサイズ
...
model {
int pos;
pos <- 1;
for (k in 1:K) {
segment(y, pos, s[k]) ~ normal(mu[k], sigma);
pos <- pos + s[k];
}
このコーディングでは十分なベクトル化が可能で, これはsegment()
によるベクトルのスライシング演算によって生じるコピーのコストに見合うものです.
データを教師なしでグループにまとめる方法は, 総じてクラスタリングと呼ばれます. 本章では, 広く用いられている統計的なクラスタリングモデルの内2つ, ソフト\(K\)-means法と潜在ディリクレ配分法(LDA)に関して, Stanでの実装について説明します. 本章にはさらに, 教師ありの場合のクラスタリングの一種とみなせるナイーブベイズ分類法が含まれます. 通常これらのモデルは, クラスタの割り当てに離散パラメータを用いて表現されます. しかしながら, Stanでは離散パラメータを周辺化消去することで, これらのモデルを混合モデルのように実装することができます(13章も参照してみてください).
\(K\)-meansクラスタリングとは, \(D\)次元のベクトルで表わされるデータをクラスタリングする方法です. 具体的に言うと, 分類される項目が\(N\)個存在し, その各々はベクトル\(y_{n} \in \mathbb{R}^{D}\)で表わされます. \(K\)-means法の「ソフト」版では, クラスタの割り当てが確率的になります.
\(K\)-meansクラスタリングは以下のアルゴリズムの様に, 通常幾何的な観点で記述されます(クラスタ数\(K\)とデータベクトル\(y\)は入力として仮定します).
このアルゴリズムは, 停止することが保証されています.
ソフト\(K\)-meansクラスタリングでは, クラスタの割り当ては複数のクラスタに渡る確率分布に基づき扱われます. ユークリッド距離と共分散が固定された多変量正規モデルの間の関係から, ソフト\(K\)-means法は, 多変量正規分布の混合モデルとして(Stanのコードも)表現することができます.
この全生成モデルでは, \(1:N\)の各データ点\(n\)に, 次のような対称で一様な確率でクラスタ\(z_n \in 1:K\)が割り当てられます.
\[ z_n \sim \mathsf{Categorical}(\mathbf{1}/K), \]
ここで\(\mathbf{1}\)は\(K\)次元の単位ベクトルですので, \(\mathbf{1}/K\)は対称な\(K\)単体となります. このようにして, このモデルではクラスタが割り当てられると, そこからおのずと各データ点が抽出されてゆくことが仮定されます. 「ソフト」さは, どのクラスタがデータ点を生成するかという不確実性のみから発生します.
データ点自体は, 割り当てられたクラスタ\(z_n\)から決定されるパラメータを持つ, 多変量正規分布より生成されます.
\[ y_n \sim \mathsf{Normal}(\mu_{z[n]}, \Sigma_{z[n]}) \]
本節での実装例では, 共分散行列には全クラスタ\(k\)に共通して固定された単位行列を仮定します.
\[ \Sigma_k = \text{diag\_matrix}(\mathbf{1}), \]
こうすると, 比例定数を除き, 多変量の対数正規分布を次のように直接実装することができます.
\[ \mathsf{Normal} \left(y_n \mid \mu_k, \text{diag\_matrix}(\mathbf{1})\right) \propto \exp \left(-\frac{1}{2}\sum_{d=1}^{D} (\mu_{k, d}-y_{n, d})^2 \right). \]
\(K\)-means法に対する空間的な観点では, 上式のかっこの内側の項が, まさにクラスタ平均\(\mu_k\)からデータ点\(y_n\)までのユークリッド距離(を半分にして符号を反転した値)になっていることに注意して下さい.
\(K\)-meansクラスタリングを実装した, 次のStanのプログラムを考えましょう. 6
data {
int<lower=0> N; // データ点の数
int<lower=1> D; // 次元数
int<lower=1> K; // クラスタ数
vector[D] y[N]; // 観測値
}
transformed data {
real<upper=0> neg_log_K;
neg_log_K <- -log(K);
}
parameters {
vector[D] mu[K]; // クラスタの平均
}
transformed parameters {
real<upper=0> soft_z[N,K]; // 規格化されていないクラスタ割当確率の対数
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 {
// 事前分布
for (k in 1:K)
mu[k] ~ normal(0,1);
// 尤度
for (n in 1:N)
increment_log_prob(log_sum_exp(soft_z[n]));
}
重心のパラメータに関する事前分布は, 独立な標準正規分布としています. この事前分布は別の分布に変更してもよく, 問題全般のスケールと位置に適合するような階層モデルにしてもよいでしょう.
パラメータはmu
のみであり, mu[k]
はクラスタ\(k\)の重心となります. 変換パラメータsoft_z[n]
には, 規格化されていないクラスタ割当確率の対数が含まれます. ベクトルsoft_z[n]
は, Stanの外部か, もしくはモデルのgenerated quantities
ブロックの中で, softmax
関数を用いて規格化された単体に戻すことができます(42.11節もご参照ください).
共分散行列が単位行列の多変量正規分布は, ユークリッド距離(すなわち\(L_2\)距離)に比例する対数確率密度を生成します. ここで分布を変えると, 関連する幾何的な距離も別なものに変わります. 例えば, 正規分布を二重指数(ラプラス)分布に変えると, \(L_1\)距離(すなわちマンハッタン距離もしくはタクシー距離)に基づくクラスタリングモデルが生成されます.
\(K\)-means法を多変量正規分布の視点でとらえる限り, 共分散行列を単位行列からクラスタに共通する別の行列に変更しても, 結局その共分散行列の逆行列で変換された空間で定義されるユークリッド距離で動作しているのと同じことになります.
なお空間との大域的な類似性はありませんが, 共分散行列がクラスタ毎に異なるソフト\(K\)-means法も一般的です. この様な場合には, 共分散行列に対して階層的な事前分布が用いられる事があります.
クラスタリングモデルのフルベイズ推定はほぼ実行不可能であり, これはパラメータの識別可能性の欠如と事後分布の極度な多峰性の2つの問題によるものです. 24.2節には, ラベルスイッチングに起因する識別不可能性について追加で議論が行われています.
クラスタの割り当ては, そもそも識別することができません. これは, クラスタの平均ベクトルmu
を入れ替えたとしても, 尤度の同じモデルが導れるためです. 例えば, mu
の最初の2つのインデックスと各soft_z[n]
の最初の2つのインデックスを入れ替えても, 尤度は(事後分布も)同じになります. (訳注: かっこの中は原文priorですが, posteriorの誤記と捉えました)
このような識別可能性の欠如は, 複数のマルコフ連鎖の間でクラスタのパラメータが比較できないことを意味しています. ソフト\(K\)-means法におけるパラメータは一つですが, このパラメータが識別されないため, 実際収束をモニタする際に問題が生じます. 単一の連鎖の中であっても, 連鎖が長すぎたりデータがきれいに分離されないような場合にインデックスの入れ替えが伴うと, クラスタの識別に失敗する可能性すらあります.
クラスタリングモデルのもう一つの問題は, 事後分布が非常に多峰的になる点にあります. ある種の多峰性は識別不可能であり, インデックスの入れ替えを招きます. しかしながらたとえインデックスの問題がなかったとしても, 事後分布は非常に多峰的になります.
多峰性が高い場合, ベイズ推定は失敗します. これは, 事後分布のあらゆる峰を適切な比率で訪れる方法がなく, 従って事後予測の推定に含まれる積分を評価する方法もなくなるためです.
これら2つの問題の観点からクラスタリングモデルのあてはめに関してよく聞くアドバイスは, 初期値をたくさん変えて試してみる事であったり, 全体的に最も高い確率をもつサンプルを選択する事であったりします. なお, 期待値最大化法や変分ベイズ法のような最適化に基づく点推定量を用いるのもポピュラーであり, サンプリングに基づくアプローチよりずっと効率的な場合があります.
ナイーブベイズ法は一種の混合モデルであり, 観測される項目のラベルにより, 分類もしくはクラスタリング(または両者のミックス)に使用することができます. 7
混合多項分布モデルは「ナイーブベイズ」ともよばれます. これはこのモデルが, カテゴリを決める多項分布が独立しているという仮定が明らかに成り立たないような分類問題にも適用されてしまうことが多いためです.
ナイーブベイズによる分類とクラスタリングは, 多項分布で表せるような構造(以降ではこれを, 多項的な構造と呼びます)をもつあらゆるデータに適用できます. 典型的な例は自然言語テキストの分類とクラスタリングであり, これはこの後例として使用します.
この場合の観測データは一連の\(M\)本の文書から構成され, その文書は\(V\)種の異なる語彙から抽出される単語の多重集合(bags of words)からなっています. 文書\(m\)の単語数は\(N_m\)であり, 単語には\(w_{m,1}, \ldots, w_{m,N[m]} \in 1:V\)というインデックスが振られています. 文書中の単語のインデックスには順番がありますが, このモデルはその順番を考慮しないため, 人間の自然言語のモデルとしては明らかに欠点があります. なお, トピック(もしくはカテゴリ)の数は\(K\)に固定されています.
混合多項分布モデルでは, 各文書\(m \in 1:M\)に対して, カテゴリカル分布に従い単一のカテゴリ\(z_m \in 1:K\)が生成されます.
\[ z_{m} \sim \mathsf{Categorical}(\theta). \]
\(K\)単体のパラメータ\(\theta\)は, データにおける各カテゴリの出現頻度を表しています.
続いて文書のカテゴリに基づき, 各文書の単語が, その文書中の他の単語や他の文書中の単語と条件付き独立になるように生成されます. つまり, 文書\(m\)の単語\(n\)は次のように生成されます.
\[ w_{m, n} \sim \mathsf{Categorical}(\phi_{z[m]}). \]
ここでパラメータ\(\phi_{z[m]}\)は\(V\)単体であり, これはカテゴリ\(z_m\)の文書に関する語彙における各単語の確率を表しています.
パラメータの\(\theta\)と\(\phi\)の事前分布には, 通常対称ディリクレ分布が用いられます. なお出現頻度\(\theta\)は, 各カテゴリ\(k \in 1:K\)が等確率となるように固定される場合もあります.
前節におけるナイーブベイズモデルの規定では, 単語\(w\)の表記に不ぞろいな配列を使用しました. Stanは不ぞろいな配列をサポートしていないため, このモデルのコード化には別のやり方(全単語のリストから定まる語彙種別のインデックスを, 各単語に付与する方法)が用いられます. このデータは次のように構成されており, 一列目は出現した単語を全て並べて全文書で通して付けたインデックスn
, 二列目は単語n
に対する語彙種別のインデックス, 三列目は単語n
が含まれる文書のインデックス, となります.
n |
w[n] |
doc[n] |
---|---|---|
1 | \(w_{1,1}\) | 1 |
2 | \(w_{1,2}\) | 1 |
\(\vdots\) | \(\vdots\) | \(\vdots\) |
\(N_1\) | \(w_{1,N[1]}\) | 1 |
\(N_1 + 1\) | \(w_{2,1}\) | 2 |
\(N_1 + 2\) | \(w_{2,2}\) | 2 |
\(\vdots\) | \(\vdots\) | \(\vdots\) |
\(N_1 + N_2\) | \(w_{2,N[2]}\) | 2 |
\(N_1 + N_2 + 1\) | \(w_{3,1}\) | 3 |
\(\vdots\) | \(\vdots\) | \(\vdots\) |
N \({}=\sum_{m=1}^{M} N_m\) |
\(w_{M,N[M]}\) | \(M\) |
関連するプログラムの変数は, 全文書における総単語数N
, 単語の配列w
, 文書を特定する配列doc
となります.
カテゴリが既知の文書が訓練データとして与えられた場合, ナイーブベイズモデルで単体上のパラメータを推定するStanのコードは, 次のように書けます. 8
data {
// 訓練データ
int<lower=1> K; // トピック数
int<lower=1> V; // 語彙数
int<lower=0> M; // 文書数
int<lower=0> N; // 総単語数
int<lower=1,upper=K> z[M]; // 文書mにおけるトピック
int<lower=1,upper=V> w[N]; // 単語n
int<lower=1,upper=M> doc[N]; // 単語nに対する文書ID
// 超パラメータ
vector<lower=0>[K] alpha; // トピックの事前分布向け
vector<lower=0>[V] beta; // 単語の事前分布向け
}
parameters {
simplex[K] theta; // トピックの出現頻度
simplex[V] phi[K]; // トピックkにおける単語の分布
}
model {
theta ~ dirichlet(alpha);
for (k in 1:K)
phi[k] ~ dirichlet(beta);
for (m in 1:M)
z[m] ~ categorical(theta);
for (n in 1:N)
w[n] ~ categorical(phi[z[doc[n]]]);
}
トピックの識別子\(z_m\)がデータとして宣言され, カテゴリの割り当てが尤度関数の一部として含まれていることに注意してください.
ナイーブベイズモデルは, 多項的な構造のデータを, 教師なしのやり方で\(K\)個(一定)のカテゴリにクラスタリングするためにも使用できます. データの宣言には前節のモデルと同じ変数が含まれますが, トピックのラベルz
は除かれます. z
は離散的ですので, モデルの計算から積分消去しておく必要があります. この処理は, ナイーブベイズモデルでも他の混合モデルでも同様です. パラメータは事前分布を除いて先ほどと同じですが, 今度の尤度は, 文書あたりで見ると, 次のようにカテゴリ(トピック)について周辺化した確率として計算されます.
\[ \begin{aligned}& \log p \left(w_{m,1}, \ldots, w_{m,N_{m}} \mid \theta, \phi \right) \\& \quad = \log \textstyle \sum_{k=1}^{K} \left(\mathsf{Categorical}(k \mid \theta) \times \prod_{n=1}^{N_{m}} \mathsf{Categorical}(w_{m,n} \mid \phi_{k})\right) \\& \quad= \log \textstyle \sum_{k=1}^{K} \exp \left(\log \mathsf{Categorical}(k \mid \theta) + \sum_{n=1}^{N_{m}} \log \mathsf{Categorical}(w_{m,n} \mid \phi_{k}) \right).\end{aligned} \]
最終行でlog_sum_exp
関数が用いられているのは数値計算を安定させるためであり, 結果は対数スケールで返されることになります.
model {
real gamma[M,K];
theta ~ dirichlet(alpha);
for (k in 1:K)
phi[k] ~ dirichlet(beta);
for (m in 1:M)
for (k in 1:K)
gamma[m,k] <- categorical_log(k,theta);
for (n in 1:N)
for (k in 1:K)
gamma[doc[n],k] <- gamma[doc[n],k]
+ categorical_log(w[n],phi[k]);
for (m in 1:M)
increment_log_prob(log_sum_exp(gamma[m]));
}
ローカル変数のgamma[m,k]
は次の値を表しています.
\[ \gamma_{m,k} = \log \mathsf{Categorical}(k \mid \theta) + \sum_{n=1}^{N_{m}} \log \mathsf{Categorical}(w_{m,n} \mid \phi_{k}). \]
\(\gamma\)が与えられた下で, 文書\(m\)にカテゴリ\(k\)が割り当てられる事後確率は次のようになります.
\[ \text{Pr}\left[z_{m}=k \mid w, \alpha, \beta \right] = \exp \left(\gamma_{m,k} - \log \sum_{k=1}^{K} \exp(\gamma_{m,k}) \right). \]
変数gamma
をtransformed parameters
ブロックで宣言して定義すると, Stanはそのサンプル値を保存します. そうすれば, 規格化された事後確率も生成量として定義できます.
Stanでは, ナイーブベイズモデルの事後予測分布をフルベイズ推定する実装も可能です. この場合, ラベルが付与されたデータと付与されていないデータが組み合わされます. 推定対象には, モデルのパラメータと, ラベルが付与されていないデータのカテゴリに関する事後分布の, 両方が含まれることになります. このモデルは本質的に, 未知のカテゴリラベルがMCAR (missing completely at random; どの値が欠測するかが完全にランダムであるような欠測)だと仮定したモデルになっています. 欠測データの補完に関する更なる情報については, (Gelman et al., 2013; Gelman and Hill, 2007) をご参照ください. またこのモデルは, ラベルの付与されていないデータがパラメータの推定に寄与するため, 半教師あり学習の例にもなっています.
Stanでフルベイズ推定を実行するモデルを規定するためには, ラベルが付与されたデータのモデルとラベルが付与されていないデータのモデルを組み合わせます. 後者の文書の集まりはデータとして宣言されますがカテゴリラベルは付与されず, 関連して新しい変数M2
, N2
, w2
, doc2
が設定されます. 超パラメータのみならずカテゴリ数や単語数は両者で共通となっており, 宣言も一度のみです. 同様にパラメータも1種類しか存在しません. 続くモデル部には, 事前分布に関する共通する記述, ラベルが付与されたデータに関する記述, およびラベルが付与されていないデータに関する記述, が含まれます.
フルベイズ推定の代替手段の一つに, まずラベルの付与されたデータを用いてモデルを推定し, 続いてその結果をラベルの付与されていないデータに適用する(ラベルの付与されていないデータに基いてパラメータの推定値は更新しない), という方法があります. ラベルの付与されていない文書に関するgamma
の定義をgenerated quantities
ブロックに移行すると, この様な振る舞いを行う実装が可能になります. この場合その変数はもはや対数確率には寄与しなくなるので, モデルパラメータの推定においても, もはや同時には影響しないことになります.
潜在ディリクレ配分法(Latent Dirichlet Allocation, 略してLDA)は, ナイーブベイズを一般化した, 混合メンバーシップの多項クラスタリングモデル(Blei et al., 2003)です. LDAの説明で一般的なトピックと文書という用語を用いれば, まず各文書が複数のトピックを持つ様にモデル化され, その混成比に基づいて, あるトピックから各単語が抽出されることになります.
このモデルの基本型では, まず各文書が, 固定の超パラメータに基づいて独立に生成されると仮定されます. そして文書\(m\)に対する最初のステップとして, \(K\)個のトピックに渡るトピック分布である単体\(\theta_m\)が抽出されます.
\[ \theta_{m} \sim \mathsf{Dirichlet}(\alpha). \]
ここで事前分布の超パラメータ\(\alpha\)は固定されており, これは\(K\)個の正の値のベクトルとなります. 文書中の単語は, この分布\(\theta_m\)が与えられた条件の下で, 各々独立に生成されることになります. 具体的にはまず最初に, 文書に固有のトピック分布に基づき, 単語に対するトピック\(z_{m,n} \in 1:K\)が抽出されます.
\[ z_{m,n} \sim \mathsf{Categorical}(\theta_{m}). \]
そして, トピック\(z_{m, n}\)における単語の分布に従って, 最終的に単語\(w_{m,n}\)が抽出されます.
\[ w_{m,n} \sim \mathsf{Categorical}(\phi_{z[m,n]}). \]
なお, トピック\(k\)における単語の分布\(\phi_k\)にも, ディリクレ事前分布が設定されます.
\[ \phi_k \sim \mathsf{Dirichlet}(\beta) \]
ここで\(\beta\)は固定であり, \(V\)個の正の値のベクトルとなります.
Stanでは離散パラメータのサンプリングが(まだ)サポートされていませんが, 他の混合モデルの場合と同じように, 離散パラメータを積分消去して連続パラメータのみの周辺分布を計算できます. トピックと単語の周辺事後分布は, 次のようになります.
\[ \begin{aligned}p(\theta, \phi \mid w, \alpha, \beta) & \propto p(\theta \mid \alpha) \times p(\phi \mid \beta) \times p(w \mid \theta, \phi) \\& = \prod_{m=1}^{M} p(\theta_{m} \mid \alpha) \times \prod_{k=1}^{K} p(\phi_k \mid \beta) \times \prod_{m=1}^{M} \prod_{n=1}^{M[n]} p(w_{m,n} \mid \theta_m, \phi).\end{aligned} \]
上式における最後の積の内側の項は単語の確率であり, トピックの割り当てを積分消去することで次のように定義されます.
\[ \begin{aligned}p(w_{m,n} \mid \theta_{m}, \phi) & = \sum_{z=1}^{K} p(z, w_{m,n} \mid \theta_m,\phi). \\& = \sum_{z=1}^{K} p(z \mid \theta_{m}) \times p(w_{m,n} \mid \phi_z).\end{aligned} \]
この分布を先ほどの式に代入してスケールを対数に変換すると, Stanで直接実装が可能な式が得られます.
\[ \begin{aligned}& \log p(\theta, \phi \mid w, \alpha, \beta) \\& \quad = \textstyle \sum_{m=1}^{M} \log \mathsf{Dirichlet}(\theta_m \mid \alpha) + \textstyle \sum_{k=1}^{K} \log \mathsf{Dirichlet}(\phi_k \mid \beta) \\& \qquad + \textstyle \sum_{m=1}^{M} \textstyle \sum_{n=1}^{N[m]} \log \left(\textstyle \sum_{\mathrm{z}=1}^{K} \mathsf{Categorical}(z \mid \theta_m) \times \mathsf{Categorical}(w_{m,n} \mid \phi_z) \right)\end{aligned} \]
前節で導出した周辺分布に対し, 本節でデータ構造をあてはめて記述すると, StanにおけるLDAのプログラムは次のようになります.
data {
int<lower=2> K; // トピック数
int<lower=2> V; // 語彙数
int<lower=1> M; // 文書数
int<lower=1> N; // 総単語数
int<lower=1,upper=V> w[N]; // 単語n
int<lower=1,upper=M> doc[N]; // 単語nに対する文書ID
vector<lower=0>[K] alpha; // トピックの事前分布向け
vector<lower=0>[V] beta; // 単語の事前分布向け
}
parameters {
simplex[K] theta[M]; // 文書mにおけるトピックの分布
simplex[V] phi[K]; // トピックkにおける単語の分布
}
model {
for (m in 1:M)
theta[m] ~ dirichlet(alpha); // 事前分布
for (k in 1:K)
phi[k] ~ dirichlet(beta); // 事前分布
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)); // 尤度
}
}
他の混合モデルの場合と同じように, log-sum-of-exponents関数は数値演算を安定化させるために使用されています.
文書のトピック分布における相関を考慮するために, (Blei and Lafferty, 2007)ではLDAの変種が提案されており, そこでは文書毎のトピックに関する事前分布が, ディリクレ分布から多変量のロジスティック正規分布に置き換えられています.
この論文の筆者らは, 超パラメータが固定の場合を扱っています. なお論文では共分散の\(L_1\)正則化推定値が用いられていますが, これは事前分布に二重指数分布を設定した場合の最大事後確率推定値と等価です. Stanでは最大事後確率推定は(まだ)サポートされていませんので, 多変量のロジスティック正規分布の平均と共分散は, データとして規定する必要があります.
前節におけるStanのモデルは, 相関を持つトピックモデルを実装するように修正することができます. このためには, トピックの事前分布に関するデータの宣言において, ディリクレ分布におけるalpha
を多変量のロジスティック正規分布における平均と共分散で置き換えます.
data {
... dataブロックの他の記載は, alphaを除いて前と同じ ...
vector[K] mu; // トピック分布の事前分布に関する平均
cov_matrix[K] Sigma; // トピック分布の事前分布に関する共分散
}
そして, ディリクレ分布から単体となるパラメータtheta
を抽出する代わりに, まず多変量正規分布からパラメータ eta
を抽出し, 次いでsoftmax
を用いてその値を単体に変換します.
parameters {
simplex[V] phi[K]; // トピックkにおける単語の分布
vector[K] eta[M]; // 文書mにおけるトピックの分布
}
transformed parameters {
simplex[K] theta[M];
for (m in 1:M)
theta[m] <- softmax(eta[m]);
}
model {
for (m in 1:M)
eta[m] ~ multi_normal(mu,Sigma);
... modelブロックの他の記載は, thetaに関する事前分布を除いて前と同じ ...
}
先ほどの平均と共分散に事前分布を加えれば, 相関を持つトピックモデルに対するフルベイズ推定がStanでもサポートされることになります. このためには, トピック分布に関する平均mu
と共分散Sigma
の宣言をdata
ブロックからparameters
ブロックに移行し, それらの事前分布をモデル部で与える必要があります. 共分散行列Sigma
の事前分布は次のようにコード化すると, 比較的効率が良く解釈も容易でしょう.
... dataブロックは前と同じ(ただしalphaはない) ...
parameters {
vector[K] mu; // トピック分布の事前分布に関する平均
corr_matrix[K] Omega; // 相関行列
vector<lower=0>[K] sigma; // スケール
vector[K] eta[M]; // 文書mにおけるトピック分布のロジット
simplex[V] phi[K]; // トピックkにおける単語の分布
}
transformed parameters {
... etaに関する記載は前と同じ ...
cov_matrix[K] Sigma; // 共分散行列
for (m in 1:K)
Sigma[m,m] <- sigma[m] * sigma[m] * Omega[m,m];
for (m in 1:(K-1)) {
for (n in (m+1):K) {
Sigma[m,n] <- sigma[m] * sigma[n] * Omega[m,n];
Sigma[n,m] <- Sigma[m,n];
}
}
}
model {
mu ~ normal(0,5); // ベクトル化に対応, 散漫な事前分布
Omega ~ lkj_corr(2.0); // 単位相関行列への正則化
sigma ~ cauchy(0,5); // 制約により半コーシー分布
... 単語のサンプリングに関する記載は前と同じ ...
}
形状パラメータが\(\alpha > 0\)のLkjCorr
分布は, 相関行列(すなわち, 対角成分が1の対称な正定値行列)と同じ台を持ちます. この密度は次のように定義されています.
\[ \mathsf{LkjCorr}(\Omega \mid \alpha) \propto \det(\Omega)^{\alpha-1} \]
\(\alpha = 2\)というスケールでは, この分布は単位相関行列寄りの弱情報事前分布となります. したがって, 多変量のロジスティック正規分布の共分散行列\(\Sigma\)にこの事前分布を適用すると, 対角成分への集中度がいくらか増す一方で, そのスケールはsigma
の事前分布から決定されるため, これらの複合効果がもたらされることになります.
方向統計は, 方向という制約のあるデータあるいはパラメータ, もしくはその両方を扱います. 方向を組み合わせると球面となります. 球面のジオメトリは, ユークリッド空間のジオメトリにすんなりとマッピングすることができません. 球面は, 1周まわって元に戻るということも可能だからです. そういうわけで, 平面の紙の上に地図を作るのに, 地球上の互いに近い地点ならどこでも, 地図上でも互いに近くにあるというようにはできません. この根本的な問題は2次元では容易に可視化できます. 円周に沿って移動していると, 初めの場所に戻ってしまいます. 言い換えると, 0度と360度は(0と\(2\pi\)ラジアンでも同じことですが)同じ点を指しますし, 359度と2度との間の距離は137度と140度との距離と同じです.
Stanには単位ベクトルのデータ型があり, 方向統計に対応しています. 単位ベクトルの値は, 超球面(2次元では円周, 3次元では球面)上の点を決定します.
ベクトル\(x \in \mathbb{R}^K\)の長さは次式で与えられます.
\[ \|x\| = \sqrt{x^{\top} x} = \sqrt{x_1^2 + x_2^2 + \cdots + x_K^2} \]
単位ベクトルは, 単位長(すなわち長さ1)を持つベクトルと定義されます.
以下のように変数宣言します.
unit_vector[K] x;
x
の値は, 単位長を持つサイズK
のベクトルに制約されます. 34.7節に, Stanのアルゴリズムで使っている, 単位ベクトルに制約されているパラメータを制約のない空間に変換する方法の詳細があります.
\(n\)次元球面を\(S^n\)と書くことにしますが, これは\((n+1)\)次元の単位ベクトルの組で定義されます.
\[ S^n = \{x \in \mathbb{R}^{n+1} : \|x\| = 1\} \]
\(S^n\)は, \((n+1)\)次元の点で作られるにもかかわらず, \(n\)次元の多様体でしかありません. 例えば, \(S^2\)は\(\mathbb{R}^3\)の点の組で定義されますが, そうした点は緯度・経度により一意に記述することができます. 幾何学的には, \(\mathbb{R}^3\)の\(S^2\)で定義される表面は, 局所的には平面つまり\(\mathbb{R}^2\)に似ています. しかし, \(S^2\)の全体的な形は, コンパクト(すなわち, 点間に最大距離が存在する)というところで平面とは違っています. 地球を「直線」(すなわち測地線)に沿って回り始めると, 最終的には初めの場所に戻ってしまいます. 球面(\(S^2\))の測地線が「大円」と呼ばれるのはこれが理由です. そしてまた, 円周あるいは球面統計には何らかの上手な表現が必要となる理由でもあります.
\(S^{n-1}\)が局所的には\(\mathbb{R}^{n-1}\)に似ているとしても, 両者をすんなりとマッピングする方法はありません. 例えば, 緯度と経度は, (自然単位では\(2\pi\)で一回りする)モジュラ基底に基づいていますので, すんなりマッピングできません.
上下限のある区間\((a,b)\)のように, あらゆる2点間の距離には上限があることから, 幾何学的意味で球面はコンパクトです.
Stanは, Marsaglia (1972)の方法で補助変数を使い, \(\mathbb{R}^{K+1}\)における任意の点を\(S^K\)の点へと(逆)変換します. 点\(y \in \mathbb{R}^K\)は, 点\(x \in S^{K-1}\)に以下のように変換されます.
\[ x = \frac{y}{\sqrt{y^{\top} y}} \]
このマッピングの問題は, 多対1であることです. 原点から出ているベクトル上の点はすべて, 球面上の同じ点に投影されます. Marsaglia (1972)は, このマッピングの補助変数の解釈を行い, \(x\)が超球面上で一様に分布することを示しました. 詳細は34.7節を参照してください.
上の\(\mathbb{R}^n\)から\(S^n\)へのマッピングはゼロでは定義されません. サンプリング中はこの点の結果は測度ゼロであり, そのため無視されるでしょう. 同様に単位ベクトルのパラメータはゼロで初期化できません. 単純な回避法は, ゼロに近い非常に狭い区間で初期化することです. これはStanのインターフェイスすべてに組み込まれているオプションです.
単位ベクトルはそのまま角度に対応していますから, 回転にも対応します. これは2次元で見るとわかりやすいでしょう. 円周上のある点は, コンパス上の方角を決定します(等価ですが, 角度\(\theta\)を決定します). ある角度\(\theta\)が与えられたとすると, 左からの掛け算で角度\(\theta\)だけ回転させるような行列を定義できます. (2次元の)角度\(\theta\)については, \(2 \times 2\)回転行列は以下のように定義されます.
\[ R_{\theta} = \left[\begin{array}{cc} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{array}\right] \]
2次元ベクトル\(x\)が与えられたとすると, \(R_\theta x\)は\(x\)を(原点のまわりで)\(\theta\)度だけ回転させます.
24時間時計は, 深夜0時から正午を経て, 1回転して再び0時に戻るという, 1日の時間の進み方を自然に表しています. したがって, 円周上を24時間に分割した点は, 1日の時刻を自然に表したものです. 同様に, 1年は季節をめぐって, また元の季節に戻ります.
人為的なものには慣習による時間効果がよく見られます. こうしたものも, 休日や週末を示すアドホックな予測変数によって, あるいは, 夏時間を自然のスケールに戻すようなデータの正規化によって, 直接モデリングできます.
Stanでは常微分方程式(ordinary differential equations, 略してODE)の系を解くビルトインの仕組みがあります. Stanでは2つの異なるソルバーが用意されています. 1つはstiffでない系を, もう片方はstiffの系を解くためにチューニングされています.
stiffな常微分方程式の系に関する議論は20.4節を見てください. 短く言えば, stiffな系のソルバーは遅いけれどより頑健です. どれくらいそういう傾向があるかは, 解く系やパラメータ空間の領域に依存します. StanのODEソルバーの引数と返値については45章に記載があります.
ODEの系の具体例として調和振動子を考えましょう. 調和振動子の系は平衡状態の位置とそこからのズレに比例して戻ろうとする力(摩擦の影響を含む)で特徴づけられます. 系の状態は位置と運動量を表すペアである\((y = (y_1, y_2))\)で記述されます(すなわち相空間上の点になります). 時刻による系の変化は次の微分方程式で与えられます. 9
\[ \frac{d}{dt}y_{1} = y_{2} \quad \frac{d}{dt}y_{2} = -y_{1} - \theta y_{2} \]
この状態式はある与えられた時刻における系の状態を, 初期状態・初期状態からの経過時間・系のパラメータの関数として暗黙のうちに定義しています.
系のパラメータ\((\theta)\)の値と時刻\((t_0)\)における初期状態\((y(t_0))\)が与えられると, ある時刻の系列\((t_0 < t_1 < t_2 < \cdots)\)における\((y(t))\)を求めるために, 数値計算で解の時間発展をシミュレーションできます.
Stanでは厳密に引数と返値が決められた関数によって, ODEの系は直接的にコーディングされます. 例えば, (20.1)式で与えられた単純な調和振動子は, Stanでは次のようにコーディングされるでしょう(ユーザー定義関数のコーディングに関するさらなる情報は23章を見てください).
real[] sho(real t, // 時刻
real[] y, // 状態
real[] theta, // パラメータ
real[] x_r, // データ (`real`)
int[] x_i) { // データ (`integer`)
real dydt[2];
dydt[1] = y[2];
dydt[2] = -y[1] - theta[1] * y[2];
return dydt;
}
この系を表す関数は, 時刻t
(real
型の値), 系の状態y
(real
型の配列), 系のパラメータtheta
(real
型の配列), 実数値のデータである変数x_r
(real
型の配列), 整数値のデータである変数x_i
(int
型の配列)を引数にとります. この関数は系の状態y
の時間についての微分の値の配列を返します. 微分の値は時刻t
・状態y
で評価したものです. ここでコーディングした単純な調和振動子は時間に依存する方程式ではありません. すなわち, t
はdtdt
の定義に現れません. 単純な調和振動子はreal
型およびint
型のデータを使いません. しかし, 上のコードで示した関数の引数と返値の型に厳密に一致するように, これらの使わない引数も系を表す関数の引数として宣言に含める必要があります.
系を定義する関数はこれらの引数と返値の方を必ず持っている必要があります. このため, 系がデータやパラメータを含まない場合には, データやパラメータに0の長さの配列を渡すことになるかもしれません. データを表す変数に全く依存しない単純な調和振動子の完全な例は図20.1にあります.
ODEのソルバーは状態y
の関数に不連続点があっても積分できます. ただし, 不連続点の近くの点の精度は問題になるかもしれません(多数の小さな計算ステップを要します). そのような不連続点の例は薬物動態モデルの中のラグです. 体内の薬物濃度は, あるラグ時間を\((t^{\prime})\)とすると, \((0 < t < t^{\prime})\)を満たす時刻t
に対しては濃度がゼロとなる一方で, \((t \ge t^{\prime})\)を満たす時刻t
に対しては非ゼロになります. 例を挙げると, コードは以下のようになるでしょう.
if (t < t_lag)
return 0;
else
... 非ゼロの値を返す ...;
StanのODEソルバーでは引数の初期時刻は定数である必要があります(すなわち, データか変換データ(transformed data)か定数の関数となっている必要があります). これは, 一般に, 初期時刻をパラメータにしてintegrate_ode
関数を使うことはできないことを意味します. したがって, 一般に, 測定値からODEの系の初期時刻を推定することはできないことを意味します.
有限の時点における, ノイズを含む系の状態の測定結果が与えられた場合に, 統計モデルや微分方程式は力学系のパラメータと初期状態の両方あるいはいずれか一方を推定するために使うことができるでしょう.
例えば, 単純な調和振動子において, パラメータの値が\((\theta = 0.15)\)で初期状態が\((y(t=0) = (1,0))\)である場合を考えましょう. 今, 系が10時点\((t = 1,2,\cdots,10)\)において測定されたとしましょう. それぞれの時点における\((y(t))\)の測定には, \((y_1(t))\)と\((y_2(t))\)のどちらの軸にも\((\mathsf{Normal}(0, 0.1))\)に従う独立な誤差が入るとしましょう. そのような測定のプロット例を図20.1に示します.
図20.1: 単純な調和振動子において, パラメータの値が\((\theta = 0.15)\)で初期状態が\((y(t=0) = (1,0))\)である場合の軌跡. 横軸・縦軸の両方に \((\mathsf{Normal}(0, 0.1))\)に従う独立な測定誤差が入っています.
図20.2で与えられたStanのモデルを使ってノイズを含む測定をシミュレートして, このプロットを描くためのデータを生成しました.
functions {
real[] sho(real t,
real[] y,
real[] theta,
real[] x_r,
int[] x_i) {
real dydt[2];
dydt[1] = y[2];
dydt[2] = -y[1] - theta[1] * y[2];
return dydt;
}
}
data {
int<lower=1> T;
real y0[2];
real t0;
real ts[T];
real theta[1];
}
transformed data {
real x_r[0];
int x_i[0];
}
model {
}
generated quantities {
real y_hat[T,2];
y_hat = integrate_ode_rk45(sho, y0, t0, ts, theta, x_r, x_i);
// 測定誤差を加える
for (t in 1:T) {
y_hat[t,1] = y_hat[t,1] + normal_rng(0,0.1);
y_hat[t,2] = y_hat[t,2] + normal_rng(0,0.1);
}
}
図20.2: 単純な調和振動子からノイズを含む測定をシミュレートするStanのプログラム. 微分方程式の系は関数としてコーディングされています. 系のパラメータtheta
と初期状態y0
および初期時刻t0
と観測時刻ts
はデータとして読み込まれます. generated quantities
ブロックはODEを解くのに使われています. 指定した時刻における状態を求め, 測定誤差を加え, 観測y_hat
を生成しています. 系はstiffではないので, rk45
ソルバーが使われています.
このプログラムはStanのプログラムにおいてどのようにODEソルバーが呼ばれるかを示しています.
y_hat = integrate_ode_rk45(sho, y0, t0, ts, theta, x_r, x_i);
このコードは, 初期状態y0
・初期時刻t0
・解の時刻ts
(ts
で指定した時刻における状態が求まる)・パラメータtheta
・実数値のデータx_r
・整数値のデータx_i
が与えられた際に, 関数sho
で定義された系の解を求めています.
ここではODEソルバーはgenerated quantities
ブロックで呼ばれており, \((10 \times 2)\)の配列で解y_hat
を生成しています. 解y_hat
には, 正規分布に従う疑似乱数生成器であるnormal_rng
関数を使って測定誤差が加えられています. 解の配列の行の数は, 指定した解の時刻であるts
の大きさと一致しています.
他の関数とは違い, ODEソルバーは引数の変数に制限があります. 特に時刻t
・実数値のデータx_r
・整数値のデータx_i
はデータまたは変換データ(transformed data)でなくてはなりません. 初期状態y0
もしくはパラメータtheta
だけがパラメータになることが許されています.
Stanは未知の初期状態とパラメータの両方あるいはいずれか一方を推定できます. 一般化線形モデルにおいて線形予測子を生成するのと同じように, 予測値を生成するためにODEソルバーを確定的に使うことでしょう. それから確定的に生成された状態に測定誤差が加わって観測されるでしょう.
図20.3はノイズを含む観測データが与えられた場合に, 単純な調和振動子の初期状態とパラメータの値の両方を推定するStanのプログラムです.
functions {
real[] sho(real t,
real[] y,
real[] theta,
real[] x_r,
int[] x_i) {
real dydt[2];
dydt[1] = y[2];
dydt[2] = -y[1] - theta[1] * y[2];
return dydt;
}
}
data {
int<lower=1> T;
real y[T,2];
real t0;
real ts[T];
}
transformed data {
real x_r[0];
int x_i[0];
}
parameters {
real y0[2];
vector<lower=0>[2] sigma;
real theta[1];
}
model {
real y_hat[T,2];
sigma ~ cauchy(0, 2.5);
theta ~ normal(0, 1);
y0 ~ normal(0, 1);
y_hat = integrate_ode_rk45(sho, y0, t0, ts, theta, x_r, x_i);
for (t in 1:T)
y[t] ~ normal(y_hat[t], sigma);
}
図20.3: 独立した正規分布に従う測定誤差を含む単純な調和振動子において, 未知の初期状態y0
と系のパラメータtheta
を推定するStanのプログラム.
図20.2のシミュレーションのモデルと比べると, パラメータを推定するためにgenerated quantities
ブロックではなくmodel
ブロックでintegrate_ode
関数が使われています. 測定誤差のスケールsigma
の事前分布にコーシー分布を設定し, 系のパラメータの配列theta
と初期状態のパラメータの配列y0
の事前分布に標準正規分布を設定しています. ODEの解は配列y_hat
に代入されます. それから以下のように, y_hat
は観測ノイズを加える際の平均パラメータとなります.
y_hat = integrate_ode_rk45(sho, y0, t0, ts, theta, x_r, x_i);
for (t in 1:T)
y[t] ~ normal(y_hat[t], sigma);
他の回帰のようなモデルと同じように, ノイズの分布を頑健なもの(例:Studentのt分布)に変えたり, 状態を表す変数に相関を入れたり(例:多変量正規分布を使う), もしくはその両方(例:多変量のStudentのt分布)を使うのは簡単です.
スケールが0.10の独立なノイズが加わったこの単純なモデルにおいて, 時刻\((t = 1,\cdots,10)\)の10個の観測データ点は, 初期状態やノイズのスケールといったODEのパラメータを確実に推定するのに十分な数です.
常微分方程式のstiffな系は, 傾きをもとにしたステップを使うソルバーで解こうとすると数値計算上難しいことでおおまかに特徴づけられます. stiffさは典型的には, 状態の座標空間でさまざまな曲率があることが原因です. 例えば, 1つの成分の時間変化が他の成分の時間変化よりもオーダーが異なるほど遅い場合です. 10
StanはstiffなODEに特化したソルバー(Cohen and Hindmarsh, 1996; Serban and Hindmarsh, 2005)を提供しています. ODEの系は前述と全く同じ引数と返値の型をとるように指定します. 唯一の違いは解を求めるためにソルバーを呼ぶところです. 以下のようにrk45
の接尾語はbdf
に置き換わります.
y_hat = integrate_ode_bdf(sho, y0, t0, ts, theta, x_r, x_i);
stiffでない系にstiffなソルバー(bdf
)を使うと, stiffでないソルバー(rk45
)を使う場合よりずっと遅くなるかもしれません. これはstiffなソルバーでは追加のヤコビアンの計算をするからです. 他方, stiffな系にstiffでないソルバーを使おうとすると, 小さなステップ幅と非常に多数のステップを要するために実行は失敗するでしょう.
上で示したソルバーの呼び出しではデフォルトの制御パラメータの設定を使っていました. stiffでないソルバーとstiffなソルバーともに, 3つの引数を追加することができます. 3つのうちどれか1つでも追加する場合には, 残りの2つも追加する必要があります.
y_hat = integrate_ode_bdf(sho, y0, t0, ts, theta, x_r, x_i,
rel_tol, abs_tol, max_steps);
3つの制御のための引数は相対許容値(relative tolerance)・絶対許容値(absolute tolerance)・最大ステップ数(maximum number of steps)です. 相対許容値と絶対許容値のデフォルトの値は両方とも1e-6
(\((10^{-6})\))です. デフォルトの最大ステップ数は1e6
(\((10^6)\))です.
相対許容値と絶対許容値はソルバーによって生成される解の精度を制御します. 相対許容値は解の値と比べた場合の比を制御し, 一方で絶対許容差は解の値そのものの誤差の最大値を制御します. より小さな許容値を設定するとより精度のよい解となります. また, より小さな許容値はより多くの計算時間を要します.
許容値は十分に小さく設定すべきです. それ以上小さくしてもStanのプログラムによって生成される事後サンプルの統計的な特徴はほとんど変わらないことが目安です.
最大ステップ数はシミュレーションが暴走するのを止めるために使われます. MCMCにおいては, 悪いジャンプが採択されたとき, 特にwarmup期間において, 暴走が起こる可能性があります. stiffでないソルバーを使う場合には, 悪いジャンプによって結果的に, パラメータ空間のstiffな領域にサンプラーが跳びこむかもしれません. stiffな領域では適度な許容値を満たすために非常に小さなステップサイズと非常に多数のステップが必要となって, シミュレーションが暴走するかもしれません.
BUGSと同様に, Stanでは直接的な再パラメータ化がサポートされています. また, Stanでは変数変換もサポートされており, 変数変換のヤコビアンの対数を対数確率の合計に直接加えることで実現します.
ベイズの事後分布は専門的には確率「測度」であり, それはパラメータ化によって不変な, 抽象数学のものです. 11他方, Stanのモデリング言語は確率「密度」を定義します. 確率密度は\(\mathcal{R}^N\)から\(\mathcal{R}^+\)に写す関数であり, ユニークではなく, パラメータ化に依存します. 実際には, これは与えられたモデルをStanで表現する方法は複数あり得ることを意味し, 表現が異なれば計算のパフォーマンスも異なることを意味します.
パラメータ化とベイズモデリングの関係を議論したGelman (2004)で指摘されたように, パラメータ化を変えるとどのようにモデルが変わるかに関する示唆が得られる場合がしばしばあります. 私たちは特定の自然なクラスの事前分布を多用する傾向があります. それゆえ, 再パラメータ化の恩恵は, サンプリングをしたいと決めた分布に対する計算の助け「だけ」にはとどまらないのです. さらに, いったん再パラメータ化をして事前情報を加えると, モデル自体も典型的には変わりますし, しばしば有益な方向に変わります. 12
再パラメータ化は素直に実装できます. 例えば, ベータ分布は2つの正のカウントを表すパラメータ\(\alpha, \beta > 0\)によってパラメータ化されています. 次の例は, vector
型のパラメータtheta
を持つ階層的なStanのモデルを表しています. ここで, theta
は独立同一なベータ分布から抽出されており, ベータ分布のパラメータは超事前分布から抽出されます.
parameters {
real<lower = 0> alpha;
real<lower = 0> beta;
...
model {
alpha ~ ...
beta ~ ...
for (n in 1:N)
theta[n] ~ beta(alpha, beta);
...
変換パラメータ(transformed parameter)を使って超事前分布を定めた方がしばしばより自然です. ベータ分布の場合, 明らかな再パラメータ化のやり方は平均パラメータ
\[\phi = \alpha / (\alpha + \beta)\]
とカウントの合計を表すパラメータ
\[\lambda = \alpha + \beta\]
を使う方法です. (Gelman et al., 2013, Chapter 5)に従うと, 平均パラメータの事前分布に一様分布を設定し, カウントの合計を表すパラメータの事前分布に\(p(\lambda) \propto \lambda^{-2.5}\)のパレート分布を設定します.
parameters {
real<lower=0,upper=1> phi;
real<lower=0.1> lambda;
...
transformed parameters {
real<lower=0> alpha;
real<lower=0> beta;
...
alpha = lambda * phi;
beta = lambda * (1 - phi);
...
model {
phi ~ beta(1, 1); // phiは一様分布に従う. この行は省略してもよい.
lambda ~ pareto(0.1, 1.5);
for (n in 1:N)
theta[n] ~ beta(alpha, beta);
...
新しいパラメータphi
とlambda
はparameters
ブロックで宣言され, ベータ分布のパラメータであるalpha
とbeta
はtransformed parameters
ブロックで宣言されて定義されます. もし, alpha
とbeta
の値に興味がなければ, 代わりにmodel
ブロックで局所変数として定義することもできます. 次のようになります.
model {
real alpha;
real beta;
alpha = lambda * phi;
beta = lambda * (1 - phi);
...
for (n in 1:N)
theta[n] ~ beta(alpha, beta);
...
}
ベクトル化すると, 次のようにもっとコンパクトで効率的に表現できます.
model {
theta ~ beta(lambda * phi, lambda * (1 - phi));
...
}
もし, alpha
とbeta
の値に興味があるならば, これらをtransformed parameters
ブロックで定義してからmodel
ブロックで使うとよいでしょう.
分布を与えるのではなく, 変換パラメータ(transformed parameter)を使う場合は, 変換に対するヤコビアンの調整は不要です. 例えば, ベータ分布の例ではalpha
とbeta
は適切な事後分布を持ちます.
パラメータの変換が確率分布によって特徴づけられるときに, 確率の調整が必要な「変数変換」となります. 標準的な教科書の例は対数正規分布です. 確率変数\(y > 0\)の分布が対数正規分布に従うとき, \(y\)の対数である\(\log y\)は正規分布に従います. 分布が\(\log y\)に割り当てられている点に注意です.
変数変換は変換によるゆがみを考慮に入れるため, 確率の調整が必要となります. この調整がうまくいくためには, 一変量の変数変換が台(support)において単調かつ至るところで微分可能でなければなりません.
一変量の変数変換では, 変換の微分の絶対値を使って確率をスケーリングする必要があります(一変量の変数変換のより正確な定義については34.1節を見てください).
対数正規分布の場合, \(y\)の対数が平均\(\mu\)・標準偏差\(\sigma\)の正規分布に従うとすると, \(y\)の分布は以下で与えられます.
\[p(y) = \mathsf{Normal}(\log y \mid \mu , \sigma) \left| \frac{d}{dy}\log y \right| = \mathsf{Normal}(\log y \mid \mu , \sigma) \frac{1}{y}\]
Stanはアンダーフローを防ぐため, 対数スケールで動作します. そのため, 以下の形で扱います.
\[\log p(y) = \log \mathsf{Normal}(\log y \mid \mu , \sigma) - \log y\]
Stanでは変数変換はサンプリング文において適用されます. 曲率を調整するために, 変換の微分の絶対値の対数が対数確率の合計に足しこまれます. Stanでは対数正規分布は次のように直接的に実装できます. 13
parameters {
real<lower=0> y;
...
model {
log(y) ~ normal(mu, sigma);
target += -log(y);
...
パラメータの宣言時に適切な制約を課すことは毎度のことながら重要です. ここでy
は正に制限されています.
次にように, 対数をとったあとの局所変数を定義すると, わずかにより効率的でしょう.
model {
real log_y;
log_y = log(y);
log_y ~ normal(mu, sigma);
target += -log_y;
...
もしy
がパラメータではなくデータとして宣言されているならば, ヤコビアンによる調整は無視されます. なぜなら, データは定数であり, Stanでは定数を除いた対数確率だけが必要となるからです.
この節では変数変換と単純な変換パラメータの違いを説明します. 単純な変換パラメータを使う場合, パラメータをサンプリングし, そのあとでサンプリングした値を変換します. ところが変数変換の場合, パラメータを変換し, そのあとでサンプリングします. 後者だけがヤコビアンの調整を必要とします.
確率関数がサンプリング文で表現されているかどうかは関係ありません. 例えば, 以下のようなサンプリング文で表現しても
log(y) ~ normal(mu, sigma);
以下の対数確率を累積する(インクリメントする)文で表現しても, ヤコビアンの調整が必要です.
target += normal_lpmf(log(y) | mu, sigma);
対数正規分布と同じように, 逆ガンマ分布に従う変数の逆数はガンマ分布に従います. この節では二つのアプローチを比較します. はじめに単純な変換パラメータを使う場合を, 次に変数変換の場合を扱います.
単純な変換パラメータを使うアプローチでy_inv
を逆ガンマ分布からサンプリングするには, 以下のようにコーディングします.
parameters {
real<lower=0> y;
}
transformed parameters {
real<lower=0> y_inv;
y_inv = 1 / y;
}
model {
y ~ gamma(2,4);
}
変数変換のアプローチでy_inv
を逆ガンマ分布からサンプリングするには, 以下のようにコーディングします.
parameters {
real<lower=0> y_inv;
}
transformed parameters {
real<lower=0> y;
y = 1 / y_inv; // 変換
target += -2 * log(y_inv); // 調整
}
model {
y ~ gamma(2,4);
}
ヤコビアンによる調整は変換の微分の絶対値の対数です. ここでは以下のようになります.
\[\log \left| \frac{d}{du} \left( \frac{1}{u} \right) \right| = \log | -u^{-2} | = \log u^{-2} = -2 \log u\]
多変量の変数変換の場合には, 変換のヤコビアンの対数を対数確率の合計に足しこまなければなりません(多変量の変換とヤコビアンのより正確な定義については34.1節を見てください). Stanでは, ヤコビアンが密行列となる一般の場合には以下のようにコーディングできます.
parameters {
vector[K] u; // 多変量のパラメータ
...
transformed parameters {
vector[K] v; // 変換パラメータ
matrix[K, K] J; // 変換のヤコビ行列
... uの関数としてvを計算する ...
... J[m, n] = d.v[m] / d.u[n] を計算する ...
target += log(fabs(determinant(J)));
...
model {
v ~ ...;
...
もちろん, ヤコビアンが解析的に分かっていれば, 行列式を求める関数を呼ぶよりも直接そのヤコビアンを適用した方がより効率的でしょう. 行列式を求める関数は効率的でないし, 数値的に安定でもありません.
多くの場合, ヤコビ行列は三角行列になるでしょう. その場合, 行列式の計算には対角成分だけが必要となります. 変換パラメータのvector
のそれぞれの要素v[k]
が, パラメータのvector
の要素u[1], ..., u[k]
にのみ依存する時, ヤコビ行列は三角行列となります. 三角行列の行列式は対角成分の積となるので, 上記のモデルのtransformed parameters
ブロックはシンプルにでき, 以下のように専用の変数を使うとより効率的にできます.
transformed parameters {
...
vector[K] J_diag; // ヤコビ行列の対角成分
...
... J_diag[k] = d.v[k] / d.u[k] を計算する ...
target += sum(log(J_diag));
...
vector
Stanではコンテナの型に対する制約は, 一つの下限と一つの上限しか宣言できません. しかし, あるvector
型のパラメータの各要素の下限値が, 同じようにvector
型で与えられているとしましょう. すると, 要素ごとの変換とそのヤコビアン(これらは34章に記述があります)をStanで計算する必要があります.
例えば, 下限を表すvector
\(L\)を持つ, パラメータのvector
\(\alpha\)を考えてみましょう. 以下のプログラムでは制約のないraw(生の)パラメータを宣言し, それからヤコビアンを考慮してrawパラメータを明示的に\(\alpha\)に変換します.
data {
int N;
vector[N] L; // 下限値
...
parameters {
vector[N] alpha_raw;
...
transformed parameters {
vector[N] alpha;
alpha = L + exp(alpha_raw);
...
model {
target += sum(alpha_raw); // ヤコビアンの対数
...
調整項は, \(\alpha_\text{raw}\)から\(\alpha = L + \exp(\alpha_\text{raw})\)への変換に関するヤコビ行列の行列式の対数になります. この場合はヤコビ行列が対角行列になるのでシンプルになります(詳しくは34.2節をみてください). ここで, \(L\)は\(\alpha_\text{raw}\)に依存しないパラメータを含むことすらできます. もし境界が\(\alpha_\text{raw}\)に依存するならば, 依存性を考慮に入れてヤコビアンを計算しなおす必要があります.
自作の確率分布もStanの中で直接実装することができます. 必要なことは対数確率の合計を累積する(インクリメントする)ことだけです. 以降では2つの例を扱います.
単純な例は三角分布です. その密度関数は二等辺三角形のような形をしており, 指定された境界において角をもち, 密度を積分すると1になるという制約から高さが決まります. もし, \(\alpha \in \mathbb{R}\)と\(\beta \in \mathbb{R}\)が境界で, \(\alpha < \beta\)とすると, \(y \in (\alpha, \beta)\)は以下で定義される密度を持ちます.
\[\mathsf{Triangle}(y \mid \alpha,\beta) = \frac{2}{\beta - \alpha}\left( 1 - \left| y - \frac{\alpha + \beta}{\beta - \alpha} \right| \right)\]
もし, \(\alpha = -1\), \(\beta = 1\), \(y \in (-1, 1)\)ならば, この式は以下のように簡単になります.
\[\mathsf{Triangle}(y \mid -1,1) = 1 - |y|\]
\(\mathsf{Triangle}(-1,1)\)からサンプリングするため, 以下のStanの実装を考えてみましょう. 14
parameters {
real<lower=-1,upper=1> y;
}
model {
target += log1m(fabs(y));
}
唯一のスカラーのパラメータであるy
が区間(-1,1)
に入るように宣言されています. 対数確率の合計に, すべてのパラメータの同時対数確率(すなわち\(\log \mathsf{Triangle}(-1,1)\))を加えてインクリメントしています. この同時対数確率の値はStanではlog1m(fabs(y))
とコーディングされています. log1m(x)
関数はlog(1.0-x)
と同じ値ですが, 計算がより速く, より正確で, より安定です.
y
の宣言における型の制約real<lower=-1,upper=1>
は, 正しくサンプリングが行われるために必須です. もし, プログラムからy
の制約を取り除いたら, すなわちy
が制約のない実数値をとるように宣言すると, プログラムはコンパイルされるけれども, 実行時においてサンプラーが\((-1,1)\)の外側を探索した時に算術例外を投げることでしょう.
以下のように\((-1,1)\)の外側の値にlog(0.0)
の対数確率(すなわち負の無限大)を定義することで, \(\mathbb{R}\)全体の値に拡張した対数確率関数を考えてみましょう.
target += log(fmax(0.0,1 - fabs(y)));
y
に制約を課した元のプログラムと比べて, これは非効率で遅くて数値計算上不安定です. しかし, y
についての制約を取り除いても, プログラムはコンパイルされ, 算術例外が発生することなしに実行されるでしょう. しかし, 適切にサンプリングはされないでしょう. 15
もし, 仮にStanに指数分布が用意されていないとすると, 指数分布は以下の代入文を用いて直接コーディングできます.
target += log(lambda) - y * lambda;
ここでlambda
はスケールの逆数で, y
はサンプリングされた確率変数を表します. このコーディングは任意のlambda
とy
についてうまくいきます. これらの両方もしくはどちらか一方がパラメータでもデータでもよいですし, ローカル変数でも大丈夫です.
前の段落の代入文は, 以下のサンプリング文によって生成されるC++コードと非常によく似たC++コードを生成します.
y ~ exponential(lambda);
注目すべき違いが二つあります. 一つ目は, サンプリング文はlambda
が正でy
が非負であることを確認するため入力をチェックします(どちらも非数でないかもチェックします).
二つ目の違いは, もしlambda
がパラメータ・変換パラメータ(transformed parameter)・model
ブロックの局所変数のいずれでもなければ, サンプリング文は賢いので定数であるlog(lambda)
の項を落とします. 結果は同じ事後分布になります. なぜなら, Stanは付加定数を除いた対数確率だけを必要とするからです. もし, lambda
とy
の両方とも定数ならば, サンプリング文は両方の項を落とします(しかし, 入力が妥当かどうかのチェックは変わらず行います).
この章ではユーザーの視点から例を交えてユーザー定義関数を説明します. 厳密な仕様は7章を見てください. ユーザー定義関数を使うと, 計算をカプセル化して一つの名前をつけることができ, その名前を使ってどこでも呼び出すことができます. 同様に, 関数を使うと, 複雑な手続きをより理解しやすい構成要素に分解することができます. 適切な名前をつけた関数を使用したモジュール性の高いコードは, 大きな一枚岩のプログラムよりも理解しやすいです. たとえ一枚岩のプログラムにコメントを多くつけたとしてもです. 16
ここではユーザー定義関数のStanプログラムの例を扱います. その関数はgenerated quantities
ブロックで呼び出され, 2つのパラメータの相対差を計算します.
functions {
real relative_diff(real x, real y) {
real abs_diff;
real avg_scale;
abs_diff = fabs(x - y);
avg_scale = (fabs(x) + fabs(y)) / 2;
return abs_diff / avg_scale;
}
}
...
generated quantities {
real rdiff;
rdiff = relative_diff(alpha, beta);
}
関数名はrelative_diff
とし, 2つのreal
型の値を引数にとり, 1つのreal
型の値を結果として返すように宣言しています. この関数はビルトイン関数をgenerated quantities
ブロックで使うのとまったく同じように使われています.
user-defined functions
ブロックすべての関数は固有のブロックで定義されます. そのブロックにはfunctions
というラベルがつけられ, 他のどのブロックよりも前に現れなくてはなりません. ただし, user-defined functions
ブロックはなくても構いません.
本体(波括弧{ }
の間の部分)には, ローカル変数を含む通常のStanコードが入ります.
return
文return
文は関数定義の本体でのみ使うことができます. 上のrelative_diff
の例では関数定義の最後の行にあります. return
文は関数の中のどこに現れても構いません. しかし, void
ではない値を返す関数は, 必ずreturn
文で終わる必要があります. これがどのように実行されるかについての詳細は7.7節を見てください.
reject
文Stanのreject
文は, プログラムの実行中に遭遇したエラーや問題のある値を報告するための仕組みを提供します. reject
文では, 引用符のついた文字列またはStanの式を任意の数だけ引数にとることができます. この文は, 何らかの処理で妥当でない結果が出たことを検出するために, 典型的には条件文の中で使われます.
この文の使い方を説明するために, 23.1節のユーザー定義の相対差関数の例を修正して, 相対差が何らかの閾値よりも小さい場合に棄却するようにします.
functions {
real relative_diff(real x, real y, real min) {
real abs_diff;
real avg_scale;
abs_diff = fabs(x - y);
avg_scale = (fabs(x) + fabs(y)) / 2;
if (abs_diff / avg_scale < min)
reject("relative_diff below ", min);
return abs_diff / avg_scale;
}
}
棄却の効果は関数が呼び出されたブロックに依存します. 詳しくはX.X節を見てください.
関数の引数と返値の型の宣言に, 変数のサイズは書きません. また, 値の制約も含みません. 図23.1に一覧にしましたので見てください.
図23.1: 一番左の列は制約と次元のない基本の型です. これらは関数の返値の型や引数の型に使われます. 中央の列は次元つきの制約のない型です. これらはローカル変数として使われます. 一番右の列は左と中央の列に対応する制約のある型です. 右側のどの型の式でも, それに対応する左側の型に代入できます. 実行時にすべての変数について次元が一貫しているかチェックされます. そして, どんなサイズのコンテナも関数の引数に代入できます. 制約のあるmatrix
型であるcov_matrix[K]
, corr_matrix[K]
, cholesky_factor_cov[K]
, cholesky_factor_corr[K]
はmatrix[K, K]
という次元を持つmatrix
に対してだけ代入できます. Stanではこれらの型の任意の配列も使うことができます. ただし, 関数の引数や返値の型や変数の宣言は少し変わります.
変数の型宣言と異なり, 関数の型宣言においてはmatrix
型とvector
型のサイズは宣言されません. ローカル変数の宣言と同じで, 関数の引数の型は制約をつけて宣言することはできません(下限や上限の制約も, 単体や相関行列のような構造を持つ制約もつけることはできません).
例えば, 単体のパラメータtheta
を使ってカテゴリカル分布のエントロピーを計算する関数は以下のようになります.
real entropy(vector theta) {
return sum(theta .* log(theta));
}
theta
は単体でなければなりませんが, 型には単にvector
が使われます. 17関数の宣言において返値の型や引数の型に上下限や制約のある型を使うことは許されていません.
引数としての配列は独自の構文を持っており, その構文はこのマニュアルにおいても関数を区別するために使われています. 例えば, 2次元配列に作用して1次元配列を生成する関数は以下のように宣言されるでしょう.
real[] baz(real[,] x);
1次元配列(上のコードの返値)には[ ]
という記法が使われており, 2次元配列には[ , ]
という記法が使われています. 3次元配列には[ , , ]
という記法が使われ, 4次元配列以降も同様です.
関数はmatrix
型とvector
型を含む任意の型の配列をサポートしています. 他の型と同様に, 制約をつけることはできません.
場合によっては, 値を返さない関数にするのが理にかなっています. 例えば, 行列の下三角成分を表示する処理は以下のように定義されるでしょう.
functions {
void pretty_print_tri_lower(matrix x) {
if (rows(x) == 0) {
print("empty matrix");
return;
}
print("rows=", rows(x), " cols=", cols(x));
for (m in 1:rows(x))
for (n in 1:m)
print("[", m, ",", n, "]=", x[m, n]);
}
}
特別な語であるvoid
が返値の型として使われています. void
は型そのものではなく空の型で値がないこと, すなわち単に値が欠けていることを表しています. そのため, void
の関数において, return
文は引数をとることはできません. 上のコード例におけるreturn
文のようになります.
適切な型の引数をとったvoid
の関数は, それ自体が文として使われます. 例えば, 上で定義したpretty-print
関数をtransformed parameters
ブロックで定義された分散共分散行列に適用すると以下になります.
transformed parameters {
cov_matrix[K] Sigma;
... Sigmaを設定するコード ...
pretty_print_tri_lower(Sigma);
...
_lp
で名前が終わる関数の中では, サンプリング文とtarget +=
文を使うことができます. 他の関数名では使うことはできません. このアクセスのため, _lp
で名前が終わる関数はtransformed parameters
ブロックとmodel
ブロックの中でしか使うことができません.
以下の関数の例では, 係数のベクトルの事前分布に標準正規分布を設定し, 位置(分布の中心)とスケールの事前分布も設定します. そして, 引数のベクトルbeta_raw
を中心mu
とスケールsigma
に従って平行移動してスケーリングして返します. 中心化に関するさらなる情報は27.6節を見てください.
functions {
vector center_lp(vector beta_raw, real mu, real sigma) {
beta_raw ~ normal(0, 1);
sigma ~ cauchy(0, 5);
mu ~ cauchy(0, 2.5);
return sigma * beta_raw + mu;
}
...
}
parameters {
vector[K] beta_raw;
real mu_beta;
real<lower=0> sigma_beta;
...
transformed parameters {
vector[K] beta;
...
beta = center_lp(beta_raw, mu_beta, sigma_beta);
...
_rng
で終わる名前をつけることで, (疑似)乱数生成器(pseudo random number generator, 略してPRNG)として振る舞うようにユーザー定義関数を宣言することができます. _rng
で終わる名前をつけると, その関数の中ではすべてのPRNG関数を含むビルトイン関数にアクセスでき, _rng
で終わるユーザー定義関数にもアクセスすることができます. _rng
で名前が終わる関数だけが, その中でビルトインのPRNG関数にアクセスすることができます. そのため, _rng
で名前が終わる関数は, 他のPRNG関数と同様にgenerated quantities
ブロックの中でしか使うことができません.
例えば, 以下の関数は\((N \times K)\)のデータの行列を生成します. その行列の1番目の列は切片として1
で埋められ, 残りの要素は標準正規分布に従う疑似乱数発生器(PRNG)から抽出されます.
matrix predictors_rng(int N, int K) {
matrix[N, K] x;
for (n in 1:N) {
x[n,1] = 1.0; // 切片
for (k in 2:K)
x[n, k] = normal_rng(0,1);
}
return x;
}
以下の関数は重回帰のモデルにおいて, データの行列x
, 回帰係数beta
, ノイズのスケールsigma
が与えられた場合の結果をシミュレーションしています.
vector regression_rng(vector beta, matrix x, real sigma) {
vector[rows(x)] y;
vector[rows(x)] mu;
mu = x * beta;
for (n in 1:rows(x))
y[n] = normal_rng(mu[n], sigma);
return y;
}
以下のようにgenerated quantities
ブロックでこれらの関数を使うと, あてはめを行った回帰モデルを使ってシミュレーションデータを生成することができるでしょう.
parameters {
vector[K] beta;
real<lower=0> sigma;
...
generated quantities {
matrix[N_sim, K] x_sim;
vector[N_sim] y_sim;
x_sim = predictors_rng(N_sim, K);
y_sim = regression_rng(beta, x_sim, sigma);
}
より洗練されたシミュレーションでは, 予測変数x
に多変量正規分布をあてはめて, 推定されたパラメータを使ってシミュレーションのための予測変数x_sim
をその多変量正規分布から抽出するかもしれません.
Stanでは確率分布関数を区別するために, 確率密度関数の名前は_lpdf
で終わり, 確率質量関数の名前は_lpmf
で終わります. どちらの関数もreal
型を返します.
標準正規分布を複数使うモデルを考えましょう. Stanでは標準正規分布のために, 特定の多重定義された密度関数もデフォルトの確率分布関数も存在しません. そのため, 標準正規分布の部分をすべて平均0・スケール1の正規分布で書くよりも, 新しい密度関数を定義して再利用した方がいいでしょう.
functions {
real unit_normal_lpdf(real y) {
return normal_lpdf(y | 0, 1);
}
}
...
model {
alpha ~ unit_normal();
beta ~ unit_normal();
...
}
unit_normal
関数を密度関数として使うには, 関数名が_lpdf
で終わる必要があります(同様に質量関数として使うには_lpmf
で終わる必要があります).
一般に, もしfoo_lpdf
が\((N + 1)\)個の引数をとるように定義されているならば, 以下のように使われます.
y ~ foo(theta1, ..., thetaN);
これは以下を略した書き方です.
target += foo_lpdf(y | theta1, ..., thetaN);
ビルトイン関数と同じようにサンプリング文においては, 末尾の_lpdf
は落ちて, 1番目の引数はサンプリングの記号(~
)の左側に移動します.
(確率質量関数を表す)_lpmf
で終わる関数もまったく同じように振る舞います. 違いは, 密度関数(_lpdf
)の1番目の引数は連続値でなければならない(整数値または整数値の配列ではない)のに対し, 質量関数(_lpmf
)の1番目の引数は離散値でなければならない(整数値または整数値の配列である)ことです.
Stanではユーザー定義関数の多重定義は許されていません. 同じ関数名で引数の型が異なる, 2つの別の関数を定義することはできないということです.
理想はインターフェースのレベルで, 関数にドキュメントをつけることでしょう. 関数の説明文のためのStanのスタイルガイドは, Doxygen(C++)とJavadoc(Java)の自動文書化システムで使われているのと同じフォーマットに従います. これらのフォーマットでは, 何らかの説明文からはじまり, それから引数の変数とそれらの型および返値を表示します.
例えば, 23.4節で扱ったデータの行列を生成する関数のドキュメントは以下のようになるでしょう.
/**
* データの行列を返す. 行はアイテムに対応する. また1番目の列は切片を表す1で埋まり,
* 残りの列は標準正規分布から抽出された乱数で埋まっている.
*
* @param N は行数で, データのアイテムに対応する.
* @param K はアイテムあたりの切片項込みの予測変数の数.
* @return シミュレーションで生成された予測変数の行列.
*/
matrix predictors_rng(int N, int K) {
...
コメントは/**
で始まって*/
で終わります. そしてコメントの各行にアスタリスク(*
)があります. @param
のあとに引数の変数名が続き, 関数の引数を説明します. @return
は返値を表します. Stanは(まだ)JavadocやDoxygenのような自動文書生成システムを持っていないので, Stanのパーサにとってはこのコメントは単に/*
から始まって*/
で終わる大きなコメントに見えます.
例外を発生させる関数については, @throws
を使って例外の説明をしましょう. 18例えば,
...
* @param theta
* @throws もしthetaの要素が1つでも負なら例外を発生します.
*/
real entropy(vector theta) {
...
たいてい例外のタイプもドキュメントにしますが, Stanの言語の一部として用意されていないので, 書く必要はありません.
関数はvoid
型またはvoid
ではない返値の型を持ちます. 特別な接尾語である_lpdf
・_lpmf
・_lp
・_rng
のうちの1つが関数名の末尾につくかもしれません.
void
vs. void
ではない返値void
を返すように宣言された関数だけがステートメント(文)として使われます. また, このような関数の内部では引数なしのreturn
文が使われます.
void
でない値を返すように宣言された関数だけが式として使われます. また, このような関数の内部では引数ありのreturn
文が使われ, その引数は宣言した返値の型とマッチする必要があります.
名前が_lpmf
または_lpdf
で終わり, real
型を返す関数だけが, サンプリング文で確率分布関数として使うことができます.
名前が_lp
で終わる関数だけが, サンプリング文やtarget +=
文を通して対数確率を累積する機能にアクセスすることができます. このような関数はtransformed parameters
ブロックまたはmodel
ブロックだけで使うことができます.
名前が_rng
で終わる関数だけがビルトインの疑似乱数発生器にアクセスすることができます. このような関数はgenerated quantities
ブロックだけで使うことができます.
Stanは再帰関数の定義をサポートしています. 再帰関数は場合によっては有用です. 例えば, 行列の累乗\((A^n)\)を考えましょう. \((A^n)\)は正方行列\((A)\)と正の整数値\((n)\)に対して次のように定義されます.
\[ A^n = \left\{\begin{array}{ll} I & \text{$n = 0$のとき} \\ A A^{n-1} & \text{$n > 0$のとき} \end{array}\right.\]
ここで\((I)\)は単位行列です. この定義は直接再帰関数の定義に変換できます.
matrix matrix_pow(matrix a, int n);
matrix matrix_pow(matrix a, int n) {
if (n == 0)
return diag_matrix(rep_vector(1, rows(a)));
else
return a * matrix_pow(a, n - 1);
}
再帰関数の中でmatrix_pow
が矛盾なく使えるように, 関数の定義の前に宣言だけは必要になります. 19次の条件節を加えることで, ベースケースまで再帰が到達しないようになり, より効率的になるでしょう.
else if (n == 1)
return a;
数学的に言うと, 正則な事後分布であればベイズ推定はできていて, 話はそこで終わりです. 有限な分散さえも, あるいは有限な平均さえも必要ではありません. 必要なのは有限な積分だけです. それにもかかわらず, モデリングは厄介な仕事で, 経験を積んだモデル作成者でも非正則な事後分布20ができるようなモデルをコーディングしてしまうことがあります. さらに, 数学的には正しいにも関わらず, 実用的には挙動がおかしい事後分布もあります. この章では, 問題のある事後分布を推定してしまうモデルについて, ベイズ推定一般として, あるいはStanでの実用面から議論します.
この節では, 識別可能性に関する古典的な問題について議論します. この問題は, 事後密度を尾根状にし, サンプリングと推定の両方をめちゃくちゃにしてしまうというものです.
共線性の最初の例は, 余分な切片パラメータを含むという人工的なものです. 21 \(n \in 1:N\)についての観測値\(y_n\), 2つの切片パラメータ\(\lambda_1\)と\(\lambda_2\), スケールパラメータ\(\sigma > 0\)があり, サンプリング分布が以下のようであるとします.
\[ y_n \sim \mathsf{Normal}(\lambda_1 + \lambda_2, \sigma) \]
任意の定数\(q\)について, \(q\)を\(\lambda_1\)に加え, \(\lambda_2\)から引くならば, \(y\)のサンプリング密度は不変です.
\[ p(y \mid \lambda_1, \lambda_2, \sigma) = p(y \mid \lambda_1 + q, \lambda_2 - q, \sigma) \]
その結果, 非正則一様事前分布\(p(\mu, \sigma) \propto 1\)からは非正則事後分布が導かれます. この非正則性が生じるのは, \(\lambda_1+q, \lambda_2-q\) 22の近傍では, どんな\(q\)についても質量が同じになるからです. したがって, \(\lambda_1=1000000000\)かつ\(\lambda_2=-1000000000\)の近傍でも, \(\lambda_1=0\)かつ\(\lambda2=0\)の近傍と同じ時間がサンプラーには必要になりますし, さらにもっと離れた値でも同様です.
このモデルの周辺事後分布\(p(\lambda_1,\lambda_2 \mid y)\)はしたがって非正則です. 23 この非正則性は, 図24.1の左側に図示したように, 視覚的には事後密度の尾根として表されます. このモデルの尾根は, \(c\)をある定数として, \(\lambda_2 = -\lambda_1 + c\)という直線24に沿ってできます.
このモデルを, 単一の切片パラメータ\(\mu\)をもち, 以下のサンプリング分布をもつ単回帰と比較しましょう.
\[ y_n \sim \mathsf{Normal}(\mu, \sigma) \]
この場合には, 非正則事前分布であっても, 異なる値を持つ少なくとも2つのデータ点\(y_n\)がある限り事後分布は正則となります.
項目反応理論(IRT)モデルで, 生徒\(j \in 1:J\)が能力\(\alpha_j\)を持ち, 試験項目\(i \in 1:I\)が難易度\(\beta_i\)を持つとします. 観測データは\(I \times J\)次元配列で, エントリー\(y_{i,j} \in \{0,1\}\)は, \(y_{i,j}=1\)のとき生徒\(j\)が問題\(i\)に正答したことを示すようにコード化します. このデータのサンプリング分布は以下のようになります.
\[ y_{i,j} \sim \mathsf{Bernoulli}(\mathrm{logit}^{-1}(\alpha_j - \beta_i)) \]
任意の定数\(c\)について, 定数\(c\)を能力すべてに加え, かつ難易度すべてにも加えると, \(y\)の確率は変わりません. 25
\[ p(y \mid \alpha, \beta) = p(y \mid \alpha + c, \beta + c) \]
このため, 上で議論した2つの切片を持つ回帰の多変量版が出現することになります.
共線性の問題の一般型は, 回帰の予測変数が共線であるときに発生します. 例えば, 直線回帰のサンプリング分布を考えます.
\[ y_n \sim \mathsf{Normal}(x_n \beta, \sigma) \]
\(y\)は\(N\)次元の観測値ベクトル, \(x\)は\(N \times K\)の予測変数行列, \(\beta\)は\(K\)次元の係数ベクトルです.
ここで, 予測変数行列の列\(k\)が列\(k'\)の定数倍になっているとします. すなわち, すべての\(n\)について\(x_{n,k}=cx_{n,k'}\)となるような定数\(c\)が存在するとします. この場合, 係数\(\beta_k\)と\(\beta_{k'}\)は予測値を変えずに共変動できます. そのため, 任意の\(d \neq 0\)について次式が成り立ちます.
\[ p(y \mid \dots,\beta_k,\dots,\beta_{k'},\dots,\sigma) = p(y \mid \dots,d\beta_k,\dots,\frac{d}{c}\beta_{k'},\dots,\sigma) \] 26
予測変数行列の列が, 上の議論のような完全な共線ではなくとも, 共線に近ければ推定に同じような問題が発生します.
IRTモデルで, 各々の質問に識別力パラメータ\(\delta_i\)を加えるとします. データをサンプリングするモデルは次式です.
\[ y_{i,j} \sim \mathsf{Bernoulli}(\mathrm{logit}^{-1}(\delta_i (\alpha_j - \beta_i))) \]
任意の定数\(c \neq 0\)について, \(\delta\)を\(c\)倍し, \(\alpha\)と\(\beta\)を\(c\)で割っても, 尤度は同じです.
\[ p(y \mid \delta, \alpha, \beta) = p(y \mid c\delta, \frac{1}{c}\alpha, \frac{1}{c}\beta) \]
もし\(c < 0\)ならば, 密度を変えずに, \(\alpha\)および\(\beta\), \(\delta\)のすべての成分の符号が反転します.
\(K\)単体(すなわち, 合計すると1になる, 非負の値からなる\(K\)次元ベクトル)をパラメータ化するためには, \(K-1\)個のパラメータだけが必要です. なぜなら, \(K\)番目のパラメータは, 最初から\(K-1\)番目までの合計を1から引いた値になるからです. したがって, \(\theta\)を\(K\)単体とすると次式が成り立ちます.
\[ \theta_K = 1 - \sum_{k=1}^{K-1}\theta_k \]
softmax関数(42.11節参照)は, 線形予測子の\(K\)次元ベクトル\(\alpha\)を\(K\)単体\(\theta\)にマッピングします. つまり\(\theta = \mathrm{softmax}(\alpha)\)で, 定義は次式です.
\[ \theta_k = \frac{\exp(\alpha_k)}{\sum_{k'=1}^{K}\exp(\alpha'_k)} \]
softmax関数は多対1関数です. パラメータ\(\alpha\)に制約がないと, 識別可能性が失われます. とくに, すべての\(\alpha_k\)に定数を足したり引いたりしても, おなじ単体\(\theta\)となります.
前の節で議論した例はすべて, データの確率密度を変えないままパラメータの平行移動や拡大縮小ができるというものです. この問題を軽減する方法はいくつかあります.
複数の切片, \(\lambda_1\)と\(\lambda_2\)がある場合には, 余分な切片を取り除くのが最も単純な解決法です. これによりモデルは, 単一の切片パラメータ\(\mu\)を持ち, サンプリング分布は\(y_n \sim \mathsf{Normal}(\mu, \sigma)\)となります. 同じ解決法を, 共線性の問題にも使えます. 予測変数行列\(x\)から列を1つ取り除くだけです.
識別力パラメータのないIRTモデルは, パラメータの1つを固定した値(普通は0)にピン留めすることで固定することができます. 例えば, 最初の生徒の能力\(\alpha_1\)を0に固定することができます. このとき, その他の生徒すべての能力パラメータは生徒1に対する相対値と解釈できます. 同様に難易度パラメータは, 生徒1の解答能力に対する相対値と解釈されます.
この解決法は, 質問の識別力パラメータ\(\delta_i\)を導入すると発生する, 乗数による不変性を扱うには十分ではありません. この問題を解決するには, 識別力27パラメータの1つ, たとえば\(\delta_1\)にも制約をつけなくてはなりません. 乗数による不変性は, 加算とは異なり, 非零の値に制約しなければなりません. 都合が良いのは1とすることです. このとき, 識別力パラメータはすべて, 項目1の識別力に対する相対値として解釈されるでしょう.
softmax(\(\alpha\))の多対1の性質は, \(\alpha\)の成分の1つをピン留めすることで軽減するのは普通です. 例えば, \(\alpha_K = 0\)と固定します. そうすると, \(K-1\)次元の制約のないパラメータから, \(K\)単体へ, 1対1対応となります. これが, 単体として制約されるパラメータをStanで定義するおおまかな方法です. 正確な定義は34.6節を参照してください. \(K-1\)次元ベクトルから単体を生成するStanコードは以下のとおりです.
vector softmax_id(vector alpha) {
vector[num_elements(alpha) + 1] alphac;
for (k in 1:num_elements(alpha))
alphac[k] = alpha[k];
alphac[num_elements(alphac)] = 0;
return softmax(alphac);
}
ここまで, パラメータの事前分布が非正則一様事前分布であるとしてモデルを議論してきました.
こうした不変性の問題に対する, より一般的なベイジアンの解決法は, パラメータに正則な事前分布を与えることです. 加法的でも, 乗法的でも, どちらの不変性に由来する問題でも, この方法を使って解決できます.
例として, 複数の切片に正規分布を事前分布として与えます.
\[ \lambda_1,\lambda_2 \sim \mathsf{Normal}(0, \tau) \]
\(\tau\)を定数値のスケールとすると, 事後最頻値が\(\lambda_1 = \lambda_2\)となる点に位置することが保証されます. なぜならこれにより, \(\log\mathsf{Normal}(\lambda_1 \mid 0, \tau) + \log\mathsf{Normal}(\lambda_2 \mid 0, \tau)\)が最小化されるからです. 28 2つの切片を持つモデルに事前分布を加えたものを図24.1の中央に示します. 図24.1の右側は, 単一の切片を持つように再パラメータ化した結果です.
図24.1: 事前分布なしの2切片パラメータ化, 平均0標準偏差1の正規事前分布の2切片パラメータ化, 事前分布なしの1切片再パラメータ化のそれぞれの事後分布. 3つの場合とも, 平均0標準偏差1の正規分布から抽出された100データ点について事後分布をプロットしています. 左)2切片パラメータ化は, 北西方向と南東方向とに無限に伸びる尾根状の非正則事後分布29となります. 中)平均0標準偏差1の正規事前分布を切片に対して加えると正則事後分布になります. 右)事前分布なしの単一切片パラメータ化も正則事後分布が得られます.
\(K\)単体パラメータ化\(\theta = \mathrm{softmax}(\alpha)\)を, 制約なしの\(K\)次元ベクトル\(\alpha\)について識別するための別の方法は, \(\alpha\)の各成分が, ある固定した位置パラメータをもつ事前分布に従うとすることです(すなわち, 位置が変わるような階層事前分はとくに避けるようにします). \(\alpha_K = 0\)とピン留めする方法では, \(K\)番目の値への相対値として\(K-1\)個の値をモデリングしていました. 事前分布に基づく方法では, そうではなく, \(K\)個の値を等しく対称的に扱ってモデリングします. 一方で, ピン留めのパラメータ化の方が通常は, (事前分布で制約された)空間内の動きに余分な自由度がないので, 統計学的にはより効率的です.
不変性を解決するために事前分布を加える際には注意が必要です. 事前分布の幅が広すぎると(すなわち漠然すぎると), 理論的には解決するとしても, 実際にはサンプラーがやはり動くのに苦労するということになるでしょう.
理想的には, モデリングする問題についての本質的な知識に基づいて, 現実的な事前分布を定式化するのがよいでしょう. そのような事前分布なら, 事前の知識に基づいた適切な強さを選択できます. 強い事前の情報があるときには, 強情報事前分布を使うとよいでしょう.
強い事前の情報がないときには弱情報事前分布を使って, 事後分布中でデータを圧倒しないことと, 推定の計算を制御することとの適切なバランスをとります. たいていの問題では, 推定値がどのくらいのスケールになるか, 少なくとも何らかの見解をモデル作成者は持っているでしょう. そして, データを圧倒しないような, しかし事後分布を十分に制御できるような, 識別可能性の目的にあう事前分布を選ぶことができます.
事前分布は, IRTモデルにおける加法的な不変性を制御するためにも同じように使えます. 典型的には単純に, スケールを制御する強い事前分布を生徒の能力パラメーター\(\alpha\)に設定することで, 基本的なIRTモデルにおける加法的な不変性と, 質問項目の識別力パラメータを含む拡張モデルにおける乗法的な不変性を制御します. そのような事前分布は, 解析対象についての事前知識を増やすものではありません. そして, 質問項目の難易度についての事前分布は, 解析対象の事前知識に基づいて, 情報のある事前分布もしくは弱情報事前分布を選ぶとよいでしょう.
回帰モデルにおいて共線性がある場合, 事後分布を最大化するパラメータの値は無限個あります. その一方で, 混合分布モデルの成分を入れ替えることができる場合, 事後分布を最大化するパラメータの値は有限個ですが, 複数あるために問題のある事後分布となります.
2つの位置パラメータ\(\mu_1\)と\(\mu_2\)をもつ正規混合分布モデルを考えます. スケールはともに\(\sigma > 0\)で, 混合率を\(\theta \in [0, 1]\)とすると尤度は次式になります.
\[ p(y \mid \theta,\mu_1,\mu_2,\sigma) = \prod_{n=1}^{N}(\theta\mathsf{Normal}(y_n \mid \mu_1,\sigma) + (1 - \theta)\mathsf{Normal}(y_n \mid \mu_2,\sigma)) \]
ここでの問題は, 次式のように混合成分が入れ替わりうることです.
\[ p(\theta,\mu_1,\mu_2,\sigma \mid y) = p((1 - \theta), \mu_2, \mu_1, \sigma \mid y) \]
この問題は, クラスタリングモデルのように, 混合成分\(K\)の数が大きくなるにつれて悪化します. \(K!\)個の同一の事後最大値ができます.
事後分布の収束と有効サンプルサイズの分析も混合分布モデルでは難しい問題です. 例えば, Stanが報告する\(\hat{R}\)収束統計量も, 有効サンプルサイズの計算も, ラベルスイッチングで不正確になります. 問題は, これらの計算の鍵となる要素である事後平均がラベルスイッチングの影響を受けることにあります. \(\mu_1\)の事後平均が\(\mu_2\)の事後平均に等しくなり, \(\theta\)の事後平均が, データによらず常に1/2になってしまいます.
ある意味, 混合成分のインデックス(あるいはラベル)は重要ではありません. 事後予測推測は, 混合成分が識別できなくとも可能です. 例えば, 新しい観測値の対数確率は, 混合成分が識別されるかに依存しません. ラベルスイッチングが起きるモデルにおいて正しいベイズ推定値とは, ラベルスイッチングが起きても不変なものだけです. パラメータの事後平均は, ラベルスイッチングに対して不変ではないので意味がありません. 例えば, 2成分の混合分布モデルにおける\(\theta\)の事後平均は常に1/2となるでしょう.
この場合には, 理論的には, 事後予測推測に含まれる積分はすべてうまくいくでしょうから, 推定の問題はないはずです. 実用上の問題は計算にあります.
そのような不変の推定が実用的にも実行可能であるかどうかは, まったく別の問題です. たった1つの事後最頻値を見つけるのにも難儀するのがほぼ毎度のことです. ましてや, 確率質量に従って, 複数ある局所最大値のうちの隣のものと探索のバランスをとるなど, さらに難儀なことです. ギブズサンプリングでは, \(\mu_1\)が, 現在の\(\mu_2\)と\(\theta\)の値に条件づけられてサンプリングされているときに, 今まで到達したことがない新たな局所最頻値へと移動することはなさそうです. HMCとNUTSではその場合, 2つの局所最頻値のまわりの2つの「くぼみ」のうちの1つにサンプラーがはまってしまうことにより, ランダムな運動量の割り当てからは, 一方の局所最頻値から他方の局所最頻値へと動くための十分なエネルギーを集められないということになります.
正則な事後分布であっても, 混合分布モデルで成分の数が増える場合のように, 指数関数よりも速く局所最頻値の数が増える場合には, 既知のサンプリングと推定の技法のすべてが非効率的となることが知られています.
いくつかのハック(つまり「トリック」)が, ラベルスイッチングにより起こる問題への実用的な対処のために提案され, 採用されています.
よく使われる戦略の1つは, 成分を識別するようにパラメータに制約を課すことです. 例えば, 上で議論した2成分正規混合分布モデルに\(\mu_1 < \mu_2\)という制約を課すことを考えましょう. この方法では, 反対の順序\(\mu_1 > \mu_2\)に実質的な確率質量が存在する場合に問題が起こりえます. この場合, その制約によって事後分布が影響を受け, \(\mu_1\)と\(\mu_2\)における真の事後分布の不確実性は, 制約のあるモデルではとらえることができません. さらに, ある事象の生起確率を事後に推定する標準的な方法においても問題が発生します. 例えば, \(\Pr[\mu_1 > \mu_2]\)を推定するため\(M\)個の事後サンプルを使おうとすると失敗します. これは, 事後分布がモデルの制約を守るため, 次式の推定量による推定値が0になるからです.
\[ \Pr[\mu_1 > \mu_2] \approx \sum_{m=1}^{M}\mathrm{I}(\mu_1^{(m)} > \mu_2^{(m)}) \]
よく使われる別の方法は, 1個の連鎖で動かすか, 現実的な値の近くでパラメータを初期化することです. 30 もっともな初期値を見つけて, マルコフ連鎖内でスイッチが起きなければ, これは, 強い制約を課す方法よりもうまくいくことがあります. 結果は, すべての連鎖が, 事後分布における特定の局所最頻値の近くに張り付きます.
サンプリングまたは最適化の間に, 混合分布モデルの2つの混合成分がつぶれて同じ値になることがあります. 例えば, \(K\)個の正規分布からなる混合分布で, \(i \neq j\)について, \(\mu_i = \mu_j\)かつ\(\sigma_i = \sigma_j\)となることがあります.
これは典型的には, MCMCの初期化または最適化によりサンプリングの初期に発生するか, あるいはMCMC中のランダムな移動により発生するでしょう. 与えられた抽出(\(m\))において, いったん複数のパラメータが同じ値になってしまうと, 現在のパラメータの値と, 成分がつぶれていないときの値との間に質量の谷があるために, そこから出られなくなることがあります.
ウォームアップ中のステップサイズを小さくすることや, 各混合成分がどのインデックスになりやすいかを定める強い事前分布が役に立つかもしれません. もっと極端な手段は, いくつかのパラメータがつぶれる可能性を考えて, 混合成分を追加しておくことです.
一般に\(K\)が1よりも大きくなると, 混合分布モデルで, \(K\)個の正しい混合成分を正確に復元することはきわめて困難です. (そうです. 2成分の混合分布でさえ, この問題は起こりえます. )
場合によっては, パラメータがある極や境界に近づいて, 際限なく事後密度が大きくなるということがあります. そのようなときは, 事後最頻値はありませんし, サンプリングされたパラメータが制約の境界に近づくので, 数値的安定性の問題が起こることがあります.
そのような例の1つは, スケール\(\sigma_1\)と\(\sigma_2\)が成分によって変わる2項混合分布モデルです. 位置は\(\mu_1\)と\(\mu_2\)とします. この状況で, ある\(n\)について, \(\sigma_1 \rightarrow 0\)かつ\(\mu_1 \rightarrow y_n\)で, 密度が際限なく大きくなります. すなわち, 混合成分の1つの質量のすべてが, 単一のデータ項目\(y_n\)の周囲に集中するのです.
際限のなく大きくなる密度のもうひとつの例は, \(\mathsf{Beta}(\phi \mid 0.5, 0.5)\)のような事後分布で発生するものです. これは非常に「弱い」ベータ事前分布がデータのない群に使われているときに発生することがあります. この密度は\(\phi \rightarrow 0\)かつ\(\phi \rightarrow 1\)で際限なく大きくなります. 同様に, 「弱い」ベータ事前分布のベルヌーイ尤度モデルも次式のような事後分布になります.
\[\begin{array}{ll} p(\phi \mid y) &\propto \mathsf{Beta}(\phi \mid 0.5, 0.5) \times \prod_{n=1}^{N}\mathsf{Bernoulli}(y_n \mid \phi)\\ &= \mathsf{Beta}(\phi \mid 0.5 + \sum_{n=1}^{N}y_n, 0.5 + N - \sum_{n=1}^{N}y_n) \end{array}\]
もし\(N=9\)で, 各々の\(y_n=1\)であれば, 事後分布は\(\mathsf{Beta}(\phi \mid 9.5, 0.5)\)になります. この事後分布は\(\phi \rightarrow 1\)で際限なく大きくなります. にもかかわらず, 事後分布は正則で, 事後最頻値がないにも関わらず, 事後平均は正確に0.95と明確に定義されます.
この問題ではStanは, 制約のある(0, 1)空間を直接サンプルすることはしませんし, 制約のない密度の値を直接あつかうこともしません. そうではなく, 確率の値\(\phi\)が(\(-\infty\), \(\infty\))にロジット変換されます. 境界の0と1はそれぞれ\(-\infty\)と\(\infty\)になります. ヤコビアンの調整はStanが自動的に行なうので, 制約なしの密度は正則であることが保証されます. (0, 1)の特定の場合の調整は\(\log\mathrm{logit}^{-1}(\phi)+\log (1 - \mathrm{logit}^{-1}(\phi))\)です. 31導出は34.4節を参照してください.
しかし, 2つの問題がまだ出てきます. 1つ目として, \(\phi\)の事後質量が境界の1近くにある場合には, ロジット変換されたパラメータは非常に長いパスを掃き出すしかなくなり, そのためno-U-turnサンプラーが課すU-turn条件ばかりが満たされ, うまくサンプリングできなくなることがあります. 2つ目の問題は, 制約のない空間から制約のある空間への逆変換のときに, 0にアンダーフローしたり, 1にオーバーフローしたりするおそれがあることです. これは, 制約のないパラメーターが無限でなくても発生します. 同様の問題は, ロジスティック回帰の期待値の項でも発生します. ベルヌーイ分布と2項分布ではロジットスケールのパラメータ化がより安定なのはこの理由によります.
場合によっては, 事後密度は際限なく大きくはならないのに, 密度のとる値を次第に増加させながらパラメータが際限なく大きくなることがあります. 前の節で議論した, 密度が際限なく大きくなるモデルと同様に, そのようなモデルにも事後最頻値はありません.
\(N\)個の結果変数\(y_n \in \{0, 1\}\)と, \(N \times K\)行列の予測変数\(x\), \(K\)次元の係数ベクトル\(\beta\)からなるロジスティック回帰モデルを考えます. サンプリング分布は次式です.
\[ y_n \sim \mathsf{Benoulli}(\mathrm{logit}^{-1}(x_n \beta)) \]
ここで, 予測変数行列の\(k\)列が, \(y_n = 1\)のときだけに\(x_{n,k} > 0\)となるとします. この条件は「分離可能性」として知られます. この場合, 観測データについての予測の正確さは, \(\beta_k \rightarrow \infty\)となるにつれ, 改善され続けます. \(y_n = 1\)のとき, \(x_n \beta \rightarrow \infty\)で, \(\mathrm{logit}^{-1}(x_n \beta) \rightarrow 1\)となるからです.
分離可能性の問題のあるときは, 尤度に最大値はありませんから, 最尤推定値もありません. ベイズの観点からいうと, 事後分布は非正則ですから, したがって\(\beta_k\)の周辺事後平均も定義されません. ベイズモデルではこの問題は通常, \(\beta\)に正則事前分布を与えて, 事後分布が正則であることを保証することにより解決します.
区間[0, 1]で定義され, 一様事前分布\(\mathsf{Uniform}(\psi \mid 0, 1)\)が与えられたパラメータ\(\psi\)を含むモデルがあるとします. ここで, データには\(\psi\)についての情報が何もないとすれば, 事後分布も\(\mathsf{Uniform}(\psi \mid 0, 1)\)となります.
\(\psi\)には最尤推定値がありませんが, 事後分布は閉じた区間で一様ですから, 正則です. 一様な事後分布[0, 1]の場合, \(\psi\)の事後平均は1/2と明確に決まります. 事後最頻値はありませんが, それにもかかわらず事後予測の推定は問題なくできるでしょう. \(\psi\)の予測値を[0, 1]のすべての点で単純に積分(すなわち平均)すればよいのです.
非正則な事後分布では, 事後分布を適切に探索することは理論的には不可能です. BUGSやJAGSで実行されるギブズサンプリングでも, そのような非正則な事後分布から適切にサンプリングすることは不可能です. しかし, それにも関わらず, 24.1節で議論し, 図24.1で図示した2切片モデルのような例に直面したとき, ギブズサンプリングは, Stanで実行されるハミルトニアンモンテカルロのサンプリングとは実際はまったく異なる挙動を示します.
BUGSやJAGSで実行されるギブズサンプリングは, この識別されないモデルについて効率的で, うまく動作するように見えるかもしれませんが, 前の小節で議論したように, 実際には事後分布を適切には探索しないでしょう.
初期値\(\lambda_{1}^{(0)}\), \(\lambda_{2}^{(0)}\)で起こることを考えましょう. ギブズサンプリングはiteration \(m\)で以下の抽出を行なって進行します.
\[\begin{array}{ll} \lambda_1^{(m)} &\sim p(\lambda_1 \mid \lambda_2^{(m-1)}, \sigma^{(m-1)},y)\\ \lambda_2^{(m)} &\sim p(\lambda_2 \mid \lambda_1^{(m)}, \sigma^{(m-1)},y)\\ \sigma^{(m)} &\sim p(\sigma \mid \lambda_1^{(m-1)}, \lambda_2^{(m)}, y) \end{array}\]
ここで, \(\lambda_1\)(\(\lambda_2\)の抽出も対称です)の抽出を考えます. このモデルでは共役であり, したがって非常に効率的に抽出ができます. このモデルでは, 次の\(\lambda_1\)が抽出可能な範囲は, 現在の\(\lambda_2\)と\(\sigma\)の値により非常に制限されています. ギブズサンプラーは非常に速く動き, 外見上はもっともらしい\(\lambda_1 + \lambda_2\)の推定値を与えるでしょう. しかし, 事後分布の範囲全体を探索はしません. 初期値付近をゆっくりランダムウォークしているに過ぎないのです. このランダムウォークの挙動は, 事後分布の相関が非常に大きいときのギブズサンプリングに典型的です. これが, 事後分布でパラメータが相関を持つモデルでは, ギブズサンプリングよりもハミルトニアンモンテカルロが好まれる主要な理由です.
Stanが実行するハミルトニアンモンテカルロ(HMC)は, 事後分布でパラメータに相関があるモデルでの事後分布の探索をはるかに効率的に行ないます. とくにこの例では, ハミルトニアンダイナミクス(すなわち, 負の対数事後分布により定義される場において, ランダムな運動量で与えられる, 架空粒子の運動)はポテンシャルエネルギーにより定義される谷(対数事後確率における尾根が, ポテンシャルエネルギーにおける谷に相当します)に沿って登ったり降りたりします. 実際には, \(\lambda_1\)と\(\lambda_2\)のランダムな運動量についてさえも, 対数事後確率の勾配により, 相関について調整が行なわれ, シミュレーションでは\(\lambda_1\)と\(\lambda_2\)が, 事後対数密度の尾根に相当する谷に沿って反対方向に動かされるでしょう(図24.1参照).
Stanのデフォルトのno-U-turnサンプラー(NUTS)はさらにもっと効率的に事後分布を探索します(Hoffman and Gelman (2011, 2014)を参照). NUTSは, パラメータの値を表す架空粒子の運動を, それがUターンするまでシミュレートします. 問題のある事後分布からサンプリングする場合, Uターンしないでポテンシャルエネルギーの谷をいつまでも降下することになり, ほとんどの場合うまくいきません. 実際に起きることは, シミュレーションの多くのiterationでリープフロッグの最大ステップ数に到達し, 対数確率と勾配の評価回数(デフォルトのように最大tree depthが10ならば, 1000となります)が非常に大きな数となることです. したがって, サンプリングは非常に遅いように見えるでしょう. これは非正則事後分布を示すもので, NUTSアルゴリズムや実装のバグではありません. 単純に, 非正則な事後分布からサンプリングすることは不可能なのです. したがって, 非正則な事後分布の場合に明確に失敗し, 事後分布の非常に長いパスを掃き出すという分かりやすい結果を得ることは, 一般的なHMCと特定のNUTSの挙動としては当然で安心できるものです.
識別不可能なモデルや弱くしか識別されないモデルからのサンプリングの問題を描写するため, パラメータの識別可能性の程度を増やしつつ, 3つのモデルに当てはめを行ないます. これらのモデルの事後分布は図24.1に図示したものです. 最初のモデルは, 24.1節で議論した, 2つの位置パラメータがあり, 事前分布はない識別されないモデルです.
data {
int N;
real y[N];
}
parameters {
real lambda1;
real lambda2;
real<lower=0> sigma;
}
transformed parameters {
real mu;
mu = lambda1 + lambda2;
}
model {
y ~ normal(mu, sigma);
}
2番目のモデルは, 前のモデルのmodel
ブロックに, lambda1
とlambda2
の事前分布を加えたものです.
lambda1 ~ normal(0, 10);
lambda2 ~ normal(0, 10);
3番目は, 単一の位置パラメータを持ちますが, 事前分布は指定しません.
data {
int N;
real y[N];
}
parameters {
real mu;
real<lower=0> sigma;
}
model {
y ~ normal(mu, sigma);
}
Inference for Stan model: improper_stan
Warmup took (2.7, 2.6, 2.9, 2.9) seconds, 11 seconds total
Sampling took (3.4, 3.7, 3.6, 3.4) seconds, 14 seconds total
Mean MCSE StdDev 5% 95% N_Eff N_Eff/s R_hat
lp__ -5.3e+01 7.0e-02 8.5e-01 -5.5e+01 -5.3e+01 150 11 1.0
n_leapfrog__ 1.4e+03 1.7e+01 9.2e+02 3.0e+00 2.0e+03 2987 212 1.0
lambda1 1.3e+03 1.9e+03 2.7e+03 -2.3e+03 6.0e+03 2.1 0.15 5.2
lambda2 -1.3e+03 1.9e+03 2.7e+03 -6.0e+03 2.3e+03 2.1 0.15 5.2
sigma 1.0e+00 8.5e-03 6.2e-02 9.5e-01 1.2e+00 54 3.9 1.1
mu 1.6e-01 1.9e-03 1.0e-01 -8.3e-03 3.3e-01 2966 211 1.0
Warmup took (0.40, 0.44, 0.40, 0.36) seconds, 1.6 seconds total
Sampling took (0.47, 0.40, 0.47, 0.39) seconds, 1.7 seconds total
Mean MCSE StdDev 5% 95% N_Eff N_Eff/s R_hat
lp__ -54 4.9e-02 1.3e+00 -5.7e+01 -53 728 421 1.0
n_leapfrog__ 157 2.8e+00 1.5e+02 3.0e+00 511 3085 1784 1.0
lambda1 0.31 2.8e-01 7.1e+00 -1.2e+01 12 638 369 1.0
lambda2 -0.14 2.8e-01 7.1e+00 -1.2e+01 12 638 369 1.0
sigma 1.0 2.6e-03 8.0e-02 9.2e-01 1.2 939 543 1.0
mu 0.16 1.8e-03 1.0e-01 -8.1e-03 0.33 3289 1902 1.0
Warmup took (0.011, 0.012, 0.011, 0.011) seconds, 0.044 seconds total
Sampling took (0.017, 0.020, 0.020, 0.019) seconds, 0.077 seconds total
Mean MCSE StdDev 5% 50% 95% N_Eff N_Eff/s R_hat
lp__ -54 2.5e-02 0.91 -5.5e+01 -53 -53 1318 17198 1.0
n_leapfrog__ 3.2 2.7e-01 1.7 1.0e+00 3.0 7.0 39 507 1.0
mu 0.17 2.1e-03 0.10 -3.8e-03 0.17 0.33 2408 31417 1.0
sigma 1.0 1.6e-03 0.071 9.3e-01 1.0 1.2 2094 27321 1.0
図24.2: \(y_n \sim \mathrm{Normal}(0, 1)\)から生成した100データ点を, デフォルトのパラメータを使ってStanであてはめを行なった結果. 上段は, 非正則一様事前分布と尤度\(y_n \sim \mathrm{Normal}(\lambda_1 + \lambda_2, \sigma)\)を使った識別できないモデル. 中段は, 上段32と同じ尤度で, 事前分布を\(\lambda_k \sim \mathrm{Normal}(0, 10)\)としたもの. 下段は, 尤度を\(y_n \sim \mathsf{Normal}(\mu, \sigma)\)とした, 識別できるモデル. すべてのモデルで, \(\mu\)はおよそ0.16と推定され, モンテカルロ標準誤差は非常に小さいのですが, 事後標準偏差は0.1と大きくなっています. 真の値\(\mu = 0\)は, 3つのモデルすべてで事後分布の90%区間に含まれています.
3つの例すべてについて, Stan 2.1.0でデフォルトのパラメータ(1000ウォームアップiteration, 1000サンプリングiteration, 最大tree depthが10のNUTSサンプラー)を用いて当てはめを行ないました. 結果は図24.2に示します. この出力から分かる主要な統計量は以下のとおりです.
R_hat
の列でわかるように, 識別されないモデルでは\(\lambda_1\)と\(\lambda_2\)以外のパラメータはすべて収束しました.最初の2点, 収束の欠如と, リープフロッグの最大ステップ数(最大tree depthも同じ)到達は, 非正則事後分布を示すものです. ギブズサンプラーで行なわれるような貧弱なサンプリングで問題を覆い隠すのではなく, ハミルトニアンモンテカルロは事後分布を探索しようとしますし, それが失敗するなら, モデルのどこかがおかしいことを明確に示しています.
vector
, and matrix
Stanには配列, vector
, matrix
3つのコンテナの型が用意されています. これら3つの型は取り換え可能ではありません. たとえ次元が一致していても, ある型の変数から別の型の変数に代入できません. Stanにおいて, 3 × 4のmatrix
は3 × 4の配列とは全く別種類のオブジェクトなのです.
vector
and matrix
vector
, matrix
は配列に比べると, 制限されたデータ構造になっています. vector
は本来real
を1次元状に集めたもので, matrix
は本来2次元状に集めたものです.
matrix
型の用途は, コード内で行列を使用していることを強調するためです. Stanにおいて, vector
,matrix
型を使うのはおそらく以下の3つの場合のみでしょう.
vector
, matrix
は整数値を返すことはできず, 取り扱えるのはreal
に限られています. (注1)
(注1):Stanにおいて, 複雑な整数行列演算や, ブール行列演算が実行されている際にはこれは変更される場合があります. これは, 整数が行列演算に適切な入力ではないためです.
他方, 配列は, は本来, 他の種類のオブジェクトを1次元状に集めたものです. 配列には単純なreal
やint
, vector
, matrix
, 別の配列のような, あらゆるデータ型の値を格納することができます. 配列はStanにおいて唯一, 整数値の並びを格納できる型です. Stanにおいては, 離散分布などの関数が整数値を引数とします. 2次元の配列は概念的にも現在の実装面からも, 配列を並べたものとして扱うことができます. インデックスを配列に与えると, 配列はそのインデックスにおける値を返します. 複数のインデックスを配列に与えると, 連鎖的にインデックスにおける値を返す動作が行われます. 例えば, a
という2次元配列があったとすると, a[m,n]
という書き方はa[m][n]
の便利な縮めた書き方にすぎません.
Stanの根底にある設計の動機の1つとして計算の効率があります.
Stanにおいて, 行列, 線形代数の演算はEigen C++ライブラリのデータ型をベースに実装されています. このため, 行列演算や線形代数の関数を使用する際vector
, matrix
を型としていれば, データ型を変換する必要はありません. 他方, 配列はC++のstd::vector
クラスのインスタンスとして実装されています. (EigenライブラリのEigen::vector
クラスや, Stanのvector
と混同しないように注意しましょう). 配列はこのように実装されているため, インデックスにおける値を返すのはとても効率的です. なぜならコピーして値を返すのではなく参照を使って値を返すからです.
matrix
vs. 2次元配列Stanのモデルにおいて, 2次元配列とmatrix
のどちらを使うか決めるときに, 効率について2,3個考えることがあります. 一見, 2次元配列とmatrix
のどちらを使ってもよく思われるかもしれません. 第1に, matrix
は2次元配列よりもメモリの使用量が少ない点です. これは, matrix
では, 配列の並び方は保存せず, データと2つの次元の情報だけを保存しているためです.
第2に, matrix
は「列優先」の順序でデータを格納する点です. さらに, matrix
内のすべてのデータはメモリ内で隣接することが保証されます. これは最適化されたコードを考えると大切なことです. なぜなら現代のCPUを使った算術演算を実行することよりもデータをメモリからキャッシュに持っていくことの方がはるかに時間がかかるからです. 他方, 配列はプリミティブ型の値はメモリ内で隣接することを保証しており, それ以外の場合はその値のコピーを保持します(可能な限り, 参照を使って値を返します).
第3に, いずれのデータ構造もデータが保持されている順序でインデックスを移動させると最も速くアクセスできます. メモリ上の位置もアクセス速度に関係します. matrix
は列優先であるため, 以下の順序でインデックスを移動させるのが適切です.
matrix[M,N] a;
//...
for (n in 1:N) //列が先
for (m in 1:M) //行が後
// ... a[m,n]を使った計算...
他方, 配列は以下の例のように行優先の順序でインデックスを移動させるべきです(すなわち, 最後のインデックスが最も移動するのが速い).
real a[M,N];
// ...
for (m in 1:M) //行が先
for (n in 1:N) //列が後
// ... a[m,n]を使った計算...
最初にa[m,n]
を使う際には, a[m]
をメモリに持ってくるように書くべきです. 一般的に, matrix
内の移動は, 配列内の移動よりも効率が良いです. これはmatrix
の配列についても同様です. 例えば, matrix
の2次元配列のインデックスを移動してアクセスするのに理想的な順序は以下になります.
matrix[M,N] b[I,J];
// ...
for (i in 1:I) //配列なので行が先
for (j in 1:J) //配列なので列が後
for (n in 1:N) //matrixなので列が先
for (m in 1:M) //matrixなので行が後
//... b[i,j,m,n] を使った計算...
a
がmatrix
の場合, a[m]
と表記するとそのmatrix
の行m
が抽出されますが, これはmatrix
を取り扱う上では非効率な操作です. もし複数のvector
にインデックスでアクセスする必要があるならば, vector
の配列を宣言する方がはるかに良いです.
row_vector[N] b[M];
// ...
for (m in 1:M)
//... row vector b[m]を使った計算 ...
これは以下のmatrix
を使った例よりも圧倒的に効率的です.
matrix b[M,N];
// ...
for (m in 1:M)
// ... row vector b[m]を使った計算 ...
同様に, 列ベクトルの配列に対してインデックスを移動させてアクセスする方が, matrix
の列を抽出するcol
関数を使うよりも効率的です. 対照的に, 行列演算や線形代数の関数として行われることは何でもmatrix
が一番速いでしょう. だから, もし予測変数と係数のドット積(訳注: 要素ごとにかけて和をとったもの, 内積)の行を作成したい場合には, 以下のように記述することで,
matrix[N,K] x; // 予測変数(説明変数, 共変量としても知られる)
// ...
vector[K] beta; // 係数
// ...
vector[N] y_hat; // 線形予測
// ...
y_hat <- x * beta;
以下のように書くよりも効率よく列を作ることができます.
row_vector[K] x[N]; // predictors (aka covariates)
// ...
vector[K] beta; // coeffs
...
vector[N] y_hat; // linear prediction
...
for (n in 1:N)
y_hat[n] <- x[n] * beta;
vector
vs. 1次元配列列ベクトルvector
と行ベクトルrow_vector
と1次元配列の間にはまったく違いがありません. . Eigen:vector
テンプレートと, C++
のstd:vector
テンプレートクラスは, double
型の値のコンテナとして非常に近い形で実装されています (Stanではreal
型). ただし, Stanにおいて整数値を格納できるのは配列だけです.
Stanはコンテナ(すなわち配列・vector
・matrix
)に対して, 整数値のインデックスの配列または範囲のインデックスを使うことで, 複数のインデックスによるアクセスを一度に行うことができます. 以降ではこの機能を「multiple indexing」と呼びます. この機能を使うと多くのモデルをベクトル化できます. 例えば, 傾きと切片が個体によって異なる階層線形回帰モデルの尤度を考えてみましょう. Stanのコードは以下になるでしょう.
for (n in 1:N)
y[n] ~ normal(alpha[ii[n]] + beta[ii[n]] * x[n], sigma);
multiple indexingを使うと, このコードは1行になり, より効率的なベクトル化されたコードになります.
y ~ normal(alpha[ii] + beta[ii] .* x, sigma);
後者のバージョンはローカル変数に代入するダサい方法とスピード面では等価です.
{
vector[N] mu;
for (n in 1:N)
mu[n] = alpha[ii[n]] + beta[ii[n]] * x[n];
y ~ normal(mu, sigma);
}
整数値の配列を使ったmultiple indexingの最も単純な具体例は以下です. 省略(...)はコメントにあるように変数を定義するコードを表します.
int c[3];
... // 定義: c == (5, 9, 7)
int idxs[4];
... // 定義: idxs == (3, 3, 1, 2)
int d[4];
d = c[idxs]; // 結果: d == (7, 7, 5, 9)
一般的にはmultiple indexを表現したc[idxs]
は, idxs
を大きさK
の配列として, 以下のように定義されます.
c[idxs] = ( c[idxs[1]], c[idxs[2]], ..., c[idxs[K]] )
このようにc[idxs]
はidxs
と同じ大きさになります. この例では大きさはK
です. multiple indexingは多次元配列にも使うことができます. 例えば, 以下を考えましょう.
int c[2, 3];
... // 定義: c = ((1, 3, 5), ((7, 11, 13))
int idxs[4];
... // 定義: idxs = (2, 2, 1, 2)
int d[4, 3]
d = c[idxs]; // 結果: d = ((7, 11, 13), (7, 11, 13),
// (1, 3, 5), (7, 11, 13))
すなわち, c[idxs]
のようにインデックスを1番目の位置に置くと, idxs
が1次元配列の場合に定義したのと全く同じように振る舞います. つまり, インデックスの指す値自体が配列になっているだけで, 結果はやはりc[idxs][j] == c[idxs[j]]
として定義されます.
multiple indexingは多次元配列の2番目の位置で使うこともできるでしょう. 上の例の続きとして, 1番目の位置にsingle indexで2番目の位置にmultiple indexを使う場合を考えましょう.
int e[4];
e = c[2, idxs]; // 結果: c[2] = (7, 11, 13)
// 結果: e = (11, 11, 7, 11)
1番目の位置にsingle indexを使うと, 1次元の結果となります. そして, その結果に対してmultiple indexが適用されます. すなわち, c[2,idxs]
はc[2][idxs]
と同じものとして評価されます.
multiple indexingは多次元配列の1つ以上の位置に対して適用できます. 例えば, 以下を考えましょう.
int c[2, 3];
... // 定義: c = ((1, 3, 5), (7, 11, 13))
int idxs1[3];
... // 定義: idxs1 = (2, 2, 1)
int idxs2[2];
... // 定義: idxs2 = (1, 3)
int d[3, 2];
d = c[idxs1, idxs2]; // 結果: d = ((7, 13), (7, 13), (1, 5))
multiple indexを複数の位置で使うと, もはやc[idxs1, idxs2]
はc[idxs1][idxs2]
と同じではなくなります. 上のコードを実行したあとのd[i, j]
の要素は以下で与えられるのです.
d[i, j] == c[idxs1, idxs2][i, j] = c[idxs1[i], idxs2[j]]
この例は一般的な場合においてmultiple indexingの動作を示したものです. idxs1
のようなmultiple indexは, 結果(ここではc[idxs1, idxs2]
)に対するインデックスi
を, アクセスの対象となる変数(ここではc
)におけるインデックスidxs1[i]
に変換します. 対照的に, single indexはそのインデックスにおける値を単に返します. そして変数の次元が1つ減った結果となります.
range indexを使ったアクセス(以降ではslicingと呼びます)は, 1次元配列の連続したスライスや2次元配列の連続した部分ブロックなどを返します. 意味的にはslicingはmultiple indexingの特殊な場合にすぎません.
例えば, インデックスに上限と下限を与える場合を考えましょう.
int c[7];
...
int d[4];
d = c[3:6]; // 結果: d == (c[3], c[4], c[5], c[6])
range indexである3:6
は意味的にはmultiple indexの(3, 4, 5, 6)
と同じように振る舞います. 実装の観点からはrange indexの方が高速かつ省メモリです. なぜなら, range indexはmultiple indexを陽に作っているわけではなく, むしろ直接的なループを使っているからです. さらにrange indexは読みやすいので, もし適用できるならばmultiple indexよりも好んで使うべきです.
下限だけ, 上限だけ与えることも可能です. c[3:]
はc[3:size(c)]
を略したものです. c[:5]
はc[1:5]
を略したものです.
最後に, 配列のすべての範囲をカバーするrange indexを書くこともできます. それは単にrangeの記号(:)をインデックスとして書くか, そのインデックスの位置を空にすることで実現できます. 両方の場合において, c[]
とc[:]
はc[1:size(c)]
と同じであり, それはすなわち単にc
と同じになります.
multiple indexingは代入文の左辺で使うこともできるでしょう. そこでは, 右辺でコンテナの要素を抽出するときとまったく同じ方法で動きます. 例えば, 以下を考えましょう.
int a[3];
int c[2];
int idxs[2];
... // 定義: a == (1, 2, 3); c == (5, 9)
// idxs = (3,2)
a[idxs] = c; // 結果: a == (1, 9, 5)
上のコードの代入文の結果は次のようになります. a[idxs[1]]
(すなわちa[3]
)にはc[1]
(すなわち5
)が代入され, a[idxs[2]]
(すなわちa[2]
)にはc[2]
(すなわち9
)が代入されます.
multiple indexが多数あるときには, 同じルールが以下のように適用されます.
int a[5, 7];
int c[2, 2];
...
a[2:3, 5:6] = c; // 結果: a[2, 5] == c[1, 1]; a[2, 6] == c[1, 2]
// a[3, 5] == c[2, 1]; a[3, 6] == c[2, 2]
1次元の場合と同じように, 左辺でスライスやブロックもしくは何らかのかたまりを指定し, 右辺がそこへ書き込まれます.
左辺でmultiple indexを使用して再配置やslicingをする際にも, 一般的にsingle indexは次元を減らし, multiple indexは次元を保ちます. 例えば, 以下のように2次元配列の行の一部分に代入することもできます.
int a[10, 13];
int c[2];
...
a[4, 2:3] = c; // 結果: a[4, 2] == c[1]; a[4, 3] == c[2]
代入の左辺と右辺で同じデータ構造を参照している際に, aliasingと呼ばれる問題が起きます. 例えば, 以下のコードにおける配列a
を考えましょう.
int a[3];
... // 定義: a == (5, 6, 7)
a[2:3] = a[1:2];
... // 結果: a == (5, 5, 6)
代入のあとにa
の値が(5, 5, 5)
ではなく(5, 5, 6)
になる理由は, Stanでは右辺の式が新しくコピーされるかのように振る舞うからです. もう一つの例として以下を考えましょう.
int a[3];
int idxs[3];
... // 定義 idxs = (2, 1, 3)
a[idxs] = a;
この場合, 代入の前にコピーが右辺の必要なのは明らかです. a[2:3] = a[1:2]
という代入では以下の一連の代入を実行するのと同じと思ってしまいがちです(それは間違いです).
... // 定義: a = (5, 6, 7)
a[2] = a[1]; // 結果: a = (5, 5, 7)
a[3] = a[2]; // 結果: a = (5, 5, 5)!
これは違う結果を生みます. なぜならa[2]
の値が使う前に変わっているからです.
vector
やmatrix
に対するmultiple indexmulitple indexはvector
やmatrix
にも使うことができますし, 同じようにvector
やmatrix
の配列にも使うことができます.
vector
の場合multiple indexを使う場合, vector
とrow_vector
は配列とまったく同じように振る舞います. もしv
がvector
ならばv[3]
はスカラーの実数値になる一方で, v[2:4]
は要素v[2]
,v[3]
,v[4]
を含む長さ3のvector
になります.
唯一少し違う点は, multiple indexを使った場合にどのような型を返すかの型推論が異なります. 例えば, 以下の最小の例を考えましょう.
vector[5] v[3];
int idxs[7];
...
vector[7] u;
u = v[2, idxs];
real w[7];
w = v[idxs, 2];
ポイントはsingle indexは常に次元を減らすのに対し, multiple indexは決して次元を減らさないことです. multiple indexを使った次元(とインデックスを使っていない次元)がインデックスされた式の型を決めます. 上の例ではv
がvector
の配列なので, v[2, idxs]
は配列の次元を減らすけれども, vector
の次元を減らしません(スカラーになりません). そして, w
の型はreal
の配列になります. 両方の場合において, multiple indexの大きさ(ここでは7
)が結果の大きさを決めています.
matrix
の場合matrix
の場合はもう少しトリッキーです. なぜならmatrix
は2つの次元を持つからです. しかし, 背後にある型推論のルールは同じです. multiple indexは次元を減らさないけれども, single indexは次元を減らすというルールです. 以下のコードはmatrix
に対するmultiple indexingがどのように動くか示します.
matrix[5,7] m;
...
row_vector[3] rv;
rv = m[4, 3:5]; // 結果は 1 x 3
...
vector[4] v;
v = m[2:5, 3]; // 結果は 3 x 1
...
matrix[3, 4] m2;
m2 = m[1:3, 2:5]; // 結果は 3 x 4
ポイントはどの位置にmultiple indexやrange indexを使っても結果に反映されている一方で, single indexを使うと1次元になることです. 上のコードのコメントで示唆した結果の次元から, 結果の型を読み取ることができるでしょう.
matrix
に1つのmultiple indexを使うもしmatrix
が1つのmultiple indexを受け取ると, 結果はmatrix
になります. したがってもしm
がmatrix
ならm[2:4]
はmatrix
です. 対照的に, m[3]
のようにsingle indexを与えると結果はrow_vector
になります. すなわち, m[3]
はm[3, ]
やm[3, 1:cols(m)]
と同じ結果になります.
vector
やmatrix
の配列matrix
やvector
やrow_vector
の配列の場合も, 基本的なルールはまったく同じままです. 例えば, 以下の例を考えましょう.
matrix[3, 4] m[5, 7];
...
matrix[3, 4] a[2];
a = m[1, 2:3]; // 1番目の配列の次元を減らす
a = m[3:4, 5]; // 2番目の配列の次元を減らす
両方の代入において, multiple indexは配列の次元を減らしています. しかし, 結果は異なります. 1番目のケースではa[i] == m[1, i + 1]
となる一方, 2番目のケースではa[i] == m[i + 2, 5]
となります.
前の例に続けて以下を考えましょう(訳注:以降26.5節まで原文ではa
でしたがm
に変更しました).
...
vector[2] b;
b = m[1, 3, 2:3, 2];
ここでは2つの配列の次元が減って, matrix
の列の次元も減り, 行の次元だけ残るので, 結果はvector
になります. この場合では, b[j] == m[1, 3, 1 + j, 2]
です.
この最後の例は大切なポイントを表しています. もし, 下限2
が与えらえた2:3
のように下限のあるインデックスがあると, 上の例で1 + j
という式で見られるように下限引く1である1
がインデックスj
に加わります.
さらに続けて, 次を考えましょう.
...
row_vector[3] c[2];
c = m[4:5, 3, 1, 2: ];
ここでは2番目の配列の次元が減って1次元配列となり, matrix
の行のインデックスが減ってrow_vector
となっています. インデックスによるアクセスの結果, 値はc[i, j] == a[i + 3, 3, 1, j + 1]
となります.
matrix
3行3列のmatrix
があって, 2つの要素がゼロで残りの要素がパラメータである場合を考えてみましょう. そのような状況は固定されたパラメータを伴う問題や欠測データがある場合に起こります.
ある3行3列のmatrix
が[1, 2]
と[1, 3]
の要素はゼロだと知られているとしましょう. この場合, パラメータに対するインデックスは「融けた」データフレームやデータベースのフォーマットで表されます.
transformed data {
int<lower=1, upper=3> ii[7];
int<lower=1, upper=3> jj[7];
ii[1] = 1; jj[1] = 1;
ii[2] = 2; jj[2] = 1; // [1, 2] と [1, 3] を飛ばす
ii[3] = 3; jj[3] = 1;
ii[4] = 2; jj[4] = 2;
ii[5] = 3; jj[5] = 2;
ii[6] = 2; jj[6] = 3;
ii[7] = 3; jj[7] = 3;
}
7つの残っているパラメータはvector
として宣言されます.
parameters {
vector[7] A_raw;
}
そして, フル行列A
はmodel
ブロックにおいてローカル変数として構築されます.
model {
matrix[3, 3] A;
A[ii, jj] = A_raw;
A[1, 2] = 0;
A[1, 3] = 0;
}
この状況ではこの方法はやりすぎに見えるかもしれません. しかし, より一般的な状況では, 固定値の要素を埋めるためにmatrix
のサイズ・vector
の長さ・配列ii
とjj
や大きさを, 固定値の値とともにデータとしてコードに書くことになるでしょう. 一握りの要素が正だとわかっている場合など, アドホックな制約があるmatrix
を構築するには, 似たテクニックが使われるでしょう.
Gelmanら(2013)はベイズデータ解析の特徴を以下のように述べています.
ベイズデータ解析と言うときは, 観測する量や, 知りたい量について, 確率モデルを使ってデータから推定を行なう実用的な方法という意味で言っています.
続けて, ベイズ統計が頻度主義の方法とどのように異なるかを記述しています.
ベイズ法の本質的な特徴は, 統計解析に基づく推定の際, 不確実性を定量化するのに確率を明示的に使うところにあります.
厳密な頻度主義者は, 観測値の相対頻度の極値を確率と見なしますから, パラメータについて確率的に言明することを禁止します. パラメータは固定したもので, 確率変数ではないとされるのです.
ベイズ主義者も, パラメータを固定した, 未知のものと扱います. しかし頻度主義者とは異なり, パラメータの事前分布と, パラメータの事後分布の両方を使います. これら事前分布・事後分布と事後予測確率は, パラメータと将来の観測値についての知識を特徴づけるためにあります. 以下で述べるように, 事後分布はベイズ推定の基礎を形づくるものです.
Gelmanら(2013)は, 応用ベイズモデリングを以下の3ステップに分解しています.
すべての観測可能な量と観測不可能な量とについてのフル確率モデルをつくります. このモデルは, モデリングするデータとそれがどのように集められたかということについて, 既存の知識と合致するべきです.
観測された量を条件として未知の量の事後確率を計算します. 未知の量には, パラメータのような観測できない量や, 将来の観測の予測値のような潜在的には観測可能な量といったものが含まれるでしょう.
データに対するモデルの当てはまりを評価します. これには, 事後分布の効果を評価することも含まれます.
典型的には, 3番目のステップでじゅうぶんな当てはまりが得られるまでこのサイクルは繰り返されます. Stanは, 2番目と3番目のステップに含まれる計算を自動化します.
ベイズ推定のメカニズムはそのままベイズの定理に従っています. 記法を定めて, \(y\)でデータのような観測された量を表し, \(\theta\)でパラメータや将来の観測値のような未知の量を表すとします. \(y\)と\(\theta\)の両方が確率変数としてモデリングされるでしょう. 定数や, ハイパーパラメータ, 予測変数のような, 既知ですがモデリングされない量を\(x\)で表すとします.
確率関数\(p(y,\theta)\)は, データ\(y\)とパラメータ\(\theta\)の同時確率関数です. 定数および予測変数\(x\)は, 明示されていませんが条件に含まれていることになっています. パラメータ\(\theta\)が与えられたときのデータ\(y\)の条件付き確率関数\(p(y \mid \theta)\)はサンプリング確率関数と呼ばれます. また, \(y\)と\(x\)を固定して\(\theta\)の関数と見たときには尤度関数とも呼ばれます.
定数\(x\)が与えられたときのパラメータについての確率関数\(p(\theta)\)は事前分布と呼ばれます. というのは, データがまだまったく観測されていないときのパラメータの確率を特徴づけるものだからです. 条件付き確率関数\(p(\theta \mid y)\)は事後分布と呼ばれます. 観測データ\(y\)と定数\(x\)とが与えられたときのパラメータの確率を特徴づけるものだからです.
ベイズ推定の技術的な仕組みは, 以下の一連の式のように決まっています. これはベイズの定理のさまざまな形式として知られています(ここでも, 定数\(x\)は明示していません).
\[ \begin{array}{lll} p(\theta\mid y) &= \frac{p(\theta, y)}{p(y)} & [\mbox{条件付き確率の定義}]\\ &= \frac{p(y\mid\theta)p(\theta)}{p(y)} & [\mbox{連鎖律}] \\ &= \frac{p(y\mid\theta)p(\theta)}{\int_{\Theta}p(y,\theta)d\theta} & [\mbox{全確率の法則}] \\ &= \frac{p(y\mid\theta)p(\theta)}{\int_{\Theta}p(y\mid\theta)p(\theta)d\theta} & [\mbox{連鎖律}] \\ &\propto p(y\mid\theta)p(\theta) & [\mbox{yは固定値}] \end{array} \]
ベイズの定理は, 事後確率\(p(\theta \mid y)\)を「ひっくり返し」て, 尤度\(p(y \mid \theta)\)と事前分布\(p(\theta)\)だけからなる項で表します(ここでも, 定数および予測変数\(x\)は明示していません). 最後のステップがStanにとって重要で, 必要なのは定係数を除いた確率関数の部分のみです.
(与えられたモデルで)データ\(y\)から推定されるパラメータ\(\theta\)の推定値の不確実性は, 事後分布\(p(\theta\mid y)\)により特徴づけられます. したがって, 事後分布はベイズ予測推定にとってきわめて重要です.
\(\tilde{y}\)が新しい, おそらくはまだ未知の観測値で, \(\tilde{x}\)がそれに対応する定数および予測変数とすると, 事後予測確率は次式で与えられます.
\[ p(\tilde{y} \mid y) = \int_{\Theta} p(\tilde{y} \mid \theta)p(\theta\mid y)d\theta \]
ここでは, もともとの定数および予測変数\(x\)と, 新しい定数および予測変数\(\tilde{x}\)はともに明示されていません. 事後確率自身と同様, 予測推定は確率的に特徴づけられます. パラメータ\(\theta\)の点推定値を使うのではなく, データ\(y\)(と定数\(x\))が与えられたときの\(\theta\)の事後確率\(p(\theta \mid y)\)によって重みづけつつ, \(\theta\)の範囲全体について予測値を平均することにより, 予測が行なわれます.
事後分布は, イベントの確率を推定することにも使われます. 例えば, パラメータ\(\theta_k\)が0より大きいという確率は, 次式により確率的に特徴づけられます.
\[ \Pr[\theta_{k}>0] = \int_{\Theta}\mathrm{I}(\theta_{k}>0)p(\theta \mid y)d\theta \]
指示関数\(\mathrm{I}(\phi)\)は, 命題\(\phi\)が真ならば1と, そうでなければ0と評価されます.
将来の観測を含む比較も同様にできるでしょう. 例えば, \(\tilde{y}_n > \tilde{y}_{n'}\)となる確率は, 以下の事後予測確率関数により特徴づけることができます.
\[ \Pr[\tilde{y}_{n} > \tilde{y}_{n'}] = \int_{\Theta}\int_{Y}\mathrm{I}(\tilde{y}_{n} > \tilde{y}_{n'})p(\tilde{y}\mid\theta)p(\theta\mid y)d\tilde{y}d\theta \]
パラメータをデータに当てはめた後は, モデルの推定を前向きに走らせることにより, そのパラメータを新しいデータセットをシミュレートするのに使うことができます. すると, このような複製データセットと元のデータとを視覚的あるいは統計的に比較して, モデルの当てはまり具合を調べることができます(Gelman et al., 2013, 6章).
Stanでは, 事後シミュレーションは2通りの方法で生成できます. 1つ目の方法は, 予測される変数をパラメータとして扱い, model
ブロックでその分布を定義します. 2つ目の方法は, 離散変数でも適用可能で, generated quantitiy
ブロックで乱数発生器を使って複製データを生成します.
BUGSと同じく, Stanも推定のため, 事後分布からのサンプルを生成するのに, マルコフ連鎖モンテカルロ(MCMC)という技術を使います.
モンテカルロ法は, 解析的には解けずとも, 積分された関数の評価はできるような積分を数値的に近似するために開発されました(Metropolis and Ulam, 1949).
例を挙げると, 確率密度\(p(\theta)\)の平均\(\mu\)は次の積分で定義されます.
\[ \mu = \int_{\Theta} \theta \times p(\theta) d\theta \]
非常に複雑というほどではないベイズモデルでも, 事後密度\(p(\theta \mid y)\)は, 解析的には評価できない積分になってしまいます. 事後分布は定数と予測変数\(x\)にも依存しますが, これ以降は省略して既知のものとします.
ここで, \(p(\theta)\)から独立なサンプルを抽出できるとして, \(\theta^{(1)}, \theta^{(2)},\dots,\theta^{(N)}\)を\(N\)個のそのようなサンプルとします. \(p(\theta)\)の平均\(\mu\)のモンテカルロ推定値\(\hat{\mu}\)は標本平均として与えられます.
\[ \hat{\mu} = \frac{1}{N}\sum_{n=1}^{N} \theta^{(n)} \]
確率関数\(p(\theta)\)が有限の平均と分散を持つなら, 大数の法則により, サンプルサイズが増加するとモンテカルロ平均は正しい値に収束することが保証されます.
\[ \lim_{N \to \infty} \hat{\mu} = \mu \]
有限の平均と分散を仮定すると, 推定誤差は中心極限定理に従うので, \(N\)の平方根に反比例して減少します.
\[ |\mu - \hat{\mu}| \propto \frac{1}{\sqrt{N}} \]
したがって, 平均を1桁正確に推定するためには, 100倍のサンプルが必要になります. 2桁精度を上げるには1万倍のサンプルということになります. モンテカルロ法が, 非常に正確な推定値というよりも数桁以内の大雑把な推定に向いているのはこの理由によります. 実用上も, ある量を点推定するのに, 元になるデータのサンプルが持つ不確実性よりも精度が上がることはありません. そのため, 何桁もの精度が得られないことが, 統計モデルの実用の際に問題になることは滅多にありません.
マルコフ連鎖モンテカルロ(MCMC)法は, 独立したサンプルを単純には抽出できないような状況のために開発されました(Metropolisら, 1953).
マルコフ連鎖は, 確率変数\(\theta^{(1)}, \theta^{(2)},\dots\)の系列であり, ある変数は, 直前の値が与えられたとき, それ以外の変数とは条件付き独立になります. したがって, \(\theta = \theta^{(1)}, \theta^{(2)},\dots,\theta^{(N)}\)ならば次式のようになります.
\[ p(\theta) = p(\theta^{(1)}) \prod_{n=2}^{N} p(\theta^{(n)} \mid \theta^{(n-1)}) \]
Stanでは, その次の状態を生成するのに, 33章で述べる方法でハミルトニアンモンテカルロ法を使っています.
Stanおよび他のMCMCサンプラーが生成するマルコフ連鎖は, マルコフ連鎖の中心極限定理に必要という意味でエルゴード的です. 大雑把にいうと, \(\theta\)のある値に他の値から適当な可能性で到達するという意味です. このマルコフ連鎖はまた定常的です. これは, 連鎖中の別の位置でも推移確率は異ならないという意味です. したがって, \(n,n\prime \ge 0\)について, 確率関数\(p(\theta^{(n+1)} \mid \theta^{(n)})\)は\(p(\theta^{(n\prime + 1)} \mid \theta^{(n\prime)})\)と同じです(慣例に従って確率変数や束縛変数は使い回しをしており, 確率関数は引数で区別しています).
定常マルコフ連鎖は, それぞれが同じ周辺確率関数を持つような状態について平衡分布を持ちます. したがって, \(p(\theta^{(n)})\)は\(p(\theta^{(n+1)})\)と同じ確率関数です. Stanでは, 平衡分布\(p(\theta^{(n)})\)は, サンプリングされる確率関数\(p(\theta)\)であり, 一般にはベイズ事後密度です.
MCMC法を使うには, 独立サンプルのモンテカルロ法にはない2つの難題があります. 1つめの問題は, ランダムに初期化されたマルコフ連鎖が, いつその平衡分布に収束したかを決定することです. 2つめの問題は, マルコフ連鎖からの抽出に相関があることです. そうなると, 中心極限定理による推定誤差への制約はもはや当てはまりません. これらの問題は次の2つの節で扱います.
マルコフ連鎖が目的分布からのサンプルを生成するのは, 平衡に収束した後だけです. 残念ながら, これが保証されるのは理論的には極限においてのみです. 実用的には, マルコフ連鎖が収束したかどうかをモニタリングするための診断が必要になります.
連鎖が平衡分布に収束したかどうかをモニタリングする方法の1つは, ランダムに初期化された別の連鎖と性質を比較することです. これは, Gelman and Rubin (1992)のpotential scale reduction 統計量\(\hat{R}\)の動機づけとなっているものです. \(\hat{R}\)統計量は, 各連鎖内でのサンプルの分散の平均と, 連鎖をまたいでプールしたサンプルの分散との比を測定します. もしすべての連鎖が平衡に達しているなら, これらはすべて同じで\(\hat{R}\)は1になるでしょう. 連鎖が共通の分布に収束していないなら, \(\hat{R}\)は1より大きくなるでしょう.
GelmanとRubinが推奨するところでは, ばらばらの初期値をパラメータに与えて, 独立したマルコフ連鎖を初期化し, すべての値について\(\hat{R}\)が1.1より小さくなるまでサンプリングします. Stanでは, ユーザーがパラメータの初期値を指定できますし, 乱数によってばらばらの初期値を抽出することもできます.
\(\hat{R}\)統計量は, \(M\)本のマルコフ連鎖の組\(\theta_{m}\)について定義されます. このとき, 各連鎖は\(N\)サンプル\(\theta_{m}^{(n)}\)からなっています. サンプル間分散の推定値は次式です.
\[ B = \frac{N}{M-1} \sum_{m=1}^{M}(\bar{\theta}_{m}^{(\bullet)} - \bar{\theta}_{\bullet}^{(\bullet)})^{2} \]
ここで,
\[ \bar{\theta}_{m}^{(\bullet)} = \frac{1}{N}\sum_{n=1}^{N}\theta_{m}^{(n)} \quad \text{\mbox{かつ}} \quad \bar{\theta}_{\bullet}^{(\bullet)} = \frac{1}{M}\sum_{m=1}^{M}\bar{\theta}_{m}^{(\bullet)} \]
サンプル内分散は次式です.
\[ W = \frac{1}{M}\sum_{m=1}^{M}s_{m}^{2} \]
ここで,
\[ s_{m}^{2} = \frac{1}{N-1}\sum_{n=1}^{N}(\theta_{m}^{(n)}-\bar{\theta}_{m}^{(\bullet)})^{2} \]
分散の推定量は次式です.
\[ \widehat{\mathrm{var}}^{+}(\theta \mid y) = \frac{N-1}{N}W + \frac{1}{N}B \]
最後に, potential scale reduction 統計量は以下のように定義されます.
\[ \hat{R} = \sqrt{\frac{\widehat{\mathrm{var}}^{+}(\theta \mid y)}{W}} \]
ここで, 各連鎖が異なる数のサンプルからなっていても良いとします. \(N_{m}\)を連鎖\(m\)内のサンプルの数とします. このとき, 連鎖\(m\)についての連鎖内平均の式には連鎖のサイズ\(N_{m}\)を使います.
\[ \bar{\theta}_{m}^{(\bullet)} = \frac{1}{N_{m}}\sum_{n=1}^{N_{m}}\theta_{n}^{(m)} \]
連鎖内分散の推定値も同様です.
\[ s_{m}^{2} = \frac{1}{N_{m}-1}\sum_{n=1}^{N_{m}}(\theta_{m}^{(n)}-\bar{\theta}_{m}^{(\bullet)})^{2} \]
\(\bar{\theta}_{\bullet}^{(\bullet)}\), \(B\), \(W\)といった, 連鎖をまたいで平均をとる項は前と同じ定義で, 各連鎖が推定値に同じ効果を持つことが保証されます. もし平均がサイズで重み付けされるとしたら, 1本の長い連鎖が統計量に強い影響を与え, 複数の連鎖を使って収束をモニタリングするという目的は達成できないでしょう.
\(N\)という項を含むので, 推定値\(\widehat{\mathrm{var}}^{+}\)は一般化する必要があります. 最初の項を展開します.
\[ \frac{N-1}{N}W = \frac{N-1}{N}\frac{1}{M}\sum_{m=1}^{M}\frac{1}{N-1}\sum_{n=1}^{N}(\theta_{m}^{(n)}-\bar{\theta}_{m}^{(\bullet)})^{2} = \frac{1}{M}\sum_{m=1}^{M}\frac{1}{N}\sum_{n=1}^{N}(\theta_{m}^{(n)}-\bar{\theta}_{m}^{(\bullet)})^{2} \]
第2項です.
\[ \frac{1}{N}B = \frac{1}{M-1}\sum_{m=1}^{M}(\bar{\theta}_{m}^{(\bullet)}-\bar{\theta}_{\bullet}^{(\bullet)})^{2} \]
分散の推定量は自然に一般化されます.
\[ \widehat{\mathrm{var}}^{+}(\theta \mid y) = \frac{1}{M}\sum_{m=1}^{M}\frac{1}{N_{m}}\sum_{n=1}^{N_{m}}(\theta_{m}^{(n)}-\bar{\theta}_{m}^{(\bullet)})^{2} + \frac{1}{M-1}\sum_{m=1}^{M}(\bar{\theta}_{m}^{(\bullet)}-\bar{\theta}_{\bullet}^{(\bullet)})^{2} \]
連鎖がすべて同じ長さならば, この定義は前の節でのものと等価です. この一般化された分散の推定量と連鎖間分散の推定値は, 前の節の\(\hat{R}\)の式に直接組み込めるでしょう.
potential scale reduction 統計量\(\hat{R}\)を計算する前に, 各連鎖を半分ずつ2つに分割することもできます. これにより追加の平均が得られ, 連鎖内での非定常性を検出できます. 1本の連鎖が次第に増加するような値を取っており, もう1本の連鎖が次第に減少する値を取っているとき, それらはうまく混合してはいないのに, \(\hat{R}\)の値は1に近い値を取ることがありえます. この場合, 各連鎖を2つに分割すると, 各連鎖の前半と後半とは混合していないので, \(\hat{R}\)の値は1よりもかなり大きくなるでしょう.
よくある疑問は, パラメータあるいは生成量の一部だけの収束をモニタリングしても良いかどうかというものです. 端的に言うと答えは「いいえ」ですが, これについてはこの節でさらに詳しく述べます.
例えば, lp__
の値を考えましょう. これは, 対数事後密度(定数は除く)です. もしlp__
が収束していないなら, いかなる実用的な意味においても収束したと結論づけるのは誤りです. これは, 全ての連鎖は実際にパラメータ空間の別々の部分に存在しているからです. とはいえ, lp__
の収束を測定するのは, 以下に書くようにとりわけトリッキーです.
マルコフ連鎖の収束は, モニタリングするパラメータの関数をどう選ぶかに依存しないという意味で大域的な特性です. 収束前の「過渡的な状態」と収束後の「平衡」との間には明確な境界はありません. ここで起きていることは, 連鎖内の状態の数が無限に近づくにつれ, 連鎖内の取り得る状態の分布が目的分布に近づき, その極限では, あらゆる積分可能なモンテカルロ推定量の期待値が真の期待値に収束するということです. ここではウォームアップのようなものはありません. というのは, 極限では初期状態の影響は完全に消えているからです.
\(\hat{R}\)統計量は, あるマルコフ連鎖とある関数の合成を考えていることになりますので, マルコフ連鎖が収束していれば, マルコフ連鎖と関数の合成は全て収束するでしょう. 多変量の場合の関数の収束は, Cramer-Woldの定理により, 確率変数のあらゆる周辺和が収束することと等価です.
制約のない空間から制約のある空間への変換は単にもう一つ別の関数となるため, 収束には影響ありません.
異なる関数は異なる自己相関を持つ可能性があります. しかしマルコフ連鎖が平衡に達していれば, マルコフ連鎖と関数の合成はすべて一貫して収束するはずです. 正式には, 収束していないように見える関数があれば, どんなものであれ不安材料となります. あらゆる関数を検証するのは合理的ではないでしょうが, 少なくともlp__とその他の測定された量は一貫して収束しているべきです.
lp__
が目立って異なる点は, 位置が急速に変化する傾向があり, そのため外れ値の影響を受けやすいことです.
状態の数が有限だとどうなるでしょう? 強い幾何学的エルゴード性を証明できるなら(これは, サンプラーと目的分布に依存します), 連鎖が初期状態を忘れるような有限な時間が, 高い確率で存在することを示すことができます. これは自己相関時間とウォームアップ時間の両方についてです. しかし, それが存在して有限であること示せるとしても(ほとんど不可能ですが), 実際の値を解析的に計算することはできません.
したがって実際には, 期待値がかなり正確と言えるほどに, 有限の抽出数が十分に大きいことを期待することしかできません. ウォームアップのiterationを取り除くことは期待値の正確さを改善しますが, どれだけの有限な数のサンプルを取り除けば十分であるかは保証されません.
ここで心配すべきことは2つあります.
1つめとして, 上に記したように, 有限の抽出数ではどれほどであっても, 何がしかの初期状態の効果が常に残るでしょう. これは, いくらかの小さな確率(あるいは, 自己相関時間がとても大きければ大きな確率)で大きな外れ値を持つという形で現れます. そのような外れ値に頑健な関数(例えば分位数)なら, より安定して見えますし, より良い\(\hat{R}\)を持つでしょう. そのような外れ値に脆弱な関数なら, 脆さが見える結果となるかもしれません.
2つめとして, \(\hat{R}\)統計量の使用には非常に強い仮定を置いています. とりわけ, 関数が正規分布とみなせると仮定していますし, 最初の2つのモーメント(訳注: 平均と分散)のみを使い, ある種の独立性を仮定しています. ポイントは, この強い仮定が常に成り立つわけではないということです. とくに, 対数事後密度(lp__
)の分布はほとんど正規分布に見えることがありません. \(N\)が非常に大きくなっても, 裾が長くて\(\hat{R}\)が大きくなる可能性があります. 生の値の代わりに分位数を使うといった小技により, 興味のあるサンプルをより正規分布に近づけ, その結果\(\hat{R}\)をより正確にする傾向を高めることができます.
「収束」は大域的な特性であり, すべての積分可能な関数について同時に当てはまりますが, \(\hat{R}\)統計量を採用することには追加の仮定が必要であり, 関数すべてに同じようにうまくいくとは限りません.
連鎖間で期待値を比較するだけでも, マルコフ連鎖の正規分布への漸近性に関する信頼性を確認することができ, 標準的な検定も適用できることに注意してください.
MCMC法がもたらす技術的な難題の2つめは, 一般に連鎖内でサンプルが自己相関を持つことです. これにより, 平均や分散, 分位数といった, 事後分布において興味のある量を推定する際に不確実性が大きくなります.
MCMCの結果の解析一般について, またとくに有効サンプルサイズについての良い入門的な参考文献がGeyer (2011)です. Stanがまさに使っている計算は分割\(\hat{R}\)の計算に従っていて, これには連鎖をプールした計算(平均)と連鎖内の計算(自己相関)の両方が含まれています. これらはこのマニュアルで紹介されますが, Gelmanほか(2013)でより詳細に説明されています.
連鎖内の自己相関が推定値の不確実性をどのくらい増やしているかは有効サンプルサイズ(ESS)で測ることができます. 独立なサンプルならば, サンプルの数\(N\)に基づいて中心極限定理が推定値の不確実性を制限します. 独立でないサンプルの場合は, 独立なサンプルの数は有効サンプルサイズ\(N_\mathrm{eff}\)に置き換えられます. これは, \(N\)個の自己相関のあるサンプルと同じ推定能力を持つ独立なサンプルの数です. 例えば, 推定誤差は, \(1/\sqrt{N}\)ではなく\(1/\sqrt{N_\mathrm{eff}}\)に比例します.
ある系列の有効サンプルサイズは, その系列におけるラグの異なる自己相関で定義されます. 平均\(\mu\), 分散\(\sigma^2\)の同時確率分布\(p(\theta)\)を持つ連鎖の, ラグ\(t \ge 0\)での自己相関は次式のように定義されます.
\[ \rho_{t} = \frac{1}{\sigma^2}\int_{\Theta}(\theta^{(n)}-\mu)(\theta^{(n+t)}-\mu)p(\theta)d\theta \]
これはまさに, 2本の連鎖の間の相関に, 位置\(t\)だけオフセットをつけたものです. MCMCの設定上, \(\theta^{(n)}\)と\(\theta^{(n+t)}\)とは同じ周辺分布を持つことがわかっていますから, 2つの差の項を掛け算して整理すると次式になります.
\[ \rho_{t} = \frac{1}{\sigma^2}\int_{\Theta}\theta^{(n)}\theta^{(n+t)}p(\theta)d\theta \]
自己相関\(\rho_t\)を持つ過程から生成された\(N\)サンプルの有効サンプルサイズは次式で定義されます.
\[ N_\mathrm{eff} = \frac{N}{\sum_{t=-\infty}^{\infty}\rho_{t}} = \frac{N}{1+2\sum_{t=1}^{\infty}\rho_{t}} \]
実用的には, 問題とする確率関数は簡単には積分できませんし, したがって自己相関も有効サンプルサイズも計算できません. そのかわり, こうした量はサンプル自身から推定される必要があります. この節の残りでは, バリオグラムに基づいた自己相関の推定量, それから複数の連鎖に基づいた有効サンプルサイズについて記述します. 簡単のため, 各連鎖\(\theta_m\)の長さはどれも\(N\)であると仮定しましょう.
有効サンプルサイズを推定する方法の1つは, ラグ\(t \in \{0,1,\dots\}\)におけるバリオグラム\(V_t\)に基づくものです. バリオグラムは, (1変量の)サンプル\(\theta_m^{(n)}\)について以下のように定義されます. ここで, \(m \in \{1,\dots,M\}\)は連鎖であり, \(N_m\)は連鎖\(m\)のサンプルの数です.
\[ V_t = \frac{1}{M}\sum_{m=1}^{M}\left(\frac{1}{N_{m}-t}\sum_{n=t+1}^{N_{m}}(\theta_{m}^{(n)}-\theta_{m}^{(n-t)})^{2}\right) \]
前の節で導入した複数連鎖の分散の推定値\(\widehat{\mathrm{var}}^{+}\)とバリオグラムを使うと, ラグ\(t\)における自己相関を推定できます.
\[ \hat{\rho}_{t} = 1 - \frac{V_{t}}{2\widehat{\mathrm{var}}^{+}} \]
もし連鎖が収束していなければ, 分散の推定量\(\widehat{\mathrm{var}}^{+}\)は分散を過大に推定し, そのため自己相関を過大に推定し, 有効サンプルサイズを過小に推定することになります.
\(t\)が増加すると, 相関の推定値\(\hat{\rho}_t\)にノイズが増えるため, 通常は\(\hat{\rho}_t > 0\)となるような\(\hat{\rho}_t\)の初期の推定値だけが使われます. \(T\prime\)を, \(\rho_{T\prime + 1} < 0\)となるような最初のラグとします.
\[ T' = \mathop{\mathrm{argmin}}\limits_{t} \hat{\rho}_{t+1} < 0 \]
有効サンプルサイズの推定量は次式のように定義されます.
\[ \hat{N}_\mathrm{eff} = \frac{1}{2}\frac{MN}{1 + \sum_{t=1}^{T\prime}\hat{\rho}_{t}} \]
(訳注: Gelmanほか(2013)では, \(\hat{N}_\mathrm{eff} = \frac{MN}{1 + 2 \sum_{t=1}^{T}\hat{\rho}_{t}}\), ただし\(T\)は\(\hat{\rho}_{T+1} + \hat{\rho}_{T+2}\)が負になるような最初の奇数, となっています. )
適正な自己相関が生じ得るのは, ラグが奇数のときだけです(Geyer, 2011). 自己相関はペア間で足し合わせることにより, positive modulo estimator noiseとなることが保証されます. これは, Geyer (2011)において多数の打ち切り基準が提案された背景になっています. Stanは(まだ)ペアでの期待値を計算しませんが, これは, そのつくりからNUTSが負の自己相関の領域をほぼ避けるためです. したがって, 負の自己相関が現れたところで打ち切りを行うのは, 自己相関推定量の中でノイズが大半を占めるようになったら打ち切りを行うことの合理的な近似になっています.
Stanはすべてのラグに対する自己相関を同時に計算します. その際, 適当なパディングを付与して, Eigenの高速フーリエ変換(FFT)パッケージを利用します. 自己相関の計算にFFTを使うことの詳細についてはGeyer (2011)を参照してください.
一般的な状況では, 自己相関\(\rho_t\)は, ラグ\(t\)が増加するにつれて減少します. このようなとき, サンプルを間引くことにより, 自己相関が減るでしょう. 例えば, 以下の2つの方法のうちの1つで1000サンプルを生成するとします.
両方とも1000サンプルを生成するとはいえ, 間引きのある2番目の方法がより有効なサンプルを生成するでしょう. これは, 間引かれた系列の自己相関\(\rho_t\)は, 間引かれていないサンプルの\(\rho_{10t}\)に相当するからです. そのため, 自己相関の和は小さくなり, そして有効サンプルサイズは大きくなるでしょう.
一方, メモリとデータの容量に問題がないなら, 10000サンプルすべてを保存する方が, 1000サンプルに間引くよりも有効サンプルサイズは大きくなるでしょう.
この章では, ベイズ推定ではありませんが, 最尤推定と罰則付き最尤推定というよく使われる方法を定義し, 事後分布の平均値・中央値・最頻値を使って, それらをベイズ点推定に関連づけます. 最尤推定値は, 事後分布ではなく, モデルのパラメーター\(\theta\)についての単一の値から求められるので, 「点推定値」と呼ばれます.
どのような尤度関数も罰則関数もStanのモデリング言語でコーディングすることができるので, Stanのオプティマイザ(optimizer)を使って(罰則付き)最尤推定を実装することができます. Stanのオプティマイザはまた, 事後最頻値に基づくベイズの枠組みでの点推定にも使うことができます. Stanのマルコフ連鎖モンテカルロサンプラーは, 事後分布の平均値や中央値に基づくベイズモデルでの点推定を実装するのにも使えます.
尤度関数\(p(y\mid\theta)\)と, 固定されたデータのベクトル\(y\)とが与えられたとき, 尤度を最大にするようなパラメータのベクトル\(\hat{\theta}\)のことを最尤推定値(maximum likelihood estimate, MLE)といいます.
\[\hat{\theta}=\mathrm{argmax}_{\theta}\ p(y\mid\theta)\]
通常は対数スケールにするほうが便利です. 次式も, MLEの定式化として等価です. 1
\[\hat{\theta}=\mathrm{argmax}_{\theta}\ \log p(y\mid\theta)\]
1 この等価性は, 密度が正値であり, 対数関数が厳密に単調であるという事実から導かれます. すなわち, \(p(y\mid\theta) \ge 0\), かつ, すべての\(a,b>0\)について, \(a>b\)のときのみ\(\log a > \log b\)です.
すべての関数がただひとつの最大値を持っているとは限りませんので, 最尤推定値が存在することは保証されるものではありません. 24章で議論したように, この状況は以下のようなときに起こります.
こうした問題は, 次の節で議論する罰則付き最尤推定でも, その後の節で議論するベイズ事後最頻値でもついて回ります.
通常の線形回帰の問題を考えます. 観測値\(y\)が\(N\)次元のベクトル, 予測変数が\((N \times K)\)次元のデータ行列\(x\), 回帰係数が\(K\)次元のパラメータのベクトル\(\beta\), 実数値のノイズのスケールが\(\sigma > 0\)のとき, 尤度関数は次式のようになります.
\[\log p(y\mid\beta,x)=\sum_{n=1}^{N}\log\mathsf{Normal}(y_n \mid x_n \beta, \sigma)\]
\(\theta = (\beta, \sigma)\)の最尤推定値は次式のようになります.
\[(\hat{\beta},\hat{\sigma})=\mathrm{argmax}_{\beta,\sigma}\log p(y\mid\beta,\sigma,x) = \mathrm{argmax}_{\beta,\sigma}\sum_{n=1}^{N} \log \mathsf{Normal}(y_n \mid x_n \beta,\sigma)\]
(訳注: 原文では最右辺でargmaxが抜けている)
対数尤度関数について少し代数計算をすると, 周辺最尤推定値\(\hat{\theta}=(\hat{\beta},\hat{\sigma})\)が, 最小二乗法で求めた\(\hat{\beta}\)と等価に定式化できることが分かります. すなわち, \(\hat{\beta}\)は, 二乗予測誤差の和を最小化するような係数ベクトルの値です.
\[\hat{\beta}=\mathrm{argmin}_{\beta}\sum_{n=1}^{N}(y_{n}-x_{n}\beta)^2=\mathrm{argmin}_{\beta}(y-x\beta)^{\top}(y-x\beta)\]
\(n\)番目のデータの残差誤差は, 実際の値と予測値との差\(y_n - x_n \hat{\beta}\)です. ノイズのスケールの最尤推定値\(\hat{\sigma}\)は, 平均二乗残差の平方根とちょうど同じです.
\[\hat{\sigma}^2=\frac{1}{N}\sum_{n=1}^{N}\left(y_{n}-x_{n}\hat{\beta}\right)^2=\frac{1}{N}(y-x\hat{\beta})^{\top}(y-x\hat{\beta})\]
線形回帰において二乗誤差を最小にするアプローチはStanでは以下のモデルで直接コーディングできます.
data {
int<lower=0> N;
int<lower=1> K;
vector[N] y;
matrix[N,K] x;
}
parameters {
vector[K] beta;
}
transformed parameters {
real<lower=0> squared_error;
squared_error <- dot_self(y - x * beta);
}
model {
increment_log_prob(-squared_error);
}
generated quantities {
real<lower=0> sigma_squared;
sigma_squared <- squared_error / N;
}
このモデルをStanのオプティマイザで走らせると, 二乗誤差の和を直接最小化するとともに, その値を使って生成量としてノイズのスケールを定義することで, 線形回帰のMLEが生成されます.
sigma_squared
の定義にある分母のN
をN-1
に変えると, より一般的に用いられる\(\sigma^2\)の不偏推定量が計算できます. 推定の偏りの定義と, 分散の推定についての議論については30.6節を参照してください.
最適化を行なう能力に関する限り, 尤度関数については特別なことはありません. 非ベイズ統計では普通に行なわれますが, 対数尤度に「罰則」関数と呼ばれる関数を加えて, その新しく定義した関数を最適化する方法があります. 対数尤度関数\(\log p(y\mid\theta)\)と罰則関数\(r(\theta)\)からなる罰則付き最尤推定量は次式のように定義されます.
\[\hat{\theta}=\mathrm{argmax}_{\theta}\log p(y\mid\theta)-r(\theta)\]
最大化のとき, 推定値\(\hat{\theta}\)は, 対数尤度の最大化と罰則の最小化との間でつりあいをとるようになります. 罰則をつけることは「正則化」とも呼ばれます.
リッジ回帰(Hoerl and Kennard, 1970)の基礎は, 係数ベクトル\(\beta\)のユークリッド長さ(訳注: 二乗のこと)に罰則をつけるところにあります. 次式がリッジ罰則関数です.
\[r(\beta)=\lambda\sum_{k=1}^{K}\beta_{k}^2=\lambda\beta^{\top}\beta\]
ここで\(\lambda\)は, 罰則の大きさを決める固定したチューニングパラメータです.
したがって, リッジ回帰の罰則付き最尤推定値は次式のとおりです.
\[(\hat{\beta},\hat{\sigma})=\mathrm{argmax}_{\beta,\sigma}\sum_{n=1}^{N}\log\mathsf{Normal}(y_{n}\mid x_{n}\beta,\sigma)-\lambda\sum_{k=1}^{K}\beta_{k}^2\]
リッジ罰則は, L2ノルムとの関連から, L2正則化あるいは縮小と呼ばれることもあります.
基本的なMLEと同様, 係数\(\beta\)についてのリッジ回帰の推定値は最小二乗法として定式化することもできます.
\[\hat{\beta}=\mathrm{argmin}_{\beta}\sum_{n=1}^{N}(y_{n}-x_{n}\beta)^2+\sum_{k=1}^{K}\beta_{k}^2=\mathrm{argmin}_{\beta}(y-x\beta)^{\top}(y-x\beta)+\lambda\beta^{\top}\beta\]
リッジ罰則関数を加えると, \(\beta\)についてのリッジ回帰の推定値がより短いベクトルとなる効果があります. すなわち\(\hat{\beta}\)が縮小されます. リッジ推定値は必ずしもすべての\(k\)について\(\beta_k\)の絶対値が小さくなるとは限りませんし, 係数ベクトルが, 最尤推定値と同じ方向を指すとも限りません.
Stanでリッジ罰則を加えるには, 罰則の大きさをデータ変数として加え, 罰則自身をmodel
ブロックに加えます.
data {
// ...
real<lower=0> lambda;
}
// ...
model {
// ...
increment_log_prob(- lambda * dot_self(beta));
}
ノイズ項の計算はそのままです.
Lasso (Tibshirani, 1966)はリッジ回帰の代替法で, 係数の二乗和ではなく, 係数の絶対値和に基づいて罰則を適用します.
\[r(\beta)=\lambda\sum_{k=1}^{K}|\beta_{k}|\]
Lassoは, L1ノルムとの関連から, L1縮小とも呼ばれます. L1ノルムは, タクシー距離あるいはマンハッタン距離としても知られています.
罰則の導関数は\(\beta_k\)の値に依存しません.
\[\frac{d}{d\beta_{k}}\lambda\sum_{k=1}^{K}|\beta_{k}|=\mathrm{signum}(\beta_{k})\]
そのため, 縮小するパラメータは, 最尤推定値では完全に0になるという効果があります. したがって, 単に縮小だけではなく変数選択として使うこともできます. 2 Lassoも, 罰則の大きさをデータとして宣言し, 罰則をmodel
ブロックに加えることにより, リッジ回帰と同じくらい簡単にStanで実装できます.
data {
// ...
real<lower=0> lambda;
}
// ...
model {
// ...
for (k in 1:K)
increment_log_prob(- lambda * abs(beta[k]));
}
2 実際には, 勾配に基づくStanのオプティマイザでは完全に0の値となることが保証されません. 勾配降下法で完全に0の値を得るための議論は, Langfordら(2009)を参照してください.
ナイーブElastic Net (Zou and Hastie, 2005)は, リッジとLassoの罰則の重みづけ平均を取り入れています. 罰則関数は次式です.
\[r(\beta)=\lambda_{1}\sum_{k=1}^{K}|\beta_{k}|+\lambda_{2}\sum_{k=1}^{K}\beta_{k}^2\]
ナイーブElastic Netは, リッジ回帰とLassoの両方の特性を組み合わせたもので, 識別と変数選択の両方ができます.
ナイーブElastic Netは, Stanでは, リッジ回帰とLassoの実装を組み合わせることにより直接実装できます.
data {
real<lower=0> lambda1;
real<lower=0> lambda2;
// ...
}
// ...
model {
// ...
for (k in 1:K)
increment_log_prob(-lambda1 * fabs(beta[k]));
increment_log_prob(-lambda2 * dot_self(beta));
}
\(r(\beta)\)は罰則関数ですので, プログラム中では符号は負であることに注意してください.
Elastic Net (Zou and Hastie, 2005)は, ナイーブElastic Netで生成された当てはめ値\(\hat{\beta}\)から, 最終的な\(\beta\)の推定値を調整するようにしています. Elastic Netの推定値は次式です.
\[\hat{\beta}=(1+\lambda_{2})\beta^{\ast}\]
ここで, \(\beta^{\ast}\)は, ナイーブElastic Netの推定値です.
StanでElastic Netを実装するときも, data
, parameters
, model
の各ブロックはナイーブElastic Netと同じままです. それに加えて, Elastic Netの推定値をgenerated quantities
ブロックで計算します.
generated quantities {
vector[K] beta_elastic_net;
// ...
beta_elastic_net <- (1 + lambda2) * beta;
}
誤差のスケールも, Elastic Netの係数beta_elastic_net
からgenerated quantities
ブロックで計算する必要があります.
James and Stein (1961)のように, 係数の推定値を0ではない値に偏らせるような罰則関数も普通に使われます. 推定値を集団の平均に偏らせる罰則関数も使うことができます(Efron and Morris, 1975; Efron, 2012). 後者の手法は, ベイズ統計で普通に採用される階層モデルに似ています.
観測値\(y\)が与えられたときのパラメータ\(\theta\)の事後分布\(p(\theta \mid y)\)に基づいてベイズ点推定を行なうのに普通に使うやり方は3つあります. すなわち, 最頻値(最大値), 平均値, 中央値です. この節では, 事後分布を最大化するようなパラメータ\(\theta\)をもととする推定値について述べ, 続いて次の節では平均値と中央値について議論します.
モデルの事後最頻値に基づく推定値は次式のように定義できます.
\[\hat{\theta}=\mathrm{argmax}_{\theta}\,p(\theta\mid y)\]
存在するならば, \(\hat{\theta}\)は, 与えられたデータのもとでのパラメータの事後密度を最大化します. 事後最頻値は, 最大事後(maximum a posteriori, MAP)推定値とも呼ばれます.
24章と30.1節で議論したように, ただひとつの事後最頻値が存在するとは限りません. 事後最頻値を最大にするような値は, ひとつも存在しないこともありえますし, 2つ以上のこともありえます. そのような場合, 事後最頻値の推定値は定義されません. ほとんどのオプティマイザと同様に, Stanのオプティマイザでもそうした状況では問題が発生します. 大域的には最大ではないような, 局所最大値を返すこともありえます.
事後最頻値が存在する場合には, その値は, 対数事前分布に負号をつけたものに等しいような罰則関数を持つ罰則付き最尤推定値に対応するでしょう. これはベイズの定理から導かれます.
\[p(\theta\mid y)=\frac{p(y\mid\theta)p(\theta)}{p(y)}\]
これにより次式が保証されます.
\[\begin{array}{ll}\mathrm{argmax}_{\theta}\ p(\theta\mid y) &= \mathrm{argmax}_{\theta}\ \frac{p(y\mid\theta)p(\theta)}{p(y)}\\ &= \mathrm{argmax}_{\theta}\ p(y\mid\theta)p(\theta)\end{array}\]
密度は正値をとり, 対数が厳密に単調であることから次式が保証されます.
\[\mathrm{argmax}_{\theta}\ p(y\mid\theta)p(\theta) = \mathrm{argmax}_{\theta}\ \log p(y\mid\theta) + \log p(\theta)\]
事前分布(正則でも非正則でも)が一様である場合, 事後最頻値は最尤推定値と同じになります.
普通に使われる罰則関数ほとんどについて, 確率的に同じものが存在します. 例えば, リッジ罰則関数は係数への正規事前分布に対応しますし, Lassoはラプラス事前分布に対応します. この逆も常に真です. 対数事前分布に負号をつけたものは常に罰則関数と見なすことができます.
標準的なベイズ法では点推定には(あると仮定して)事後平均値が使われます. 定義は次式です.
\[\hat{\theta} = \int \theta p(\theta\mid y)d\theta\]
事後平均値はまさにベイズ推定量とよく呼ばれます. 推定値の期待二乗誤差を最小にする推定量だからです.
各パラメータの事後平均値の推定値は, Stanのインターフェイスから返されます. インターフェイスとデータフォーマットの詳細はRstan, CmdStan, PyStanのユーザーズガイドを参照してください.
事後最頻値が存在しない場合でも, 事後平均値が存在することは少なくありません. 例えば, \(\mathsf{Beta}(0.1, 0.1)\)の場合, 事後最頻値はありませんが, 事後平均値はきちんと定義されて, 値は0.5となります.
事後平均値が存在しないのに, 事後最頻値は存在するという状況のひとつは, 事後分布がコーシー分布\(\mathsf{Cauchy}(\mu,\tau)\)の場合です. 事後最頻値は\(\mu\)ですが, 事後平均値を表す積分は発散します. そのような幅の広い事後分布(訳注: 原文はpriorだがposteriorの誤り)は, 実際にモデリングを使うときにはめったに出てきません. パラメータにコーシー分布の事前分布を使うときでも, データより十分な制約が与えられるので, 事後分布は行儀が良くなり, 平均値も存在するようになります.
事後平均値が存在しても, 意味がないものであることもあります. 混合分布モデルで起きる多峰の事後分布の場合や, 閉区間での一様分布の場合がそれに当たります.
事後中央値(すなわち50番目の百分位点または0.5分位)は, ベイズモデルの報告によく使われる, もうひとつの点推定値です. 事後中央値は, 推定値の誤差の期待絶対値を最小化します. こうした推定値は, さまざまなStanのインターフェイスで返されます. フォーマットについてのさらに情報を得るにはRStan, PyStan, CmdStanのユーザーズガイドを参照してください.
事後中央値が意味のないものになることもありえますが, 事後平均値が存在しないようなときでも多くの場合, 事後中央値は存在します. コーシー分布もこれにあてはまります.
推定値\(\hat{\theta}\)は, 特定のデータ\(y\)のほか, 対数尤度関数\(\log p(y \mid \theta)\), 罰則付き尤度関数\(\log p(y \mid \theta) - r(\theta)\), 対数確率関数\(\log p(y,\theta) = \log p(y \mid \theta) + log p(\theta)\)(訳注: 原文は\(\log p(y, \theta) = \log p(y, \theta) + log p(\theta)\)だがこれは誤り)のうちのいずれかに依存します. この節では, \(\hat{\theta}\)という記法は推定量を示すものとしても定義します. このときの推定量とは, データと(罰則付き)尤度あるいは確率関数の非明示的な関数です.
真のパラメータ\(\theta\)にしたがって生成された特定の観測値のデータセット\(y\)について, パラメータの推定値と真の値との差が推定誤差です.
\[\mathrm{err}(\hat{\theta}) = \hat{\theta} - \theta\]
特定の真のパラメータの値を\(\theta\), 尤度関数を\(p(y \mid \theta)\)とすると, 推定量のバイアスとは推定誤差の期待値です.
\[\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}-\theta] = \mathbb{E}_{p(y\mid\theta)}[\hat{\theta}] - \theta\]
ここで\(\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}]\)は以下のように, 推定値\(\hat{\theta}\)を尤度関数\(p(y \mid \theta)\)で重みづけてデータセット\(y\)の取りうる範囲全体について積分したものです.
\[\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}] = \int \left(\mathrm{argmax}_{\theta'}p(y\mid\theta')\right)p(y\mid\theta)dy\]
バイアスは\(\theta\)と同じ次元の多変量です. この期待推定誤差が0であれば推定量は不偏ですが, そうでなければバイアスがあります.
正規分布から抽出された, \(n \in 1:N\)についての観測値\(y_n\)からなるデータセットがあるとします. これは, \(y_n \sim \mathsf{Normal}(\mu, \sigma)\)というモデルを仮定しています. ここで, \(\mu\)と\(\sigma>0\)はともにパラメータです. 尤度は次式のとおりです.
\[\log p(y\mid\mu,\sigma) = \sum_{n=1}^{N}\log\mathsf{Normal}(y_{n}\mid\mu,\sigma)\]
\(\mu\)の最尤推定量はちょうど標本平均, すなわち標本の平均値となります.
\[\hat{\mu} = \frac{1}{N}\sum_{n=1}^{N}y_{n}\]
この平均についての最尤推定量は不偏です.
分散\(\sigma^2\)の最尤推定量は, 平均との差の二乗の平均です.
\[\hat{\sigma}^2 = \frac{1}{N}\sum_{n=1}{N}(y_{n} - \hat{\mu})^2\]
分散の最尤値は小さい方に偏っています.
\[\mathbb{E}_{p(y\mid\mu,\sigma)}[\hat{\sigma}^2] < \sigma\]
最尤推定値が, 平均値の推定値\(\hat{\mu}\)との差に基づいていることがこの偏りの理由です. 実際の平均値をいれてみると, 差の二乗和が大きくなります. すなわち, \(\mu \neq \hat{\mu}\)ならば, 次式のようになります.
\[\frac{1}{N}\sum_{n=1}^{N}(y_{n}-\mu)^2 > \frac{1}{N}\sum_{n=1}^{N}(y_{n}-\hat{\mu})^2\]
分散の推定値はもうひとつあり, それが不偏分散(訳注: 原文はsample varianceですが, これは誤り)です. 次式のように定義されます.
\[\hat{\sigma}^2 = \frac{1}{N-1}\sum_{n=1}^{N}(y_{n}-\hat{\mu})^2\]
(訳注: 原文は\(\hat{\mu}\)ですが, これは誤り)
この値は, 最尤推定値よりも\(N/(N-1)\)倍だけ大きくなります.
推定量\(\hat{\theta}\)の成分\(k\)の分散も他の分散と同じように計算されます. 期待値との差の二乗の期待値です.
\[\mathrm{var}_{p(y\mid\theta)}[\hat{\theta}_{k}] = \mathbb{E}_{p(y\mid\theta)}[(\hat{\theta}_{k} - \mathbb{E}_{p(y\mid\theta)}[\hat{\theta}_{k}])^{2}]\]
推定量の\(K \times K\)共分散行列全体も, いつもどおり以下のように定義されます.
\[\mathrm{covar}_{p(y\mid\theta)}[\hat{\theta}] = \mathbb{E}_{p(y\mid\theta)}[(\hat{\theta} - \mathbb{E}[\hat{\theta}])(\hat{\theta}-\mathbb{E}[\hat{\theta}])^{\top}]\]
標本データから正規分布の平均と分散を推定する例での計算では, 最尤推定量(すなわち標本平均)は, 分散を最小とするような平均\(\mu\)の不偏推定量です. そのことは, 正規ノイズの仮定のもとで最小二乗推定について, また等価ですが, 最尤推定について, ガウス-マルコフの定理によって一定の一般性をもって証明されました. Hastieら(2009)の3.2.2節を参照してください.
この章では, ベイズ推定ではありませんが, 最尤推定と罰則付き最尤推定というよく使われる方法を定義し, 事後分布の平均値・中央値・最頻値を使って, それらをベイズ点推定に関連づけます. 最尤推定値は, 事後分布ではなく, モデルのパラメーター\(\theta\)についての単一の値から求められるので, 「点推定値」と呼ばれます.
どのような尤度関数も罰則関数もStanのモデリング言語でコーディングすることができるので, Stanのオプティマイザ(optimizer)を使って(罰則付き)最尤推定を実装することができます. Stanのオプティマイザはまた, 事後最頻値に基づくベイズの枠組みでの点推定にも使うことができます. Stanのマルコフ連鎖モンテカルロサンプラーは, 事後分布の平均値や中央値に基づくベイズモデルでの点推定を実装するのにも使えます.
尤度関数\(p(y\mid\theta)\)と, 固定されたデータのベクトル\(y\)とが与えられたとき, 尤度を最大にするようなパラメータのベクトル\(\hat{\theta}\)のことを最尤推定値(maximum likelihood estimate, MLE)といいます.
\[\hat{\theta}=\mathrm{argmax}_{\theta}\ p(y\mid\theta)\]
通常は対数スケールにするほうが便利です. 次式も, MLEの定式化として等価です. 1
\[\hat{\theta}=\mathrm{argmax}_{\theta}\ \log p(y\mid\theta)\]
1 この等価性は, 密度が正値であり, 対数関数が厳密に単調であるという事実から導かれます. すなわち, \(p(y\mid\theta) \ge 0\), かつ, すべての\(a,b>0\)について, \(a>b\)のときのみ\(\log a > \log b\)です.
すべての関数がただひとつの最大値を持っているとは限りませんので, 最尤推定値が存在することは保証されるものではありません. 24章で議論したように, この状況は以下のようなときに起こります.
こうした問題は, 次の節で議論する罰則付き最尤推定でも, その後の節で議論するベイズ事後最頻値でもついて回ります.
通常の線形回帰の問題を考えます. 観測値\(y\)が\(N\)次元のベクトル, 予測変数が\((N \times K)\)次元のデータ行列\(x\), 回帰係数が\(K\)次元のパラメータのベクトル\(\beta\), 実数値のノイズのスケールが\(\sigma > 0\)のとき, 尤度関数は次式のようになります.
\[\log p(y\mid\beta,x)=\sum_{n=1}^{N}\log\mathsf{Normal}(y_n \mid x_n \beta, \sigma)\]
\(\theta = (\beta, \sigma)\)の最尤推定値は次式のようになります.
\[(\hat{\beta},\hat{\sigma})=\mathrm{argmax}_{\beta,\sigma}\log p(y\mid\beta,\sigma,x) = \mathrm{argmax}_{\beta,\sigma}\sum_{n=1}^{N} \log \mathsf{Normal}(y_n \mid x_n \beta,\sigma)\]
(訳注: 原文では最右辺でargmaxが抜けている)
対数尤度関数について少し代数計算をすると, 周辺最尤推定値\(\hat{\theta}=(\hat{\beta},\hat{\sigma})\)が, 最小二乗法で求めた\(\hat{\beta}\)と等価に定式化できることが分かります. すなわち, \(\hat{\beta}\)は, 二乗予測誤差の和を最小化するような係数ベクトルの値です.
\[\hat{\beta}=\mathrm{argmin}_{\beta}\sum_{n=1}^{N}(y_{n}-x_{n}\beta)^2=\mathrm{argmin}_{\beta}(y-x\beta)^{\top}(y-x\beta)\]
\(n\)番目のデータの残差誤差は, 実際の値と予測値との差\(y_n - x_n \hat{\beta}\)です. ノイズのスケールの最尤推定値\(\hat{\sigma}\)は, 平均二乗残差の平方根とちょうど同じです.
\[\hat{\sigma}^2=\frac{1}{N}\sum_{n=1}^{N}\left(y_{n}-x_{n}\hat{\beta}\right)^2=\frac{1}{N}(y-x\hat{\beta})^{\top}(y-x\hat{\beta})\]
線形回帰において二乗誤差を最小にするアプローチはStanでは以下のモデルで直接コーディングできます.
data {
int<lower=0> N;
int<lower=1> K;
vector[N] y;
matrix[N,K] x;
}
parameters {
vector[K] beta;
}
transformed parameters {
real<lower=0> squared_error;
squared_error <- dot_self(y - x * beta);
}
model {
increment_log_prob(-squared_error);
}
generated quantities {
real<lower=0> sigma_squared;
sigma_squared <- squared_error / N;
}
このモデルをStanのオプティマイザで走らせると, 二乗誤差の和を直接最小化するとともに, その値を使って生成量としてノイズのスケールを定義することで, 線形回帰のMLEが生成されます.
sigma_squared
の定義にある分母のN
をN-1
に変えると, より一般的に用いられる\(\sigma^2\)の不偏推定量が計算できます. 推定の偏りの定義と, 分散の推定についての議論については31.6節を参照してください.
最適化を行なう能力に関する限り, 尤度関数については特別なことはありません. 非ベイズ統計では普通に行なわれますが, 対数尤度に「罰則」関数と呼ばれる関数を加えて, その新しく定義した関数を最適化する方法があります. 対数尤度関数\(\log p(y\mid\theta)\)と罰則関数\(r(\theta)\)からなる罰則付き最尤推定量は次式のように定義されます.
\[\hat{\theta}=\mathrm{argmax}_{\theta}\log p(y\mid\theta)-r(\theta)\]
最大化のとき, 推定値\(\hat{\theta}\)は, 対数尤度の最大化と罰則の最小化との間でつりあいをとるようになります. 罰則をつけることは「正則化」とも呼ばれます.
リッジ回帰(Hoerl and Kennard, 1970)の基礎は, 係数ベクトル\(\beta\)のユークリッド長さ(訳注: 二乗のこと)に罰則をつけるところにあります. 次式がリッジ罰則関数です.
\[r(\beta)=\lambda\sum_{k=1}^{K}\beta_{k}^2=\lambda\beta^{\top}\beta\]
ここで\(\lambda\)は, 罰則の大きさを決める固定したチューニングパラメータです.
したがって, リッジ回帰の罰則付き最尤推定値は次式のとおりです.
\[(\hat{\beta},\hat{\sigma})=\mathrm{argmax}_{\beta,\sigma}\sum_{n=1}^{N}\log\mathsf{Normal}(y_{n}\mid x_{n}\beta,\sigma)-\lambda\sum_{k=1}^{K}\beta_{k}^2\]
リッジ罰則は, L2ノルムとの関連から, L2正則化あるいは縮小と呼ばれることもあります.
基本的なMLEと同様, 係数\(\beta\)についてのリッジ回帰の推定値は最小二乗法として定式化することもできます.
\[\hat{\beta}=\mathrm{argmin}_{\beta}\sum_{n=1}^{N}(y_{n}-x_{n}\beta)^2+\sum_{k=1}^{K}\beta_{k}^2=\mathrm{argmin}_{\beta}(y-x\beta)^{\top}(y-x\beta)+\lambda\beta^{\top}\beta\]
リッジ罰則関数を加えると, \(\beta\)についてのリッジ回帰の推定値がより短いベクトルとなる効果があります. すなわち\(\hat{\beta}\)が縮小されます. リッジ推定値は必ずしもすべての\(k\)について\(\beta_k\)の絶対値が小さくなるとは限りませんし, 係数ベクトルが, 最尤推定値と同じ方向を指すとも限りません.
Stanでリッジ罰則を加えるには, 罰則の大きさをデータ変数として加え, 罰則自身をmodel
ブロックに加えます.
data {
// ...
real<lower=0> lambda;
}
// ...
model {
// ...
increment_log_prob(- lambda * dot_self(beta));
}
ノイズ項の計算はそのままです.
Lasso (Tibshirani, 1966)はリッジ回帰の代替法で, 係数の二乗和ではなく, 係数の絶対値和に基づいて罰則を適用します.
\[r(\beta)=\lambda\sum_{k=1}^{K}|\beta_{k}|\]
Lassoは, L1ノルムとの関連から, L1縮小とも呼ばれます. L1ノルムは, タクシー距離あるいはマンハッタン距離としても知られています.
罰則の導関数は\(\beta_k\)の値に依存しません.
\[\frac{d}{d\beta_{k}}\lambda\sum_{k=1}^{K}|\beta_{k}|=\mathrm{signum}(\beta_{k})\]
そのため, 縮小するパラメータは, 最尤推定値では完全に0になるという効果があります. したがって, 単に縮小だけではなく変数選択として使うこともできます. 2 Lassoも, 罰則の大きさをデータとして宣言し, 罰則をmodel
ブロックに加えることにより, リッジ回帰と同じくらい簡単にStanで実装できます.
data {
// ...
real<lower=0> lambda;
}
// ...
model {
// ...
for (k in 1:K)
increment_log_prob(- lambda * abs(beta[k]));
}
2 実際には, 勾配に基づくStanのオプティマイザでは完全に0の値となることが保証されません. 勾配降下法で完全に0の値を得るための議論は, Langfordら(2009)を参照してください.
ナイーブElastic Net (Zou and Hastie, 2005)は, リッジとLassoの罰則の重みづけ平均を取り入れています. 罰則関数は次式です.
\[r(\beta)=\lambda_{1}\sum_{k=1}^{K}|\beta_{k}|+\lambda_{2}\sum_{k=1}^{K}\beta_{k}^2\]
ナイーブElastic Netは, リッジ回帰とLassoの両方の特性を組み合わせたもので, 識別と変数選択の両方ができます.
ナイーブElastic Netは, Stanでは, リッジ回帰とLassoの実装を組み合わせることにより直接実装できます.
data {
real<lower=0> lambda1;
real<lower=0> lambda2;
// ...
}
// ...
model {
// ...
for (k in 1:K)
increment_log_prob(-lambda1 * fabs(beta[k]));
increment_log_prob(-lambda2 * dot_self(beta));
}
\(r(\beta)\)は罰則関数ですので, プログラム中では符号は負であることに注意してください.
Elastic Net (Zou and Hastie, 2005)は, ナイーブElastic Netで生成された当てはめ値\(\hat{\beta}\)から, 最終的な\(\beta\)の推定値を調整するようにしています. Elastic Netの推定値は次式です.
\[\hat{\beta}=(1+\lambda_{2})\beta^{\ast}\]
ここで, \(\beta^{\ast}\)は, ナイーブElastic Netの推定値です.
StanでElastic Netを実装するときも, data
, parameters
, model
の各ブロックはナイーブElastic Netと同じままです. それに加えて, Elastic Netの推定値をgenerated quantities
ブロックで計算します.
generated quantities {
vector[K] beta_elastic_net;
// ...
beta_elastic_net <- (1 + lambda2) * beta;
}
誤差のスケールも, Elastic Netの係数beta_elastic_net
からgenerated quantities
ブロックで計算する必要があります.
James and Stein (1961)のように, 係数の推定値を0ではない値に偏らせるような罰則関数も普通に使われます. 推定値を集団の平均に偏らせる罰則関数も使うことができます(Efron and Morris, 1975; Efron, 2012). 後者の手法は, ベイズ統計で普通に採用される階層モデルに似ています.
観測値\(y\)が与えられたときのパラメータ\(\theta\)の事後分布\(p(\theta \mid y)\)に基づいてベイズ点推定を行なうのに普通に使うやり方は3つあります. すなわち, 最頻値(最大値), 平均値, 中央値です. この節では, 事後分布を最大化するようなパラメータ\(\theta\)をもととする推定値について述べ, 続いて次の節では平均値と中央値について議論します.
モデルの事後最頻値に基づく推定値は次式のように定義できます.
\[\hat{\theta}=\mathrm{argmax}_{\theta}\,p(\theta\mid y)\]
存在するならば, \(\hat{\theta}\)は, 与えられたデータのもとでのパラメータの事後密度を最大化します. 事後最頻値は, 最大事後(maximum a posteriori, MAP)推定値とも呼ばれます.
24章と31.1節で議論したように, ただひとつの事後最頻値が存在するとは限りません. 事後最頻値を最大にするような値は, ひとつも存在しないこともありえますし, 2つ以上のこともありえます. そのような場合, 事後最頻値の推定値は定義されません. ほとんどのオプティマイザと同様に, Stanのオプティマイザでもそうした状況では問題が発生します. 大域的には最大ではないような, 局所最大値を返すこともありえます.
事後最頻値が存在する場合には, その値は, 対数事前分布に負号をつけたものに等しいような罰則関数を持つ罰則付き最尤推定値に対応するでしょう. これはベイズの定理から導かれます.
\[p(\theta\mid y)=\frac{p(y\mid\theta)p(\theta)}{p(y)}\]
これにより次式が保証されます.
\[\begin{array}{ll}\mathrm{argmax}_{\theta}\ p(\theta\mid y) &= \mathrm{argmax}_{\theta}\ \frac{p(y\mid\theta)p(\theta)}{p(y)}\\ &= \mathrm{argmax}_{\theta}\ p(y\mid\theta)p(\theta)\end{array}\]
密度は正値をとり, 対数が厳密に単調であることから次式が保証されます.
\[\mathrm{argmax}_{\theta}\ p(y\mid\theta)p(\theta) = \mathrm{argmax}_{\theta}\ \log p(y\mid\theta) + \log p(\theta)\]
事前分布(正則でも非正則でも)が一様である場合, 事後最頻値は最尤推定値と同じになります.
普通に使われる罰則関数ほとんどについて, 確率的に同じものが存在します. 例えば, リッジ罰則関数は係数への正規事前分布に対応しますし, Lassoはラプラス事前分布に対応します. この逆も常に真です. 対数事前分布に負号をつけたものは常に罰則関数と見なすことができます.
標準的なベイズ法では点推定には(あると仮定して)事後平均値が使われます. 定義は次式です.
\[\hat{\theta} = \int \theta p(\theta\mid y)d\theta\]
事後平均値はまさにベイズ推定量とよく呼ばれます. 推定値の期待二乗誤差を最小にする推定量だからです.
各パラメータの事後平均値の推定値は, Stanのインターフェイスから返されます. インターフェイスとデータフォーマットの詳細はRstan, CmdStan, PyStanのユーザーズガイドを参照してください.
事後最頻値が存在しない場合でも, 事後平均値が存在することは少なくありません. 例えば, \(\mathsf{Beta}(0.1, 0.1)\)の場合, 事後最頻値はありませんが, 事後平均値はきちんと定義されて, 値は0.5となります.
事後平均値が存在しないのに, 事後最頻値は存在するという状況のひとつは, 事後分布がコーシー分布\(\mathsf{Cauchy}(\mu,\tau)\)の場合です. 事後最頻値は\(\mu\)ですが, 事後平均値を表す積分は発散します. そのような幅の広い事後分布(訳注: 原文はpriorだがposteriorの誤り)は, 実際にモデリングを使うときにはめったに出てきません. パラメータにコーシー分布の事前分布を使うときでも, データより十分な制約が与えられるので, 事後分布は行儀が良くなり, 平均値も存在するようになります.
事後平均値が存在しても, 意味がないものであることもあります. 混合分布モデルで起きる多峰の事後分布の場合や, 閉区間での一様分布の場合がそれに当たります.
事後中央値(すなわち50番目の百分位点または0.5分位)は, ベイズモデルの報告によく使われる, もうひとつの点推定値です. 事後中央値は, 推定値の誤差の期待絶対値を最小化します. こうした推定値は, さまざまなStanのインターフェイスで返されます. フォーマットについてのさらに情報を得るにはRStan, PyStan, CmdStanのユーザーズガイドを参照してください.
事後中央値が意味のないものになることもありえますが, 事後平均値が存在しないようなときでも多くの場合, 事後中央値は存在します. コーシー分布もこれにあてはまります.
推定値\(\hat{\theta}\)は, 特定のデータ\(y\)のほか, 対数尤度関数\(\log p(y \mid \theta)\), 罰則付き尤度関数\(\log p(y \mid \theta) - r(\theta)\), 対数確率関数\(\log p(y,\theta) = \log p(y \mid \theta) + log p(\theta)\)(訳注: 原文は\(\log p(y, \theta) = \log p(y, \theta) + log p(\theta)\)だがこれは誤り)のうちのいずれかに依存します. この節では, \(\hat{\theta}\)という記法は推定量を示すものとしても定義します. このときの推定量とは, データと(罰則付き)尤度あるいは確率関数の非明示的な関数です.
真のパラメータ\(\theta\)にしたがって生成された特定の観測値のデータセット\(y\)について, パラメータの推定値と真の値との差が推定誤差です.
\[\mathrm{err}(\hat{\theta}) = \hat{\theta} - \theta\]
特定の真のパラメータの値を\(\theta\), 尤度関数を\(p(y \mid \theta)\)とすると, 推定量のバイアスとは推定誤差の期待値です.
\[\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}-\theta] = \mathbb{E}_{p(y\mid\theta)}[\hat{\theta}] - \theta\]
ここで\(\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}]\)は以下のように, 推定値\(\hat{\theta}\)を尤度関数\(p(y \mid \theta)\)で重みづけてデータセット\(y\)の取りうる範囲全体について積分したものです.
\[\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}] = \int \left(\mathrm{argmax}_{\theta'}p(y\mid\theta')\right)p(y\mid\theta)dy\]
バイアスは\(\theta\)と同じ次元の多変量です. この期待推定誤差が0であれば推定量は不偏ですが, そうでなければバイアスがあります.
正規分布から抽出された, \(n \in 1:N\)についての観測値\(y_n\)からなるデータセットがあるとします. これは, \(y_n \sim \mathsf{Normal}(\mu, \sigma)\)というモデルを仮定しています. ここで, \(\mu\)と\(\sigma>0\)はともにパラメータです. 尤度は次式のとおりです.
\[\log p(y\mid\mu,\sigma) = \sum_{n=1}^{N}\log\mathsf{Normal}(y_{n}\mid\mu,\sigma)\]
\(\mu\)の最尤推定量はちょうど標本平均, すなわち標本の平均値となります.
\[\hat{\mu} = \frac{1}{N}\sum_{n=1}^{N}y_{n}\]
この平均についての最尤推定量は不偏です.
分散\(\sigma^2\)の最尤推定量は, 平均との差の二乗の平均です.
\[\hat{\sigma}^2 = \frac{1}{N}\sum_{n=1}{N}(y_{n} - \hat{\mu})^2\]
分散の最尤値は小さい方に偏っています.
\[\mathbb{E}_{p(y\mid\mu,\sigma)}[\hat{\sigma}^2] < \sigma\]
最尤推定値が, 平均値の推定値\(\hat{\mu}\)との差に基づいていることがこの偏りの理由です. 実際の平均値をいれてみると, 差の二乗和が大きくなります. すなわち, \(\mu \neq \hat{\mu}\)ならば, 次式のようになります.
\[\frac{1}{N}\sum_{n=1}^{N}(y_{n}-\mu)^2 > \frac{1}{N}\sum_{n=1}^{N}(y_{n}-\hat{\mu})^2\]
分散の推定値はもうひとつあり, それが不偏分散(訳注: 原文はsample varianceですが, これは誤り)です. 次式のように定義されます.
\[\hat{\sigma}^2 = \frac{1}{N-1}\sum_{n=1}^{N}(y_{n}-\hat{\mu})^2\]
(訳注: 原文は\(\hat{\mu}\)ですが, これは誤り)
この値は, 最尤推定値よりも\(N/(N-1)\)倍だけ大きくなります.
推定量\(\hat{\theta}\)の成分\(k\)の分散も他の分散と同じように計算されます. 期待値との差の二乗の期待値です.
\[\mathrm{var}_{p(y\mid\theta)}[\hat{\theta}_{k}] = \mathbb{E}_{p(y\mid\theta)}[(\hat{\theta}_{k} - \mathbb{E}_{p(y\mid\theta)}[\hat{\theta}_{k}])^{2}]\]
推定量の\(K \times K\)共分散行列全体も, いつもどおり以下のように定義されます.
\[\mathrm{covar}_{p(y\mid\theta)}[\hat{\theta}] = \mathbb{E}_{p(y\mid\theta)}[(\hat{\theta} - \mathbb{E}[\hat{\theta}])(\hat{\theta}-\mathbb{E}[\hat{\theta}])^{\top}]\]
標本データから正規分布の平均と分散を推定する例での計算では, 最尤推定量(すなわち標本平均)は, 分散を最小とするような平均\(\mu\)の不偏推定量です. そのことは, 正規ノイズの仮定のもとで最小二乗推定について, また等価ですが, 最尤推定について, ガウス-マルコフの定理によって一定の一般性をもって証明されました. Hastieら(2009)の3.2.2節を参照してください.
ここではStanで実装されているアルゴリズムの実装の詳細とそれをどのように設定するかを説明します. この章ではハミルトニアンモンテカルロ(Hamiltonian Monte Carlo, HMC)アルゴリズムとその適応的な変種であるno-U-turn sampler(NUTS)をStanでの実装と構成に基づいて説明します. 次の2つの章ではStanのオプティマイザとdiagnostics(診断の方法)について説明します.
ハミルトニアンモンテカルロ(HMC)とは事後分布間の効率的な遷移を生成するためのサンプリングとして密度関数の導関数を使うようなマルコフ連鎖モンテカルロ法(Markov chain Monte Carlo, MCMC)です(より詳細はBetancourt and Girolami, 2013; Neal, 2011).
この手法はメトロポリス法の受理ステップを行うことで補正された数値積分に基づいて近似されたハミルトン力学系のシミュレーションを用いています. このセクションはBetancourt and Girolami (2013)によるHMCのプレゼンテーションをGelman et al. (2013)の記法に直したものです.
サンプリングの最終目的はパラメータθに対する密度p(θ)から値を抽出することです. これは典型的には与えられたデータyに対するベイズ事後分布p(θ|y), 特にStanプログラムで書かれたベイズ事後分布になります.
HMCでは補助運動量変数ρが導入され, 結合密度
\[ p(\rho,\theta)=p(\rho | \theta)p(\theta0) \]
から値が抽出されます. Stanを含むHMCのほとんどの応用では補助分布としてはパラメータθに依存しない多変量正規分布
\[ \rho \sim MultiNormal(0,\sigma) \]
を用います. 共分散行列Σは目標とする分布の回転とスケール変換のためのユークリッド計量として振舞います(幾何学の詳細についてはBetancourt and Stein, 2011を参照). Stanではこの行列はidentity matrix(単位行列)またはwarmup期間のサンプルから推測され, 制限された対角行列になります. 逆行列\[ Σ{-} \]は質量行列(mass matrix)という名前で知られ, Σが単位, 対角, 密行列のいずれかであればそれと同じ性質を持ちます.
結合密度p(ρ,θ)はハミルトニアン
で定義され, 項T
は"運動エネルギー",項V
は"ポテンシャルエネルギー"と呼ばれます. このポテンシャルエネルギーはStanでは対数密度の定義から求まります.
パラメータθの現在の値から新しい状態への遷移はメトロポリス法の受理ステップとその前の2つの手順で生成されます.
最初に運動量の値が現在のパラメータの値とは独立に分布
\[ \rho \sim MultiNormal0(0,\sigma) \]
から抽出されます. 運動量はiterationのたびに新しい値になり以前の値に影響されません.
次に現在のパラメータの値θと新しい運動量ρを結合した系(θ, ρ)をハミルトン方程式
にしたがって発展させます. 運動量密度と目標の密度関数は独立, つまりp(ρ|θ)=p(ρ)なので運動量の時間微分のはじめの項∂T/∂θ=0になります. よって時間微分の式は
\[ \dot{\theta},\dot{\rho} \]
となります.
このセクションの最後に2状態の微分方程式を解く作業が残されています. Stanは他のほとんどのHMCの実装と同様に蛙飛び積分(Leapfrog Integrator)を使っています. これはハミルトン方程式の結果を安定させるために調整された数値積分アルゴリズムです.
ほとんどの数値積分と同じ様に蛙飛びアルゴリズムは小さな時間間隔εの離散的なステップごとに行われます.
蛙飛びアルゴリズムは新しい運動量の項をパラメータθとは独立に抽出するか前の運動量の値
\[ \rho \]
から始め, ステップの半分だけの運動量の更新と1ステップ分の位置の更新を交互に行います.
\[ Leapfrog of \rho,\theta \]
L回の蛙飛びステップでLεだけの時間の動きがシミュレートされます. この結果(上記の3つのステップのL回繰り返し)で得られる状態を(ρ,θ)と書きます.
蛙飛び積分の誤差は1ステップあたりの時間間隔(step size)であるεの3乗, 大域的にはεの2乗のオーダーになります. Leimkuhler and Reich(2004)には蛙飛び積分の誤差の範囲の導出を含めたハミルトン系の数値積分の詳細な解析が紹介されています.
蛙飛び積分が完全に数値的であれば運動量ベクトルをランダムに生成するのとは別に遷移ごとにランダム化を施す必要はなくなります. しかし実際には積分による数値誤差をメトロポリス受理ステップを適用する際には考慮する必要があります. (ρ,θ)からの遷移で生成された提案値(ρ,θ)が受理される確率は
\[ min(1,\exp(H(\rho,\theta)-H(\rho^*,\theta^*))) \]
であり, もし提案分布が受理されなければ前のパラメータの値が次の値としてサンプルされ, 次のiterationにも使われます.
ハミルトニアンモンテカルロアルゴリズムは初期設定されたパラメータの組θからはじめられます. Stanではこの値はユーザーが設定することもランダムに生成することも出来ます. そして指定された数だけ新しい運動量のサンプリングとパラメータθの現在の値が 時間間隔εで離散化された蛙飛び積分がL回実行が繰り替えされることで更新されます. その後メトロポリス受理ステップが適用され, 提案された状態値(ρ,θ)が使われるか元の状態値のままにするかが決定されます.
ハミルトンモンテカルロ法は以下の3つのパラメータを設定しなければいけません.
実際, サンプリングの効率, 1回のサンプリングとその繰り返しの速度はこの3つのパラメータに大きく依存します(Neal, 2011;Hoffman and Gelman, 2014).
もしεが大きすぎれば蛙飛び積分は正確なものではなくなりメトロポリス受理ステップにおける提案の多くが棄却されるでしょう. もしεが小さすぎれば蛙飛び積分のシミュレーション時間は長くなってしまうでしょう. これらの困難な点をバランスさせるような受理率を決めることが目標になります.
もしLが小さすぎれば1回の繰り返しあたりのハミルトン方程式の積分軌道は短すぎサンプリングはランダムウォークによって移り変わって行ってしまうことでしょう. Lが大きすぎれば1回の繰り返しに対するアルゴリズムの負荷は大きくなってしまいます.
質量行列Σが事後分布の共分散に合致していない場合, 計算の正確性を確保するためにステップサイズεは小さくしなければなりませんし, 一方同時に統計的な効率性を保つために必要なステップ数Lは大きくなければなりません.
実際に積分される時間間隔はLεでステップ数の関数です. いくつかのStanのインターフェースは積分時間tと離散化の間隔(step size)εの近似値を設定します. そのような場合ステップ数は \[ L= \frac{t}{\epsilon} \] と丸められ実際の積分時間はLεとなるでしょう.
図58.1 warmup中の適応は3段階に分けて行われる: 期間(I) 最初の速いステップ, 期間(II) ゆっくりした適応の展開列, 期間(III) 最後の速い適応. HMCでは速い期間, ゆっくりした期間の双方がステップサイズを調整する為に使われていて, ゆっくりした期間は計量に必要な(共)分散の学習の為に使われる. iteration回数は左端が1で図の右に行くに従って増えていく.
Stanはno-U-turn sampling(NUTS)アルゴリズムを使うことで目標とする受理率になるように自動的にεを最適化すること, warmupでのサンプリングのiterationに基づいてΣを推定すること, サンプリング(とwarmup)中に動的にLを調整することができます(Hoffman and Gelman, 2014). .
パラメータの適応を行う場合には (step size, 質量行列を調整することで適応をoffにすることもできます)warmupは図58.1のように3つの期間(interval), 1つのゆっくりと成長する期間の連なりとそれをそれを挟む2つの速い期間とに分けて行われます. ここで速い期間, 遅い期間とはパラメータの適応に局所的な情報か大域的な情報を使うかの違いです. ハミルトニアンモンテカルロサンプラーでは, 例えばstep sizeを速いパラメータ, (共)分散を遅いパラメータとして定義しています. 最初と最後の速い期間のサイズと最初の遅い期間のサイズはカスタマイズすることができ, ユーザーの設定した値はwarpup期間中にalignmentを満たすために少し変更されることがあります.
warmup期間を分割する動機は適応をより安定したものにすることで, 各段階は以下のようになります.
最初の速い間隔ではチェーンは局所的な情報からのみ学習できるパラメータで典型的集合(typical set) に収束することが許されています. ((典型的集合(typical set)は情報理論で使われている概念を借用したものでここでは平衡状態でマルコフ連鎖(Markov chain)が遷移しているような 実体のある事後確率質量の近傍(または多変量モデルにおける近傍)です. ))
最後に遅いパラメータの最終的な更新の後に速いパラメータの適応が行われます.
これらの期間は以下のような調整パラメータにのっとって制御されます. 各パラメータは正の整数である必要があります.
パラメータ | 説明 | デフォルト値 |
---|---|---|
初期バッファ | 最初の速い期間の長さ | 25 |
期間バッファ | 最後の速い期間の長さ | 50 |
窓 | 最初の遅い適応期間の長さ | 25 |
StanのHMCアルゴリズムはstep sizeを最適化するのに双平均化法(dual averaging, Nesterov, 2009)を使っています. このwarmupの手続きはとても柔軟で完全なもので, StanではHoff- man and Gelman (2014)の記法を用いて, オプションとして指定できるようにそのインターフェイスをユーザに提供しています. 実際最適化の有効性はこのパラメータの値に対して敏感に変化します. しかし我々は双平均化法を使った経験があるのではない限りパラメータの値をデフォルト値から動かすことは推奨しません. より詳細な情報は(Hoffman and Gelman, 2011, 2014)の双平均化法に関する議論を参照して下さい.
双平均法の全てのパラメータを以下に示します.
パラメータ | 説明 | 拘束条件 | デフォルト値 |
---|---|---|---|
δ | 目標とするメトロポリス受理率 | δ∈[0,1] | 0.80 |
γ | 適応正規化スケール | γ > 0 | 0.05 |
κ | 適応緩和指数 | κ > 0 | 0.75 |
t_0 | 適応のイテレーションのオフセット | t_0> 0 | 10 |
目標とする受理率δ(1より小さい値に設定する必要があり, デフォルト値は0.8)を1に近い値に設定することで適応は小さなstep sizeを用いて行われることに成ります. これはイテレーションの増加のコストに対するサンプリングの効率(イテレーションあたりの有効なサンプリング)を高めます. δの値を上げることでいくつかのモデルではそれ以外の方法では行き詰まってしまうような場合にその行き詰まり(blockage)に打ち勝つことができます.
数値積分を使った全てのHMCの実装はstep size(時間間隔の離散化)を必要とします. Stanはstep sizeを適応的に決める方法, 明示的に決める方法の両方に対応しています. またStanでは曲率が大きな場合に固定したstep sizeと合わさって悪影響がでるのを避けるためにサンプリングごとランダムなゆらぎ(jitter)を持たせたstep sizeを使うことができます.
ゆらぎの値はもとのstep sizeの値に足される, あるいはもとの値から引かれることになります. それなのでとりうるゆらぎの最大値は1となり, この場合step sizeは0から適応値の2倍までの範囲になります.
小さなstep sizeを設定することでより大きな値ではHMCサンプラーが行き詰まってしまうような場合((訳注:受理率が低い?))にも進むようにできます. 良くない点として値が適応値を下回って揺らいでいる場合必要な蛙飛びのステップ回数が多くなるのでイテレーションを遅くし, 値が適応値を上回って揺らいでいる場合はハミルトン力学系の計算のシミュレーションエラーによる早すぎる棄却を引き起こすということがあります. step sizeのゆらぎに関するより詳細な議論は(Neal, 2011)を参照してください.
Stanの中の全てのHMCの実装では質量行列(mass matrix), より形式的には計量(metric)と呼ばれる対称で正定値の行列の中から選ばれた2次の運動エネルギー関数として使われています. もし計量が定数であればその実装はユークリッドHMCと呼ばれます. Stanでは以下の3つのユークリッドHMCの実装が使えます.
+単位計量(対角要素が1の対角行列) +対角計量(対角要素が正の値の対角行列) +密な計量(密な正定値対称行列)
ユーザーは計量の形を設定することができます.
もし質量行列が対角に設定されていれば, 正規化された分散((訳注: 共分散?))は1イテレーションの遅い期間(図58.1のIIの期間)毎に推定され, それぞれの推定値はその期間内のイテレーションによってのみ決まります. これはwarmupで指針として使われる早い段階での推定に使われますが, 後には忘れされるので最終的な共分散の値の推定には影響しません.
もし質量行列が密に設定されれば正規化された共分散の推定が行われ, それ自体は単位行列に正規化されるような対角行列に正規化されます.
分散または共分散は多くの浮動小数点演算の繰り返しによる精度の喪失を避けるためにWelford累積を用いて推定されます.
質量行列は事後分布の線形(ここでは大域的と同義になる)な相関を補正し, いくつかの問題ではHMCのパフォーマンスを劇的に改善します. これには大域的な相関を知る必要があります.
複雑なモデルでは大域的な相関は大抵は難しく, 不可能でなければ解析的に導かれます. 例えばデータのスケーリングがねじれているような非線形なモデルではデータの正規化(standardizing)は必ずしも助けには成りません. それゆえStanは適応的なwarmupでオンラインに補正を行います. 強い非線形性(ここでは局所性と同義)の相関は正規化を行った場合でさえ学習が遅くなり得ます. これがStanでなぜwarmupが必要でしばしばそれが長い時間を必要とするかの究極的な理由であり, 十分長いwarmupが実質的なパフォーマンスの改善をもたらすことが出来るかの理由でもあります.
質量行列は事後分布での線形(ここでは大域的または位置に依存しないことと等価)な相関のみを補正します. 階層的なパラメータ一方では階層的なモデルでよく現れるたちの悪い非線形(ここでは局所的または位置に依存したことと等価)な相関しています. ((リーマニアンHMCだけが計量を実現でき, それによって(パラメータ空間での)位置に依存した質量行列, 非線形な相関の補正が有効になり始めます. ))
密な質量行列の最も大きな問題は質量行列それ自体の推定が卵と鶏のような関係を引き起こすことです. 適切な質量行列をサンプリングから推定するには収束する必要があり, それには適切な質量行列が必要になります.
サンプリングに問題があるような統計モデルは一般には密な行列で調節できるような線形相関だけに支配されている訳ではありません. むしろもっと複雑な非線形の相関に支配されていてリーマニアンHMC(Riemannian HMC)のようなより高度なアルゴリズムを用いたパラメータの採取するのがよいです.
MCMCの収束にかかる時間は大まかには自己相関時間と同じです. HMC(とNUTS)のチェーンは自己相関が低くなりがちなのでとても速く収束しがちだからです.
これが唯一の適用できる場合は事後分布の幅の中に一様に分布している場合で多くの複雑なモデルではこの仮定は破れています. 実に多くの場合事後分布の質量は良い性質を満たしていて分布の裾はの曲率は大きく, 別の言い方をすればwarmupがゆっくりな理由は分布の裾においてHMCのイテレーションのコストが高いからと言えます.
分布の裾における低性能は数回のwarmupのイテレーションではカバーできないような振る舞いです. 最初の数回のイテレーションの受理確率とstep sizeを見れば, 解こうとしている問題がいかに悪い問題であるかといったことや, 事前分布をより問題にあうようタイトに設定するとか, パラメータを再調整するなどのモデリングの努力が必要であるかがわかります.
no-U-turnサンプラー(NUTS)は各イテレーションでの 不必要に事後分布の中を横切ることなしに値を提案出来るような適切な数の蛙飛びステップを自動的に選択できます. そのような手法を使う動機としては各ステップでのジャンプ距離の2乗の期待値を最大化すること(例えば(Roberts et al., 1997)を参照)と, 事後分布に相関があるときにメトロポリスサンプラーまたはギブスサンプラーで生じるのランダムウォーク的特性を避けるためというのがあります. NUTSアルゴリズムの厳密な定義と詳細釣り合いの照明は(Hoffman and Gelman, 2011, 2014)を参照してください.
NUTSはひとつ前のイテレーションで抽出されたパラメータで決められる初期位置から出発して提案値を生成します. そして独立な単位正規分布に従う運動量ベクトルを生成します. この最初の系は平衡2分木(balanced binary tree)を作るために時間的に前向き, 後ろ向きの両方に発展させます. NUTSアルゴリズムにおけるそれぞれのイテレーションにおいて木の深さは蛙飛びステップの数が倍になる, 実効的には計算時間が倍になると1増えます. アルゴリズムは以下の2つの条件のうち1つを満たすと停止します.
標準的なメトロポリスステップではなく, 最後の発展ステップ(生成されたイテレーションの後半)のスライスサンプリングのパラメータ値が選ばれます.
no-U-turnサンプリングの調整は各イテレーションで評価される木の深さに上限を定めることを伴います. これは深さの最大値のパラメータによって調節することができます. 蛙飛びステップの数は2の深さの最大値-1乗によって抑えられます.
木の深さと実際に計算された蛙飛びステップの数の双方はパラメータと一緒にレポートされ, それぞれtreedepth__ , n_leapfrog__ として出力されます. 最後の部分木は部分的にしか作られていないのでこの2つの値は常に
\[ 2^{treedepth-1} < N_leapfrog < 2^{treedepth}-1 \]
を満たします. 木の深さはNUTSの診断ツールとして重要です. 例えば木の深さが0というのは最初の蛙飛びステップによる提案値がすぐに拒絶(受理されず)最初の状態に戻ったときにおきます. これは極端な曲率か(少なくとも現在の状態に対して)不適切なステップサイズが選ばれたときに起こりえます. 一方木の深さが最大値に等しいときはNUTSは多くの蛙飛びステップを行い計算時間が非常に長くなるのを避けるために未熟な状態で停止することを示唆しています. 非常に多くの回数のステップは適応がうまく出来ていないことを示していて, 目標とする受理率が非常に高いかあるいは単にサンプルをするには難しい事後分布の形をしているのを示唆していると思われます. 後者の場合パラメータの再設定(reparameterization)が有効でしょう. しかし稀にモデルが正しく定義されていても多くのステップが必要になり, 最大の深さはNUTSの木が必要な大きさだけ成長できるような数まで大きくなるでしょう.
有効グラフィカルモデル上での純粋に前向きなデータのシミュレーション(あるいはパラメータとデータをシミュレートするための既知の事前分布から生成的に行われるようなもの)のような状況においては Stanではいかなるパラメータ(parameters
)も定義する必要がなくなります. model block
は空になり, 全ての出力される量はgenerated quantities block
で作られることになります. 例えばK回試行, 成功率θの二項分布からのN回抽出を生成するようなプログラムは以下のようになります.
data {
real<lower=0,upper=1> theta;
int<lower=0> K;
int<lower=0> N;
}
model {
}
generated quantities {
int<lower=0,upper=K> y[N];
for (n in 1:N)
y[n] <- binomial_rng(K, theta);
}
全てのStanプログラムはmodel block
を持たなければいけないのでこのプログラムは空のmodel block
を含みます. このモデルではサンプラーはパラメータがないために固定されたパラメータで調整されなければいけません. パラメータサンプリングなしでは適応をする必要がなく, warpupのイテレーションもする必要がないのでその回数は0にすべきです.
パラメータなしでのサンプリング用に書かれたほとんどのモデルではパラメータが定義される代わりにパラメータのような振る舞いをするdata block
が置かれます. それでも固定パラメータサンプリングと通常の方法(ランダム, 0固定, または指定した値)による初期化のためにパラメータを含めることは可能です. 例えば上記の例のtheta
はパラメータとして定義, 初期化することが出来ます.
次の演算子は配列をインプットとしてとり, 一つの値をアウトプットとして返すものです. The boundary values for size 0 arrays are the unit with respect to the combination operation (min, max, sum, or product).
real min(real x[])
x
の中の最小値を返す. ただし, x
のサイズが0の時は+∞を返す.
int min(int x[])
x
の中の最小値を返す. ただし, x
のサイズが0の時はerrorを返す.
real max(real x[])
x
の中の最大値を返す. ただし, x
のサイズが0の時は-∞を返す.
int max(int x[])
x
の中の最大値を返す. ただし, x
のサイズが0の時はerrorを返す.
int sum(int x[])
x
の要素の総和を返す. ただし, x
のsizeN
によっては以下のように返す.
\[\mbox{\tt sum}(x) = \left\{\begin{array}{ll} \sum_{n=1}^{N}x_{n} & \mbox{if} N > 0 \\ 0 & \mbox{if} N = 0 \end{array}\right.\]
real sum(real x[])
x
の要素の総和を返す. 上の定義を参照.
real prod(real x[])
x
の要素の総乗を返す. ただし, x
のsizeが0の時は1を返す.
real prod(int x[])
x
の要素の総乗を返す.
\[\mbox{\tt product}(x) = \left\{\begin{array}{ll} \prod_{n=1}^{N}x_{n} & \mbox{if} N > 0 \\ 1 & \mbox{if} N = 0 \end{array}\right.\]
real log_sum_exp(real x[])
x
の各要素のexpをとったものの総和の自然対数を返す. ただし, 配列が空の時は-∞を返す.
Stanで統計モデルの開発を行うということはStanプログラムを書くということを意味し, つまりそれは一種のソフトウェア開発プロセスです. ソフトを開発することは大変です. とても大変です. 動くパーツとその組み合わせが非常にたくさんあるため, とても多くのことが間違った方向に向かうことがあります.
コンピュータプログラムを書くこと固有の複雑さによって引き起こされる諸問題を軽減するために, ソフトウェア開発の営みはデザインされています. 不幸なことに, 多くの方法論は独断的な考えかセコいテクニック, もしくはその両方に話題がそれます. 開発者への堅実で実践的なアドバイスで私達がオススメできるものは, (Hunt and Thomas, 1999)と(McConnell, 2004)です. この節では彼らのアドバイスのいくつかをまとめようとしています.
バージョン管理のソフト(SubversionやGitなど)はコーディングしはじめる前に使えるように用意しておくべきです(注1). バージョン管理を学ぶことは大きな投資に見えるかもしれません. しかし, 一つのコマンドで前に作業していたバージョンに戻ることができたり, 今のバージョンと古いバージョンの差分を得ることができますので, その価値はあります. あなたが他の人と作業を共有する必要がある時には, それが紙の上であっても, もっとよいものになります. 作業は独立に行われ, 自動的にマージされるでしょう. Stan自体がどのように開発されているかについては62章を見てください.
(注1): StanはSubversion (SVN)を使ってはじまりましたが, もっと色々なことができるGitに移行しました. GitはSVNができることは全部できるし, もっと色々なことができます. 対価としてはSVNより学習曲線が急なことが挙げられます. 個人用, もしくはとても小さいチームでの開発ならSVNでいいです.
モデルを動かす時にコマンドライン上で(もしくはRやPythonのような言語を対話的なモードで起動して)コマンドを入力するよりは, スクリプトを書いてモデルにデータを入れて, 必要な事後の解析を試して欲しいです. スクリプトはシェルスクリプトでもRでもPythonでも書けます(訳注1). そのスクリプトはどんな言語でもいいですが, 必要なものはすべて含んで自己完結しているべきです. 設定されているグローバル変数や読み込まれている他のデータに依存するべきではありません.
Stanにおける再現性とそのインターフェースについての完全な情報については22章を見てください.
(訳注1): シェルスクリプトで書く場合はCmdStanを, Rで書く場合はRStanを, Pythonで書く場合はPyStanを使うことになります.
もし1行のコードのプロジェクトならやりすぎに見えるかもしれませんが, スクリプトはコードの実行の仕方だけではなく, 何が実行されるかについて具体的なドキュメントでもあるべきです.
乱数を使ったランダム性は再現性を損なわせますが, MCMC法は概念として乱数を利用します. Stanのサンプラーは乱数を使ったランダムな初期化を行うだけでなく, MCMCのiterationごとにも乱数を使ってランダム化しています(例えば, ハミルトニアン・モンテカルロは各々のiterationでランダムなモーメントを生成します).
コンピュータは決定論的です. 真のランダムネスは存在せず, 擬似乱数の生成があるだけです. 「種」をもとに乱数の列が生成されます. Stan では(Rのような他の言語でも)時刻や日付に基づいた種を生成する方法を使うことができます. また, 種をStanに(もしくはRに)整数として与えることもできます. Stanはあとで結果が再現するように, Stan自体のバージョン番号だけでなく, 乱数を生成するのに使った乱数の種を出力することができます(注2).
(注2): 再現性を保つにはコンパイラを同じにして, ハードウェアを同じにする必要もあります. なぜなら浮動小数点演算はOSやハードウェアの設定やコンパイラをまたいで振る舞いが完全に同じではないからです.
他の形式のライティングのように聴衆ありきでプログラムやスクリプトを扱うことで, コードの使われ方について重要な広がりがあります. 他人がプログラムやモデルを読みたくなるかもしれないだけでなく, 開発者もあとからそれを読みたくなるでしょう. Stanのデザインのモチベーションの一つは次の諸観点からモデルがドキュメントそのものになることでした. 変数の使い方(すなわちデータとパラメータの対比)や型(すなわち分散共分散行列と制限のない行列の対比)やサイズの観点です.
可読性の大きな部分は一貫性です. 特に名前とレイアウトにおける一貫性です. プログラムだけではなく, そのプログラムが置かれるディレクトリやファイルの名前やレイアウトの一貫性です.
コードの可読性はコメントについてだけではありません(Stanのコメントの推奨や文法については62.8節を見てください).
誰か他の人に助けを得るためにデバッグやデザインの問題について十分に説明しようとする時に, その問題の解決策が出てくることは驚くべきほどたくさんあります. これはメーリングリスト上でも起こりえます. 人to人の時に最もそうなります. あなた自身の問題を誰かに説明する時に解決策を見つけることはソフトウェア開発において非常に多いので, 聞いている人は「ラバーダック」と呼ばれています. なぜなら聞いている人は話に合わせてうんうんと相づちを打つだけだからです(注3).
(注3): 実際のラバーダックではうまくいかないことが研究によって示されています. 何らかの理由でラバーダックは実際に説明を理解できなければならないのです.
言うまでもないことですが, やみくもにデータをフィットだけさせようとしないでください. 実際に性質を理解しなければならないデータをよく見てください. もしロジスティック回帰をしているなら, それは分離可能ですか?もしマルチレベルモデリングをしているなら, そもそもの結果はレベルごとに変化していますか?もし線形回帰をしているなら, xとyの散布図を書いて線形モデルがあてはまりそうかどうか見てみましょう.
ソフトウェアのプロジェクトはだいたいいつも一つかそれ以上の意図的なユースケースからトップダウンでデザインされます. 一方, 良いソフトウェアのコーディングは典型的にはボトムアップで行われます.
トップダウンデザインの動機は明白です. ボトムアップ開発の動機は, すでにすっかりテスト済みのコンポーネントを使って開発するほうがはるかに容易だということです. Stanはモジュール対応もテストへの対応も組み込まれていませんが, 同じ原理の多くがあてはまります.
Stanの開発者自身がモデルを構築するやり方は, できるだけ単純にスタートし, そこから組み立てていきます. これは最終的に複雑なモデルを想定しているときであっても, また最終的にフィットさせたいモデルの良いアイディアを持っている場合であっても同様です. 複数の交互作用, 共分散の事前分布, またはその他の複雑な構造の階層的モデルを構築するよりも, 単純にスタートしましょう. 固定の(そしてややタイトな)事前分布の単純な回帰モデルを構築しましょう. それから交互作用やレベルの追加をおこないます. 1度にひとつずつ. 正しくなっていることを確認しましょう. それから拡張です.
あなたのモデルが計算上正しいことを確認するための最善の方法のひとつは, シミュレートされた(つまり偽りの)データを既知のパラメータで作成し, それからモデルがこのパラメータをデータから推定することができるかどうかを確認することです. もしだめであれば, 生のデータで正しい結果を得る希望はほとんど持てないでしょう.
これを行う洒落た方法がいくつかあります. そこでは周辺化された統計量に対するカイ2乗検定を行ったり, 区間検定を扱うクックら(2006)の枠組みに従うことができます.
Stanにはステップごとのデバッガも単体テストのフレームワークもついていませんが, 昔ながらのprintfでのデバッグはサポートしています(注4).
Stanは1以上の文字列もしくは式を引数として持つprint文をサポートしています. Stanは命令型の言語なので, 変数はプログラムの実行中に異なる場所で異なる値を持つことができます. print文はStanのようなステップごとのデバッガを持たない言語には非常に価値があります.
例えば, 変数yとzの値を表示するときは以下のような文を使います.
print("y=", y, " z=", z);
このprint文は文字列"y="
に続いて変数yの値, 文字列" z="
(頭にスペースをつけて)に続いて変数zの値を表示します.
それぞれのprint文の最後には改行がつきます. 改行のための具体的なアスキー文字はプラットフォーム依存です.
任意の式表現を使うことができます. 例えば,
print("1+1=", 1+1);
という文は"1+1=2"
に続けて改行を表示します.
print文は他の命令を使うことができる場所ならどこでも使うことができますが, 何回実行されるかは記述されているブロックがどのくらいの回数評価されるかに依存します. print文の文法と評価について詳しくはセクション26.8を参照してください.
(注4) 「f」がついているのはタイプミスではありません. これはC言語で書式付き表示をするときに使われるprintf関数の名前にちなんだ歴史的な産物です.
機械はドキュメントに書かれたことではなくコードに書かれたことを実行します. ドキュメントは一方, 必ずしもコードと一致しません. ドキュメントがきちんとメンテナンスされていない場合, コードの進化にともなってコードのドキュメントは容易に腐ってしまいます.
したがって, 読めないコードのドキュメントを書くのに対して, 読めるコードを書くほうが常に好ましいです. ドキュメントを書くときにはいつも, コードをそんな風にかく方法がないか自問して, ドキュメントが不必要になるようにしましょう.
StanはC++スタイルのコメントをサポートしています. 詳しくはセクション28.1をごらんください. お勧めのスタイルはコード内の短いコメントや1行以上をコメントアウトするときには行コメントにすることです. ブロックコメントは長いドキュメンテーションコメント用にとっておきます. このしきたりの理由は, ブロックコメントはブロックコメントの中に作ることができないからです.
コードにコメントをつける際, 使用中のプログラミング言語の基本を理解している他のプログラマのためにコメントを書いていると想定するのが通常は安全です. 別の言葉で言えば, 明白なことはコメントしてはいけません. 例えば, 以下のような, コードに何の情報も付加しないコメントを書く必要はありません.
y ~ normal(0,1); // yは標準正規分布に従う
以下の例のような手動での変数変換に対するヤコビアンの調整であればコメントする価値があるかもしれません.
exp(y) ~ normal(0,1);
// 変数変換に対するヤコビアンの調整: y = log | d/dy exp(y) |
increment_log_prob(y);
将来このコードを読む人に共感し, その人達が統計やStanについて何を知らないか(または覚えていないか)を決めてコメントを書くのは一種の芸術です.
N
やmu
, sigma
といった一般的な変数名をつけているときには変数宣言にコメントをつけることが助けになるでしょう. 例えば, 項目反応モデルにおけるいくつかのデータ変数宣言は以下のように有用なコメントをつけるとよいでしょう.
int<lower=1> N; // 観測数
int<lower=1> I; // 生徒数
int<lower=1> J; // テストの問題数
別の方法は, コメントが不要な長い変数名にすることです.
int<lower=1> n_obs;
int<lower=1> n_students;
int<lower=1> n_questions;
どちらのスタイルも合理的で, どちらを採用するかは主として好みの問題です(統計的な慣習を持つコードの読者を混乱させないように, 時にモデル独自の命名の慣習に従う必要があるというのが主な理由です).
一部のコード作者は冒頭に大きなブロックコメントを置いてモデルの目的, 誰が書いたか, 著作権とライセンスの情報などなどを説明することを好みます. 以下の複数行コメントは大きなコメントブロックの従来の書き方の例です.
/*
* Item-Response Theory PL3 Model
* -----------------------------------------------------
* Copyright: Joe Schmoe <joe@schmoe.com>
* Date: 19 September 2012
* License: GPLv3
*/
data {
// ...
アスタリスクで始まるコメントの書き方は読者がコメントの範囲を理解するのを助けます. 一方コメントに日付やその他の変わりやすい情報を含めることの問題点は, その情報が, 簡単にコードの実情と同期しなくなってしまうことです. 誤解を招いたり間違っているコメントは, 全くコメントがないよりも悪いのです!
現代のコンピュータにおける浮動小数点演算は, IEEE 754に準拠した基礎的な数値演算が完全には規定されていないため, 追試が難しいことで有名です. 根本的な問題点は演算の精度がハードウェアプラットフォームやソフトウェアの実装により異なっていることにあります. Stanは完全な再現性を許容すべく設計されています. しかしながら, それはあくまで浮動小数点計算により課せられた外的制約により左右されます.
Stanの結果は以下のすべての要素が一致しているときにのみ厳密に再現可能となります:
これはStanの安定リリースを使っているか, 特定の Git ハッシュタグを持つバージョンを使っているかには関係ありません. インターフェイスやコンパイラなどについても同様です. 重要なのはもしこれらのどれか一つでも何らかの違いがあれば, 浮動小数点計算の結果は変わる可能性があるということです.
具体的には, もしあるStanプログラムをCmdStanでコンパイルするときに, 最適化フラグを(-O3 から -O2 または -O0へ)変更した場合, 変更前と変更後の一連の結果は必ずしも一致しません. このため, クラスターや, IT部門に管理されている, もしくは自動更新がONになっているデスクトップのように外部に管理されたハードウェア上で再現性を保証するのは極めて困難です.
しかしながら, もしStanプログラムを一組のフラグを使ってコンパイルし, そのコンピュータをインターネットから取り外して一切アップデートしないようにし, 10年後に戻ってきて同じように再コンパイルした場合, 同じ結果が得られます.
データについてもビットレベルで同じである必要があります. 例えば, もしRStanであればRcppがRの浮動小数点数とC++の倍精度小数の間の変換を行います. もしRcppが変換のプロセスを変更したり異なる型を使うと, 結果はビットレベルで同じであることは保証されません.
コンパイラとコンパイラの設定も同じ問題を起こす可能性があります. インテル製のプロプライエタリなコンパイラで再現性をいかにコントロールするかについての素敵な議論はCoden and Kreirzer(2014)を読んでください.
RやPythonなどの高レベルなスクリプト言語から使う場合, Stanのプログラムは動的にリンク可能なオブジェクトファイルに変換されます.↩
これらの推定された質量行列は大域的です. これは, サンプリングされるパラメータ空間の全ての点に同じ質量行列が適用されるという意味です. Riemann-manifold HMCではこれが一般化され, 質量行列によって示唆される曲率が位置ごとに変化することが許されます.↩
十分に有効サンプルが得られないことは, warmupの期間の長さが不十分であることがしばしば原因です. この再実行戦略は, 最初に正しいiteration数を推測するよりも, 高々約50%多いiterationを消費するだけです.↩
Linuxでは ラインフィードが改行に, Windowsでは キャリッジリターン+ラインフィードが改行に相当します.↩
C++やRのような言語では, ある名前の変数を, より狭いスコープで宣言し, それを含むスコープで定義された変数を隠蔽する(評価時に優先する)ことが許されています.↩
このモデルはhttps://github.com/stan-dev/example-models/tree/master/misc/clusterから入手可能です.↩
クラスタリングではあらゆる混合モデルにおいて識別不可能性が問題となりますが, 分類ではそのような問題はありません. なお, クラスタリングのフルベイズ推定がむずかしいにもかかわらず, 研究者に利用され続けているのは, 予測のためのモデル化というよりも探索的データ分析としての位置づけによるものです.↩
このモデルはhttps://github.com/stan-dev/example-models/tree/master/misc/clusterから入手可能です.↩
この例は, Stanがrk45
ソルバーを実装するために使ったBoost Numeric Odeint library (Ahnert and Mulansky, 2011)のドキュメントからとってきている.↩
偶然の一致ではなく, 一般のStanのモデルの事後分布に曲率が高い箇所があると, ユークリッド距離を使うハミルトニアンモンテカルロ(HMC)のサンプリングにおいて同様の問題が起こります. 理由はHMCは, 蛙跳び積分のアルゴリズムや傾きをもとにしたステップを使う微分方程式のソルバーを使っているからです. その微分方程式のソルバーはポテンシャルエネルギーと運動エネルギーの項を分けることができるハミルトン系に特化しています.↩
対照的に(罰則付き)最尤推定は, パラメータ化によって不変ではありません.↩
Gelmanの便利な統計用語集(http://andrewgelman.com/2009/05/24/handy_statistic/)ではピノキオの原則に言及しています. この原則は「計算上の理由だけで作られたモデルにも魂が宿り, ひとり歩きする可能性がある」というものです. この原則はGelmanがフォーク定理と呼んでいる経験則にも関係があります. その定理は「計算がうまくいかないときは, しばしばあなたのモデルに問題がある」というものです.↩
この例は説明のためのものです. Stanで対数正規分布を実装するオススメの方法はビルトインの確率分布関数lognormal
を使うことです(52.1節を見てください).↩
プログラムはhttps://github.com/stan-dev/example-models/tree/master/basic_distributionsから入手可能です.↩
問題は三角分布の裾が(とても!)軽いことです. 標準のHMCやNUTSサンプラーは三角分布の角にうまく入っていくことができません. 一方はじめのStanコードでは, y
の型をreal<lower=-1,upper=1>
と宣言しているので, 制約のない変数に逆ロジット変換が適用され, その変換のヤコビアンの絶対値の対数が対数確率に足されます. 結果的に得られるロジット変換されたy
の分布は問題なく振る舞います. Stanで使われる変換についてもっと情報が知りたい場合は34章を参照してください.↩
コメントの主な問題は誤解を招く恐れがあることです. それはプログラマー側の誤解によるかもしれませんし, コメントがかかれたあとにプログラムの振る舞いが変えられたためかもしれません. プログラムは常にコードに書かれたように振る舞います. そのため, 複雑なコードをリファクタリングして理解しやすい部品にすることは単にコメントを加えることよりも好ましいのです.↩
さまざまなビルトインのバリデーション方法はもうすぐStanにも実装されます!現状では代わりに, 単体の制約をチェックするためにreject
文を使うことができるでしょう.↩
Stan 2.10.0現在, ユーザー定義関数で例外を発生させる唯一の方法は, 関数が呼ばれる際に(サンプリング文で呼ばれる場合も含む)reject
文を使って例外を発生させることです.↩
Stanの将来のバージョンでは前もって宣言しなくても動くようになるでしょう.↩
訳注: 原文はpriorsとなっていますが, 文脈からすると事後分布です.↩
この例は, Richard McElreathがStan users groupで提起しました. BUGSおよびJAGSで使われるギブズサンプリングと, Stanで使われるハミルトニアンモンテカルロ(HMC)およびno-U-turnサンプラー(NUTS)との挙動の違いについての質問の中でのことです.↩
訳注: 原文では, \(\lambda_1+q, \lambda_1-q\)ですが, 誤植のようです.↩
\(\sigma\)の周辺事後分布\(p(\sigma \mid y)\)はこの場合, 少なくとも異なる2つのデータ点がある限りは正則です.↩
訳注: 原文では, \(\lambda_2 = \lambda_1 + c\)となっていますが, 誤りのようです.↩
訳注: 原文では\(c\)を能力に加え, 難易度から引くとなっていますが, 上の式からすると双方に加えるのが正しいようです.↩
訳注: \(x_{k'}\)の係数は正しくは\((\beta_{k'}+c(1-d)\beta_{k})\)のようです.↩
訳注: 原文はdifficultyですが, discriminationの誤りと思われます↩
ラプラス分布を事前分布にすること(あるいは罰則付き最尤推定のL1正規化)は加法的な不変性を取り除くには十分ではありません. 縮小はしますが, それ自体がパラメータを識別するというわけではありません. \(\lambda_1\)に定数を加え, \(\lambda_2\)からそれを引くと, 事前分布で同じ値となる場合があるからです.↩
訳注: 原文はpriorですが, 文脈からすると事後分布と思われます.↩
テンパリング法が, 局所最頻値を探す自動化した方法と見られることもあります. しかし, ほとんどのMCMCテンパリング法は, 止めることが難しく, そのまま局所最頻値を探し続けます. Swendsen and Wang, 1986; Neal, 1996bを参照.↩
訳注: 原文は誤っているようです.↩
訳注: 原文はmiddleですが, 誤りと思われます.↩