Stan モデリング言語: ユーザーガイド・リファレンスマニュアル

Stan Development Team

翻訳: stan-jaチーム

なお最新のpdfはこちらからダウンロードできます.

Stan Version 2.9.0

1. Overview

このドキュメントは統計モデリング言語であるStanのユーザーガイド兼リファレンスマニュアルです. この導入の章ではStanの全体像について紹介しますが, 残りの章ではモデルの実際のプログラミングや, Stanのモデリング言語としての詳細な解説を, コードやデータの型も含めて, 実践的な解説を行います.

1.1 Stan Home Page

最新のコード, 例, マニュアル, バグレポート, 機能追加の要望など, Stanに関する情報は下記のリンクにあるStanのホームページから参照できます.

http://mc-stan.org

1.2 Stanのインターフェース

Stan Projectでは3つのインターフェースをプロジェクトの一部としてサポートしています. モデリング部分やその使い方に関しては3つのインターフェースで共通していてるので, このマニュアルはその3つに共通するモデリング言語としてのマニュアルとなります. 初期化やサンプリング, チューニング方法についてはすべてのインターフェースで共通していて, また事後分布を分析する機能についてもおおむね共通しています.

提供されているすべてのインターフェースについて, getting-started guideやドキュメントが完全なソースコードと共に提供されています.

CmdStan

CmdStanはコマンドラインからStanを利用することを可能にします. ある意味でCmdStanはStanのリファレンス実装ともいえます. もともとCmdStanのドキュメントはこのドキュメントの一部でしたが, 今では独立したドキュメントとなっています. CmdStanのホームページは以下になります.

http://mc-stan.org/cmdstan.html

RStan

RStanはRにおけるStanのインターフェースです. R2WinBUGSやR2jagsでは, Rから外部のソフトウェアとしてWinBUGSやJAGSを呼び出していました. RStanではそうではなく, むしろRのメモリを通じたインターフェースになっています. RStanのホームページは以下になります.

http://mc-stan.org/rstan.html

PyStan

PyStanはPythonにおけるStanのインターフェースです. 外側のStanを呼び出すというよりは, RStanと同様にPythonのメモリレベルのインターフェースです. PyStanのホームページは以下になります.

http://mc-stan.org/pystan.html

MatlabStan

MatlabStanはMATLABにおけるStanへのインターフェースです. RstanやPyStanとは異なり, 現状MatlabStanはCmdStanのラッパーです. MatlabStanのホームページは以下になります.

http://mc-stan.org/matlab-stan.html

Stan.jl

Stan.jlはJuliaにおけるStanのインターフェースです. これもMatlabStanと同様に, CmdStanのラッパーです. Stan.jlのホームページは以下になります.

http://mc-stan.org/julia-stan.html

StataStan

StataStanはStataにおけるStanのインターフェースです. MatlabStan, Stan.jl と同様にこれもCmdStanのラッパーです. StataStanのホームページは以下になります.

http://mc-stan.org/stata-stan.html

1.3 Stanのプログラム

Stanのプログラムでは条件付き確率分布 p(θ ∣ y, x) を通して統計モデルが定義されます. ここでθはモデルに組み込まれる, 一連の未知の変数(例: モデルのパラメータ, 隠れ変数, 欠測データ, 将来の予測値)です. yはモデルに組み込まれる, 一連の値が得られている変数です. xはモデルに組み込まれない, 一連の説明変数と定数です(例:サイズ, ハイパーパラメータ).

Stanのプログラムは, 変数の型宣言と文(ステートメント)からなります. 変数の型には整数, 実数, ベクトル, 行列はもちろん, その他の型の(多次元な)配列があり, それぞれ値を制約することもできます. 変数は, その役割に応じて, datatransformed dataparameterstransformed parametersgenerated 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で実装できることを意味しています(もちろんその道は険しいですが). ちなみにチューリング完全であるために求められるのは, ループと条件分岐, そしてループの中でサイズが変更できる配列のみです.

1.4 コンパイルとStanプログラムの実行

Stanのコードはまず最初にStanのコンパイラstancによってC++の言語へと変換され, そしてそのC++はプラットフォーム依存の単独で実行可能な形式に変換されます. またStanはWindows, Mac OS X, Linuxなど様々な実行可能形式を出力することができます. 1 Stanの実行ファイルを実行すると, まず既知の yx を読み込み, その妥当性を評価します. そして(独立ではない)同一分布に従うサンプルの列 θ(1), θ(2), … を生成します. これらは各々, 周辺分布 p(θ ∣ y, x) に従います.

1.5 サンプリング

連続値をとるパラメータに対してStanは Hamiltonian Monte Carlo (HMC) Sampling (Duane et al., 1987; Neal, 1994, 2011) というある種のマルコフ連鎖モンテカルロ(MCMC)サンプリング(Metropolis et al., 1953)を用います. Stanは離散値をとるパラメータのサンプリングは提供していません. 観測値としては離散値を直接利用することができますが, 離散値をとるパラメータはモデルから周辺化消去されている必要があります. 10章と12章では, いかにして有限の離散値をとるパラメータを和をとってモデルから消去するか, そしてその消去がもたらすサンプリング効率の大幅な向上について議論します.

HMCは対数確率関数の勾配を使うことで, 定常分布への収束とパラメタの探索の効率化を促進しています. 推定すべき量のベクトルθは仮想的な粒子の位置と解釈されます. それぞれの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の間に対角行列となる質量行列や完全な質量行列を推定するように設定できます. また, 将来的にはユーザが指定した質量行列をサポートする予定です. 対角行列となる質量行列を推定すると, 未知パラメータ系列θの各要素θkのスケールは正規化されます. 一方, 完全な質量行列を推定すると, スケーリングと回転の両方を考慮することができますが, 2行列演算のためにleapfrogの各ステップにおいて, より多くのメモリと計算を要します.

収束のモニタリングと有効サンプルサイズ

マルコフ連鎖から得られるサンプルは, その連鎖が定常分布に収束したあとに, 周辺分布 p(θ ∣ 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}}}$ に比例します. ここでN_EFF は有効サンプルサイズです. したがって, 目下の推定や推論のタスクに対し十分な大きさになるまで, 有効サンプルサイズ(やその推定値)をモニターするのは標準的な習慣です.

ベイズ推定とモンテカルロ法

Stanはフルベイズの推定をサポートするために開発されました. ベイズ推定は下記の正規化されていないベイズ則


p(θ ∣ y, x) ∝ p(y ∣ θ, x)p(θ, x)

に基づいています. これは, データ y(と定数x)が与えられたもとでのパラメータ θ の事後分布 p(θ ∣ y, x) が尤度 p(y ∣ θ, x) と事前分布 p(θ, x) の積に比例することを表します.

Stanでは, ベイズモデリングは定係数を除いて事後確率関数のコーディングを伴います. ベイズ則から事後確率分布は定係数を除いて尤度関数と事前確率の積と等価です.

フルベイズ推定では, 事後分布p(θ|y, x)によるモデル化において, パラメータθの値に関する不確実性が含まれ伝搬されてゆくことになります. このことは, 事後分布からの一連のサンプルについて推定を行うことで実現できます. その際には, 事後平均や事後確率区間等を関心のある量のためにプラグイン推定値として利用したり, 事後分布に基づいて事象の結果や未観測のデータ値を予測したりします.

1.6 最適化

Stanは最適化に基づく推論もサポートしています. 与えられた事後分布 p(θ ∣ y) に対して, Stanは以下で定義される事後分布の最頻値 θ *  を見つけることができます.


θ *  = argmaxθp(θ ∣ y)

ここで argmaxvf(v) という記法は 関数 f(v) を最大化する v の値を選ぶことを意味します.

もし事前分布が一様分布ならば, 事後分布の最頻値は, パラメータの最尤推定値(maximum likelifood estimate, MLE)に対応します. また, 事前分布が一様分布でなければ, この事後分布の最頻値はしばしばMAP(maximum a posterior)推定値と呼ばれます.

最適化においては, 変数の制約に由来するいかなる変換のヤコビアンも無視されます. 多くの最適化問題でより効率的に計算するには, 変数宣言時における上下限の制約を取り除いて, 代わりに台の範囲外となる無効な解を許さないようにmodelブロックで棄却するようにします.

点推定値による推論

推定値θ *  はいわゆる「点推定値」と呼ばれています. これは事後分布を分布と言うよりむしろ一つの点として要約することを意味します. もちろん点推定値それ自体は推定のばらつきを考慮しません. 事後予測確率$p(\tilde{y} \mid y)$は, データ y が与えられた場合の事後最頻値を用いて, $p(\tilde{y} | \theta^\ast)$ のように作ることができます. しかし, これらは事後確率に不確実性を考慮しないため, たとえ事前分布を持っていたとしてもベイズ推定ではありません. もし, 事後分布の分散が小さく, その平均が最頻値に近ければ, 点推定の結果はフルベイズの推定結果と非常に近くなります.

1.7 変分推論

Stanはベイズ推定を近似する変分推論もサポートしています(Jordan et al., 1999; Wainwright and Jordan, 2008). 変分推論では近似した事後分布を用いて, 事後分布の平均値と不確実性を推定することができます. 近似した事後分布は, パラメトリックな分布が真の事後分布にあてはまるように最適化して求めます. 変分推論は, 特に機械学習の分野においてベイズ推定の計算にすさまじい影響を与えてきました. 典型的には変分推論は従来のサンプリングによる推定に比べて速く, 巨大なデータにスケールしやすいという特徴があります(Hoffman et al., 2013).

変分推論は事後分布 p(θ ∣ y) をシンプルなパラメトリックな分布 q(θ ∣ ϕ) で近似します. このことは真の事後分布との以下で与えられるKullback-Leibler情報量を最小化することに対応します.


ϕ *  = argminϕKL[q(θ ∣ ϕ)‖p(θ ∣ y)]

これはベイズ推定の問題を, 解がwell-definedな計量をもつ最適化問題に帰着できることを意味します. 変分推論では, サンプリングに比べてオーダーが異なるほど収束が速いです. 近似の精度はモデルによって異なります. 変分推論が点推定のテクニックではないことに注意すると, 結果は事後分布を近似した分布だということができます.

Stanでは自動微分変分推論(Automatic Differentiation Variational Inference, ADVI)というアルゴリズムが実装されています. このアルゴリズムはStanの変数変換ライブラリや自動微分に関するツールボックスを活用するようにデザインされています(Kucukelbir et al., 2015). ADVIは変分推論のアルゴリズムを導出するのに典型的に必要となるすべての数学を回避し, いかなるStanのモデルにおいても動作します.

2. ソフトウェア開発としてのモデル構築

Stanで統計モデルの開発を行うということはStanプログラムを書くということを意味し, つまりそれは一種のソフトウェア開発プロセスです. ソフトを開発することは大変です. とても大変です. 動くパーツとその組み合わせが非常にたくさんあるため, とても多くのことが間違った方向に向かうことがあります.

コンピュータプログラムを書くこと固有の複雑さによって引き起こされる諸問題を軽減するために, ソフトウェア開発の営みはデザインされています. 不幸なことに, 多くの方法論は独断的な考えかセコいテクニック, もしくはその両方に話題がそれます. 開発者への堅実で実践的なアドバイスで私達がオススメできるものは, (Hunt and Thomas, 1999)と(McConnell, 2004)です. この節では彼らのアドバイスのいくつかをまとめようとしています.

2.1. バージョン管理を使う

バージョン管理のソフト(SubversionやGitなど)はコーディングしはじめる前に使えるように用意しておくべきです(注1). バージョン管理を学ぶことは大きな投資に見えるかもしれません. しかし, 一つのコマンドで前に作業していたバージョンに戻ることができたり, 今のバージョンと古いバージョンの差分を得ることができますので, その価値はあります. あなたが他の人と作業を共有する必要がある時には, それが紙の上であっても, もっとよいものになります. 作業は独立に行われ, 自動的にマージされるでしょう. Stan自体がどのように開発されているかについては62章を見てください.

(注1): StanはSubversion (SVN)を使ってはじまりましたが, もっと色々なことができるGitに移行しました. GitはSVNができることは全部できるし, もっと色々なことができます. 対価としてはSVNより学習曲線が急なことが挙げられます. 個人用, もしくはとても小さいチームでの開発ならSVNでいいです.

2.2 再現可能にする

モデルを動かす時にコマンドライン上で(もしくは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やハードウェアの設定やコンパイラをまたいで振る舞いが完全に同じではないからです.

2.3. 可読性を高める

他の形式のライティングのように聴衆ありきでプログラムやスクリプトを扱うことで, コードの使われ方について重要な広がりがあります. 他人がプログラムやモデルを読みたくなるかもしれないだけでなく, 開発者もあとからそれを読みたくなるでしょう. Stanのデザインのモチベーションの一つは次の諸観点からモデルがドキュメントそのものになることでした. 変数の使い方(すなわちデータとパラメータの対比)や型(すなわち分散共分散行列と制限のない行列の対比)やサイズの観点です.

可読性の大きな部分は一貫性です. 特に名前とレイアウトにおける一貫性です. プログラムだけではなく, そのプログラムが置かれるディレクトリやファイルの名前やレイアウトの一貫性です.

コードの可読性はコメントについてだけではありません(Stanのコメントの推奨や文法については2.8節を見てください).

誰か他の人に助けを得るためにデバッグやデザインの問題について十分に説明しようとする時に, その問題の解決策が出てくることは驚くべきほどたくさんあります. これはメーリングリスト上でも起こりえます. 人to人の時に最もそうなります. あなた自身の問題を誰かに説明する時に解決策を見つけることはソフトウェア開発において非常に多いので, 聞いている人は「ラバーダック」と呼ばれています. なぜなら聞いている人は話に合わせてうんうんと相づちを打つだけだからです(注3).

(注3): 実際のラバーダックではうまくいかないことが研究によって示されています. 何らかの理由でラバーダックは実際に説明を理解できなければならないのです.

2.4. データを探検する

言うまでもないことですが, やみくもにデータをフィットだけさせようとしないでください. 実際に性質を理解しなければならないデータをよく見てください. もしロジスティック回帰をしているなら, それは分離可能ですか?もしマルチレベルモデリングをしているなら, そもそもの結果はレベルごとに変化していますか?もし線形回帰をしているなら, xとyの散布図を書いて線形モデルがあてはまりそうかどうか見てみましょう.

2.5. トップダウンでデザインし, ボトムアップでコーディングする

ソフトウェアのプロジェクトはだいたいいつも一つかそれ以上の意図的なユースケースからトップダウンでデザインされます. 一方, 良いソフトウェアのコーディングは典型的にはボトムアップで行われます.

トップダウンデザインの動機は明白です. ボトムアップ開発の動機は, すでにすっかりテスト済みのコンポーネントを使って開発するほうがはるかに容易だということです. Stanはモジュール対応もテストへの対応も組み込まれていませんが, 同じ原理の多くがあてはまります.

Stanの開発者自身がモデルを構築するやり方は, できるだけ単純にスタートし, そこから組み立てていきます. これは最終的に複雑なモデルを想定しているときであっても, また最終的にフィットさせたいモデルの良いアイディアを持っている場合であっても同様です. 複数の交互作用, 共分散の事前分布, またはその他の複雑な構造の階層的モデルを構築するよりも, 単純にスタートしましょう. 固定の(そしてややタイトな)事前分布の単純な回帰モデルを構築しましょう. それから交互作用やレベルの追加をおこないます. 1度にひとつずつ. 正しくなっていることを確認しましょう. それから拡張です.

2.6. シミュレートされたデータでフィットさせる

あなたのモデルが計算上正しいことを確認するための最善の方法のひとつは, シミュレートされた(つまり偽りの)データを既知のパラメータで作成し, それからモデルがこのパラメータをデータから推定することができるかどうかを確認することです. もしだめであれば, 生のデータで正しい結果を得る希望はほとんど持てないでしょう.

これを行う洒落た方法がいくつかあります. そこでは周辺化された統計量に対するカイ2乗検定を行ったり, 区間検定を扱うクックら(2006)の枠組みに従うことができます.

2.7. 表示することでデバッグする

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関数の名前にちなんだ歴史的な産物です.

2.8. コメント

コードは嘘をつかない

機械はドキュメントに書かれたことではなくコードに書かれたことを実行します. ドキュメントは一方, 必ずしもコードと一致しません. ドキュメントがきちんとメンテナンスされていない場合, コードの進化にともなってコードのドキュメントは容易に腐ってしまいます.

したがって, 読めないコードのドキュメントを書くのに対して, 読めるコードを書くほうが常に好ましいです. ドキュメントを書くときにはいつも, コードをそんな風にかく方法がないか自問して, ドキュメントが不必要になるようにしましょう.

Stanのコメントスタイル

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について何を知らないか(または覚えていないか)を決めてコメントを書くのは一種の芸術です.

コメントすべきこと

Nmu, 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 {
  // ...

アスタリスクで始まるコメントの書き方は読者がコメントの範囲を理解するのを助けます. 一方コメントに日付やその他の変わりやすい情報を含めることの問題点は, その情報が, 簡単にコードの実情と同期しなくなってしまうことです. 誤解を招いたり間違っているコメントは, 全くコメントがないよりも悪いのです!

3.データ型

本章ではStanで変数の宣言や, 式の値に使われるデータ型について議論します. 変数の型はパラメータの宣言, データの一貫性のチェック, 関数の呼び出し, 変数に値を代入する場合のすべてにおいて重要な要素となります. Stanにおいて, すべての式と変数の宣言は, 静的に定められたデータ型と関係しています(すなわちプログラムがコンパイルされた時). 一方, vector,matrix, arrayの大きさは動的に決定されます(プログラムが動作する際). これは動的に変数に文字列を代入し, 後から変数に行列を代入することができる, Rのような言語とは大きく異なっています. 式は変数, 定数のような基礎要素, もしくは引数に適用される関数や演算子のような要素から構成されているでしょう. 本章では基本的なデータ型に話題を絞り, それらがどのように宣言, 代入, 使用されるかについて解説します. array, vector,matrixなどのコンテナ型の詳細な比較については次章に譲ります.

3.1. 基本データ型

組み込み型関数やユーザ定義が定義した関数の引数, ローカル変数はいずれも基本データ型である必要があります. 基本データ型とは, 制約のないプリミティブ型, vector型, matrix型, およびそれらを並べたarray型のことです.

プリミティブ型

Stanには連続値に対応したrealと, 整数に対応したintの2種のプリミティブ型が用意されています.

vector, matrix

Stanには列ベクトルに対応したvector, 行ベクトルに対応したrow_vector, 行列に対応したmatrixの3種の行列ベースのデータ型が用意されています.

array

array引数を宣言することで, 任意の型(次節で紹介する制約付き型も含む)をarray型とすることができます. 以下に具体例を示します.

real x[10];
matrix[3,3] m[6,7];

上記のように記述すると, は1次元で10個の実数が含まれた値として宣言することになります. mは同様に, 3 × 3行列を6 × 7個並べた2次元arrayを宣言したことになります.

3.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)であるような下三角行列を表すために使用できます. 因子分解, スケーリングが容易なコレスキー因子型を使うことで, すべての相関行列, 分散共分散行列を計算するよりもはるかに効率よく計算を行うことができます.

3.3. 代入・引数の受け渡し

代入

制約付けられたデータの値は基本型にマッチする制約付けられていない変数に代入されるかもしれないし, その逆の, 制約付けられていないデータの値が基本型にマッチする制約付けられた変数に代入されることもあるかもしれません. マッチングに際してはarrayの次元数, 基本型が同じであるか, 厳密に解釈されます. 制約付けは考慮されませんが, 基本データ型については考慮されます. array, vectorはお互いに代入することはできません. 同様に, たとえ次元が一致していても, vector, matrixはお互いに代入することはできません. 第4章ではvector, arrayをどのように使い分けるのが適切かについて, 詳細を示します.

関数の呼び出し

Stanの関数に引数を渡すと, 基本型への代入のように動作します. Stanの関数はarrayの次元(real a[10,10,10]なら3)を含む, 引数の基本データ型だけからどの関数が呼ばれるかが決まります(arrayの大きさ, 制約は含まない). もちろん, 関数はしばしばそれらの動作の一部として制約を確認することがあります.

4. コンテナ(値の入れ物): array, vector, and matrix

Stanにはarray, vector, matrix 3つのコンテナの型が用意されています. これら3つの型は取り換え可能ではありません. たとえ次元が一致していても, ある型の変数から別の型の変数に代入できません. Stanにおいて, 3 × 4のmatrixは3 × 4のarrayとは全く別種類のオブジェクトなのです.

4.1. vector and matrix

vector, matrixarrayに比べると, 制限されたデータ構造になっています. vectorは本来realを1次元状に集めたもので, matrixは本来2次元状に集めたものです.

matrix型の用途は, コード内で行列を使用していることを強調するためです. Stanにおいて, vector,matrix型を使うのはおそらく以下の3つの場合のみでしょう.

  1. 行列演算 (行列の乗算など)
  2. 線形代数の関数 (固有値, 行列式など)
  3. 多変量関数のパラメータと出力 (多変量正規分布の引数など)

vector, matrixは整数値を返すことはできず, 取り扱えるのはrealに限られています. (注1)

(注1):Stanにおいて, 複雑な整数行列演算や, ブール行列演算が実行されている際にはこれは変更される場合があります. これは, 整数が行列演算に適切な入力ではないためです.

4.2. array

他方, arrayは, は本来, 他の種類のオブジェクトを1次元状に集めたものです. arrayには単純なrealint, vector, matrix, 別のarrayのような, あらゆるデータ型の値を格納することができます. arrayはStanにおいて唯一, 整数値の並びを格納できる型です. Stanにおいては, 離散分布などの関数が整数値を引数とします. 2次元のarrayは概念的にも現在の実装面からも, arrayを並べたものとして扱うことができます. インデックスをarrayに与えると, arrayはそのインデックスにおける値を返します. 複数のインデックスをarrayに与えると, 連鎖的にインデックスにおける値を返す動作が行われます. 例えば, aという2次元arrayがあったとすると, a[m,n]という書き方はa[m][n]の便利な縮めた書き方にすぎません.

4.3. 効率についての考察

Stanの根底にある設計の動機の1つとして計算の効率があります.

Stanにおいて, 行列, 線形代数の演算はEigen C++ライブラリのデータ型をベースに実装されています. このため, 行列演算や線形代数の関数を使用する際vector, matrixを型としていれば, データ型を変換する必要はありません. 他方, arrayはC++のstd::vectorクラスのインスタンスとして実装されています. (EigenライブラリのEigen::vectorクラスや, Stanのvectorと混同しないように注意しましょう). arrayはこのように実装されているため, インデックスにおける値を返すのはとても効率的です. なぜならコピーして値を返すのではなく参照を使って値を返すからです.

matrix vs. 2次元array

Stanのモデルにおいて, 2次元arraymatrixのどちらを使うか決めるときに, 効率について2,3個考えることがあります. 一見, 2次元arraymatrixのどちらを使ってもよく思われるかもしれません. 第1に, matrixは2次元arrayよりもメモリの使用量が少ない点です. これは, matrixでは, arrayの並び方は保存せず, データと2つの次元の情報だけを保存しているためです.

第2に, matrixは「列優先」の順序でデータを格納する点です. さらに, matrix内のすべてのデータはメモリ内で隣接することが保証されます. これは最適化されたコードを考えると大切なことです. なぜなら現代のCPUを使った算術演算を実行することよりもデータをメモリからキャッシュに持っていくことの方がはるかに時間がかかるからです. 他方, arrayはプリミティブ型の値はメモリ内で隣接することを保証しており, それ以外の場合はその値のコピーを保持します(可能な限り, 参照を使って値を返します).

第3に, いずれのデータ構造もデータが保持されている順序でインデックスを移動させると最も速くアクセスできます. メモリ上の位置もアクセス速度に関係します. matrixは列優先であるため, 以下の順序でインデックスを移動させるのが適切です.

  matrix[M,N] a;
  //...
  for (n in 1:N)  //列が先
    for (m in 1:M)  //行が後
      // ... a[m,n]を使った計算...

他方, arrayは以下の例のように行優先の順序でインデックスを移動させるべきです(すなわち, 最後のインデックスが最も移動するのが速い).

  real a[M,N];
  // ...
  for (m in 1:M)  //行が先
    for (n in 1:N)  //列が後
      // ... a[m,n]を使った計算...

最初にa[m,n]を使う際には, a[m]をメモリに持ってくるように書くべきです. 一般的に, matrix内の移動は, array内の移動よりも効率が良いです. これはmatrixarrayについても同様です. 例えば, matrixの2次元arrayのインデックスを移動してアクセスするのに理想的な順序は以下になります.

  matrix[M,N] b[I,J];
  // ...
  for (i in 1:I)  //arrayなので行が先
    for (j in 1:J)  //arrayなので列が後
      for (n in 1:N)  //matrixなので列が先
        for (m in 1:M) //matrixなので行が後
          //... b[i,j,m,n] を使った計算...

amatrixの場合, a[m]と表記するとそのmatrixの行mが抽出されますが, これはmatrixを取り扱う上では非効率な操作です. もし複数のvectorにインデックスでアクセスする必要があるならば, vectorarrayを宣言する方がはるかに良いです.

  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]を使った計算  ...

同様に, 列ベクトルのarrayに対してインデックスを移動させてアクセスする方が, 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次元array

列ベクトルvectorと行ベクトルrow_vectorと1次元arrayの間にはまったく違いがありません. . Eigen:vectorテンプレートと, C++std:vectorテンプレートクラスは, double型の値のコンテナとして非常に近い形で実装されています (Stanではreal型). ただし, Stanにおいて整数値を格納できるのはarrayだけです.

5. multiple indexingとrange indexing

Stanはコンテナ(すなわち配列・vectormatrix)に対して, 整数値のインデックスの配列または範囲のインデックスを使うことで, 複数のインデックスによるアクセスを一度に行うことができます. 以降ではこの機能を「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);
}

5.1. Multiple Indexing

整数値の配列を使った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つ減った結果となります.

5.2. range indexを使ったアクセス(slicing)

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と同じになります.

5.3. 代入文の左辺でmultiple indexingを使う

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

代入の左辺と右辺で同じデータ構造を参照している際に, 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]の値が使う前に変わっているからです.

5.4. vectormatrixに対するmultiple index

mulitple indexはvectormatrixにも使うことができますし, 同じようにvectormatrixの配列にも使うことができます.

vectorの場合

multiple indexを使う場合, vectorrow_vectorは配列とまったく同じように振る舞います. もしvvectorならば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を使った次元(とインデックスを使っていない次元)がインデックスされた式の型を決めます. 上の例ではvvectorの配列なので, 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になります. したがってもしmmatrixならm[2:4]matrixです. 対照的に, m[3]のようにsingle indexを与えると結果はrow_vectorになります. すなわち, m[3]m[3, ]m[3, 1:cols(m)]と同じ結果になります.

vectormatrixの配列

matrixvectorrow_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]となります.

前の例に続けて以下を考えましょう(訳注:以降5.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]となります.

5.5. パラメータと固定値を含んだ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;
}

そして, フル行列Amodelブロックにおいてローカル変数として構築されます.

model {
  matrix[3, 3] A;
  A[ii, jj] = A_raw;
  A[1, 2] = 0;
  A[1, 3] = 0;
}

この状況ではこの方法はやりすぎに見えるかもしれません. しかし, より一般的な状況では, 固定値の要素を埋めるためにmatrixのサイズ・vectorの長さ・配列iijjや大きさを, 固定値の値とともにデータとしてコードに書くことになるでしょう. 一握りの要素が正だとわかっている場合など, アドホックな制約があるmatrixを構築するには, 似たテクニックが使われるでしょう.

6. 回帰モデル

Stanでは, 単純な線形回帰からマルチレベルの一般化線形モデルまで, 回帰モデルを扱えます.

6.1. 線形回帰

以下は最も単純な線形回帰モデルで, 1つの予測変数と, 傾きと切片の係数があり, ノイズは正規分布です. このモデルは, 標準的な回帰の記法を用いて記述できます.


yn = α + βxn + ϵn ここで ϵn ∼ Normal(0, σ)

これは, 以下のように残差を取り入れてサンプリングするのと等価です.


yn − (α + βXn) ∼ Normal(0, σ)

さらに短くなります.


yn ∼ Normal(α + βXn, σ)

このモデルの最後の形は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]とがあります. 切片と傾きのパラメータはalphabetaです. このモデルでは, スケールsigmaの, 正規分布するノイズ項を仮定しています. また, 2つの回帰係数には非正則事前分布が設定されています.

行列記法とベクトル化

前のモデルのサンプリング文はベクトル化されています.

y ~ normal(alpha + beta * x, sigma);

同じモデルの, ベクトル化されていないバージョンは以下のとおりです.

for (n in 1:N)
  y[n] ~ normal(alpha + beta * x[n], sigma);

より簡潔なことに加えて, ベクトル化された形の方がはるかに高速です. 1

Stanでは一般に, normalのような分布に渡す引数はベクトルにすることができます. いずれかの他の引数がベクトルまたは配列なら, すべて同じサイズでなくてはなりません. いずれかの他の引数がスカラーなら, その値はベクトルの各要素に再利用されます. 確率関数のベクトル化についてのより詳しい情報は37.5節を参照してください.

この書き方がうまくいく他の理由は, 行列には行列演算を行なうように, Stanの算術演算子がオーバーロードされるからです. この場合では, xvector型でbetareal型なので, 式beta * xvector型です. 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ブロックには事前分布がないので, 非負の実数の非正則事前分布ということになります. より情報のある事前分布を加えることもできますが, 正則事後分布が導ける限り, 非正則事前分布でも問題はありません.

上のモデルでは, xN × K行列の予測変数, betaK次元ベクトルの係数なので, x * betaN次元ベクトルの予測値です. 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] * betareal型のスカラーです.

1 インタープリタのPythonやRとは違って, StanはC++に変換されてコンパイルされるので, ループも代入文も高速です. ベクトル化したコードがStanで速いのは次の理由によります. (a)導関数を計算するのに使われる式木を単純にできるので, 実質的な関数呼び出しを少なくできます. (b)上のモデルのlog(sigma)のような計算は, ループ版では繰り返し実行されますが, ベクトル化すると1度だけ実行されて, その後は再利用されます.

入力に含めた切片

以下にあるモデルの定式化では, 切片の係数alphaはもうありません.

y ~ normal(x * beta, sigma);

そのかわり, 入力行列xの最初の列が, 値が1の列であると仮定しています. この方法では, beta[1]が切片の役割を果たしているのです. もし切片が, 傾きの項とは異なる事前分布を取っているなら, 違いがはっきりするでしょう. 乗算の回数が1つ減るので, 係数の変数を明示的に別にする形式よりもやや効率的でもあります. といっても, 速度にはたいした違いはでないでしょうから, これを選ぶ理由は明確さにあるでしょう.

6.2. 係数とスケールの事前分布

この節では, 回帰の係数とスケールの事前分布をモデリングするときにどのような選択肢があるか説明します. 階層モデルにおける1変量のパラメータの事前分布は6.9節で議論し, 多変量のパラメータについては6.12節で議論します. また, モデルの識別性のために使う事前分布については6.11節で議論します.

背景となる文献

スケールパラメータの事前分布の選択についての概要についてさらに知るにはGelman (2006)を参照してください. 罰則付き最尤推定値におけるスケールの事前分布の選択の概要についてはChung et al. (2013)を参照してください. 回帰係数の事前分布の選択に関する議論についてはGelman et al. (2008)を参照してください.

非正則一様事前分布

Stanはデフォルトでは, 宣言された制約で定まる範囲の値をすべて取りうる一様(あるいは「平坦」)事前分布をパラメータに設定します. したがって, 制約なしに宣言されたパラメータはデフォルトでは( − ∞,)の一様事前分布が与えられます. 一方, 下限が0と宣言されたスケールパラメータでは, (0,)の非正則一様事前分布となります. 両者の事前分布とも, 取りうる範囲全体で積分すると1になるような密度関数には定式化する方法がないという意味で非正則です.

Stanでは, 非正則事前分布でモデルを定式化することができますが, サンプリングあるいは最適化がうまくいくためには, 与えられたデータによって事後分布が正則にならなければなりません. そのためには通常, 必要最小限のデータ量がなくてはなりませんが, 最小量のデータを与えることは推定のはじめの一歩として有用なことがあります. あるいは, 感度分析(すなわち, 事前分布が事後分布に及ぼす影響を検討する)の基準としても同様です.

一様事前分布は, それが定式化された軸に依存します. 例えば, スケールパラメータσ > 0に(0,)という一様事前分布を与え, q(σ) = c(「密度」は正規化されていないものだけではなく, 正規化できないものにも使われるので, ここではqを使います)とすることもできるでしょうし, 対数軸を使って, logσに( − ∞,)という一様事前分布を与え, q(logσ) = cとすることもできるでしょう. 対数変換に必要なヤコビアンの調整のため, これらはσについて別の事前分布となります. 変数変換とそれに必要なヤコビアンの調整についてもっと知りたいときは56.1節を参照してください.

Stanは, 制約付きで宣言された変数について, 制約された範囲の値を取りうる一様密度となるように, 必要なヤコビアンの調整を自動的におこないます. このヤコビアンの調整は最適化のときには行なわれませんが, これは適切な最尤推定値を生成するためです.

正則一様事前分布: 範囲の制約

上限と下限の両方を設定することで, 正則な一様事前分布に従うような変数を宣言することも可能です. 以下はその例です.

real<lower=0.1, upper=2.7> sigma;

これはsigmaに, 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, )よりも狭くなっています. このようにすると, Stanプログラムは, 初期化が困難になったり, サンプリング中にハングしたり, ランダムウォークに陥ったりする可能性があります.

上下限付近の推定値

範囲制限のついたパラメータの境界近くに推定値があることは通常, 事前分布がモデルに合っていないことを示しています. また, サンプリングまたは最適化の際に, アンダーフローやオーバーフローといった数値的な問題を起こす可能性もあります.

「非情報」正則事前分布

回帰係数にNormal(0, 1000)のような事前分布をつけたモデルは珍しくありません. 2事前分布のスケールが, 1000のように, 推定される係数よりも数桁大きい場合は, そのような事前分布は事実上まったく何の効果も持ちません.

BUGSの例題(Lunn et al., 2012)は全般に, スケールについてのデフォルトの事前分布を下のようにしていますが, これは使わないようにしましょう.


σ2 ∼ 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より大きくなることはあまりないでしょう. この場合, Normal(0, 5)のような弱情報事前分布をそのような係数に設定するのは道理にかなっています.

計算機的にも統計学的にも, 推定を制御するのに弱情報事前分布は役に立ちます. 計算機的には, 解があると期待される量のまわりの曲率を増加させます. これにより, L-BFGSのような勾配に基づく方法でもハミルトニアンモンテカルロのサンプリングでも, 曲面の位置から遠く離れすぎたところに迷い込まないようになります. 統計学的には, 女性の平均身長のような問題で弱情報事前分布はより有効です. というのも, Normal(0, 1000)のような非常に幅の広い事前分布では, 事前分布の確率質量の大半が, 期待される答えの範囲外にあるようにされるからです. 小さなデータセットでは, そうした事前分布が推測値を覆い隠すことがありえます.

上下限のある事前分布

女性の身長の例についてもう一度考えてみましょう. 正則事前分布を定式化する方法の1つは, 上下限のあるスケールに一様事前分布を設定することです. 例えば, 女性の平均身長のパラメータは, 下限が1メートルで上限が3メートルと宣言することができるでしょう. 確かに答えはこの間にあるはずです.

同様に, スケールパラメータの事前分布の下限に0を, 上限に, 10,000のような非常に大きな数を設定する例を見ることも珍しくありません. 3これは, 幅の広い逆ガンマ分布を分散の事前分布に与えて推定するのと, おおまかに言って同じ問題をもたらします. 物理的に完全に制約があるというわけではないパラメーターは固定せずに, 情報事前分布を設定する方がよいでしょう. 女性の身長の場合なら, そのような事前分布は, メートルのスケールでNormal(2, 0.5)のようになると思われます. この場合, (1,3)の区間に確率質量の95%が集中しますが, 依然としてその範囲外の値も取りえます.

上下限のある事前分布を使う場合は, パラメータの推定値が上下限に, あるいはそれに非常に近い値になっていないか, 当てはめた事後分布を確認すべきです. そうなっていることは, 計算上の問題が発生しているだけではなく, モデルの定式化に問題があることを示しています. そのような場合, 設定した制約がないときにパラメータが当てはまると思われるところまで範囲を広げるか, あるいは境界の値を推定させないようにする事前分布を使うべきです(6.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はともに, 共分散行列や単体のような「制約のある」多変量の量については共役事前分布を使うように制限されています.

6.3. ロバストノイズモデル

線形解析の標準的な手法では, ノイズ項ϵが正規分布するとモデリングします. 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はデータとして指定します.

6.4. ロジスティック回帰とプロビット回帰

結果変数が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)} $$

上のモデルの定式化では, ベルヌーイ分布をロジットでパラメータ化したバージョンを使っています. その定義は以下です.


BernoulliLogit(y ∣ α) = Bernoulli(y ∣ logit − 1(α))

この定式化ではまたベクトル化もおこなわれています. alphabetaとがスカラーで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]));

別のリンク関数も同様に使えるでしょう. 例えば, プロビット回帰は, 正規累積分布関数を使います. これは以下のように書かれます.


Φ(x) = ∫ − ∞xNormal(y ∣ 0, 1)dy

標準正規累積分布関数Φは, StanではPhi関数として実装されています. Stanでプロビット回帰モデルをコーディングするには, ロジスティックモデルのサンプリング文を以下のように変えればよいでしょう.

y[n] ~ bernoulli(Phi(alpha + beta * x[n]));

Stanでは, 標準正規累積分布関数Φの高速な近似がPhi_approx関数として実装されています. 近似プロビット回帰モデルは以下のようにコーディングされるでしょう.

y[n] ~ bernoulli(Phi_approx(alpha + beta * x[n]));

6.5. 多項ロジット回帰

ロジスティック回帰の形式で, 結果変数が多値になるものもStanでそのままコーディングできます. 例えば, それぞれの出力の変数ynについて, 結果がK種類の値を取りうるとします. また, ynに対応する予測変数のD次元ベクトルxnがあるとします. 係数の事前分布を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関数の定義は34.11節を参照してください. 最後の行をもっと効率的にすると以下のように書けます.

y[n] ~ categorical_logit(beta * x[n]);

categorical_logit分布は, カテゴリカル分布に似ていますが, パラメータがロジットスケールになります(categorical_logitの完全な定義は39.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に固定することです. 8.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の定義は34.7節を, append_colの定義は34.10節を参照してください.

これは, パラメータとしてK次元ベクトルを使ったモデルとまったく同じというわけではありません. 事前分布が(K − 1)ベクトルにのみ適用されるようになったからです. 実用的には, これにより最尤法の解が違うものになり, 事前分布を0のまわりに中央化したときの事後分布もやや異なります. これは回帰係数でよく起こります.

6.6. 中央化されたベクトルへのパラメータ化

パラメータのベクトルβを, 合計して0になる制約を満たすという意味で中央化して定義すると便利なことがよくあります.


$$ \sum_{k=1}^{K}\beta_{k} = 0 $$

このようなパラメータのベクトルは, 多項ロジット回帰のパラメータのベクトルを識別するのに使われたり(6.5節を参照), IRTモデルの能力パラメータや難易度パラメータ(ただしどちらか一方)に使われることがあります(6.10節を参照).

K-1自由度

合計して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の事前分布に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に設定します.

柔らかい中央化

β ∼ Normal(0, σ)のような事前分布を加えることは, パラメータのベクトルβについて, $\sum_{k=1}^{K}\beta_{k}=0$となることはないものの, それに近くなるような, 1種の柔らかい中央化を設定することになるでしょう. スカラーの定数cについての要素ごとの和β + cβとが同じ尤度を生成する場合のみ, この方法でおおまかに中央化されることが保証されます(IRTモデルではおそらく, 別のベクトルαがあって, α − cと変換されます). これは, 対称な事前分布を得るためのまた別の方法です.

6.7. 順序ロジスティック回帰と順序プロビット回帰

予測変数xn ∈ ℝDに対する結果変数yn ∈ 1, …, Kの順序回帰は, 単一の係数ベクトルβ ∈ ℝDと, 切断点の数列c ∈ ℝK − 1により決まります. ただしcは, cd < cd + 1のように並んでいます. 線形予測子xnβck − 1ckの間に入るなら, 離散値の結果変数はkとなります. ここでは, c0 =  − ∞かつcK = ∞と仮定されています. ノイズ項は回帰の形式によって決まります. ここでは, 回帰ロジスティック回帰と回帰プロビット回帰の例を示します.

順序ロジスティック回帰

順序ロジスティック回帰は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);
  }
}

ロジスティックモデルも, Phiinv_logitに入れ替えれば, この方法でコーディングできるでしょう. ただし, softmax変換に基づく組込みのエンコーディングの方がより効率的で, 数値的により安定です. Phi(eta - c[k])の値を一度だけ計算して保存し, その後は保存しておいた値を再利用するようにすると, 少しだけ効率が良くなるでしょう.

6.8. 階層ロジスティック回帰

もっとも単純なマルチレベルモデルは, Lだけある離散カテゴリー(あるいはレベル)にデータがグループ化されるような階層モデルです. 極端な方法は, 全データを完全にプールして, 回帰係数βのベクトルを共通のものとして推定するというものでしょう. また反対側に極端な方法は, プールはせず, 各レベルlに固有の係数ベクトルβlを割り当て, 他のレベルとは別に推定するというものでしょう. 階層モデルは中間の解法で, プールの程度は, データと, プールの量についての事前分布とで決まります.

2値の結果変数yn ∈ 0, 1がすべて, レベルlln ∈ 1, …, Lと関連しているとします. 各結果変数はまた, 予測変数のベクトルxn ∈ ℝDとも関連しているでしょう. 各レベルlは, 固有の係数ベクトルβl ∈ ℝDをとります. 階層モデルでは, これもデータから推定される事前分布から係数βl, d ∈ ℝ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, ∞)を取ります. Stanでは, 事後分布が正則である限り, 非正則の事前分布を許します. とはいっても通常は, すべてのパラメータに情報事前分布, あるいは少なくとも弱情報事前分布をつけるのが有用です. 回帰係数とスケールの事前分布についてのおすすめは6.2節を参照してください.

モデルの最適化

可能なところでは, サンプリング文をベクトル化すると, 対数確率と導関数の評価が速くなります. 高速化の理由は, ループがなくなったからではなく, 対数確率と勾配計算の下位計算がベクトル化により共有されること, 勾配計算に必要な式木のサイズが減少することによります.

まず最初の最適化として, Dについてのforループをベクトル化します.

  mu ~ normal(0,100);
  for (l in 1:L)
    beta[l] ~ normal(mu,sigma);

betaはベクトルの配列として宣言されていますので, 式beta[l]はベクトルを示すことになります. betaを行列として宣言することもできたでしょうが, ベクトルの配列(あるいは2次元配列)の方が行へのアクセスはより効率的です. 配列, ベクトル, 行列の間での効率性のトレードオフについては第4章にさらに情報があります.

このモデルは, ベルヌーイ分布内で逆ロジットを使用しているのを, ロジットでパラメータ化したベルヌーイ分布に置き換えることで, さらに高速化し, 算術的にもより安定させることができます.

for (n in 1:N)
  y[n] ~ bernoulli_logit(x[n] * beta[ll[n]]);

bernoulli_logitの定義は38.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ブロックの最初で変数を宣言しても構いません.

上のように局所変数への代入を使うと, モデルが読みにくくなる場合があります. そのような場合には, まず読みやすいバージョンでモデルを開発, デバッグし, 単純な定式化でデバッグし終わってはじめて最適化の作業にかかるというようにするのがおすすめです.

6.9. 階層事前分布

事前分布の事前分布は「超事前分布」とも呼ばれます. 超事前分布も, 下位レベルのパラメータの事前分布と同様に扱われるべきです. すなわち, 利用可能なだけの事前分布の情報がすべて使われるべきです. 超事前分布は, ほんの少数の下位レベルのパラメータにしか適用されないことが多いので, 事後分布が正則であることと, 事前分布の裾の重さに統計的にも計算的にも過度に敏感ではないことの両方が確かかどうか注意を払う必要があります.

階層モデルのMLEで, 境界の値を推定させないようにするための事前分布

階層モデルの設定における最尤推定(MLE)の基本的な問題は, 階層事前分布の分散が小さくなって, 階層事前分布の平均のまわりに値が集まり, 全体の密度が限度なく大きくなることです. 例として, xn ∈ ℝKについてのyn ∈ ℝの単純な階層線形回帰(事前分布の平均を固定)を考えます. 定式化は以下のとおりです.


$$ \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} $$

この場合で, τ → 0かつβk → 0となるとき, 事後密度


p(β, τ, σ ∣ y, x) ∝ p(y ∣ x, β, τ, σ)

は限度なく大きくなります. 図21.1にNealのじょうご密度の図がありますが, これと同様の挙動を示します.

この場合明らかに, β, τ, σに最尤推定値はありません. したがって, 事後の最頻値を推測に使うなら, モデルを変えなければなりません. Chung et al. (2013)が勧めているのは, 以下のように事前分布にガンマ分布を使用する方法です.


τ ∼ Gamma(2, 1/A)
(訳注: 原文では'σ'ですが, 'τ'の誤りと思われます)

Aには, A = 10のようにかなり大きな値を与えます.

6.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) モデル

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);
}

このモデルはロジットでパラメータ化したベルヌーイ分布を使っています.


bernoulli_logit(y ∣ α) = bernoulli(y ∣ logit − 1(α))

このモデルを理解する鍵は, bernoulli_logit分布の中の項です. 以下の式に従います.


Pr[yn = 1] = logit − 1(αjj[n] − βkk[n] + δ)

このモデルでは, 事前分布を設定しないと加法的な識別可能性の問題が発生します. 例えば, αjβkの両方にξを加えると, 予測値が同じになります. αβに平均を0とした事前分布を設定すると, パラメータが識別できます. 識別可能性の問題と, 識別可能にする他の手法についてはGelman and Hill (2007)を参照してください.

テスト用に, Stanとともに配布されているIRT 1PLモデルは, Rでデータをシミュレートするために使われる実際のデータ生成過程に合うような情報のある事前分布を使っています(このシミュレーションのコードはモデルと同じディレクトリに入っています). 実際に利用するにはほとんどの場合は現実的ではありませんが, Stanの推定が有効であることは分かります. 事前分布の裾を重くして簡単な感度分析を行なうと, 400人の生徒に100問の問題で, 25%だけランダムに欠測するとしても, 事後分布は事前分布にかなり敏感です. 実際に使用するときには, 他のパラメータと同様に事前分布も階層的にすべきです. これは次の節で記述します.

5 Gelman and Hill (2007)はδ項を, 生徒の能力の分布における位置のパラメータと等価に扱っています.

マルチレベル2PLモデル

前の節で記述した単純な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に標準正規分布の事前分布を与えるようなモデルに, ここではパラメータ化しています. これは, このパラメータの位置とスケールの両方を識別可能にするためです. そうしなければ, その両方が識別不可能になるでしょう. 識別可能性については20章でさらに議論します. このモデルでは, 難易度と識別性のパラメータbetagammaのスケールはパラメータとし, 階層的に定めています. betagammaには以下のように階層的ではない, 弱情報事前分布を与えてもよいでしょう.

beta ~ normal(0,5);
gamma ~ lognormal(0,2);

ポイントは, alphaがスケールと位置を決定し, betagammaは変動できるようにしていることです.

パラメータbetaには, ここでは中央化しないパラメータ化を行なっています. パラメータmu_betabetaの平均の位置を与えています. あるいはまた, 以下のようにもできるでしょう.

beta ~ normal(mu_beta, sigma_beta);

および

y[n] ~ bernoulli_logit(gamma[kk[n]] * (alpha[jj[n]] - beta[kk[n]]));

中央化しないパラメータ化は階層モデルではより効率的になる傾向にあります. 中央化しない再パラメータ化についてもっと知るには21.2節を参照してください.

切片の項mu_betaはそれ自身は階層的にモデリングできません. そのため, 弱情報事前分布Cauchy(0, 5)を与えています. 同様に, スケールの項sigma_alphaおよびsigma_beta, sigma_gammaには半コーシー分布の事前分布を与えています. 半コーシー分布の切断は暗黙のものです. 明示的な切断は必要ありません. これは, 対数確率は割合だけ計算すればよく, スケールの変数は宣言により(0,)に制約されているからです.

6.11. 識別可能性のための事前分布

位置とスケールの固定

(階層)事前分布の応用の1つに, パラメータ群のスケールあるいは位置, またはその両方を識別させるということがあります. 例えば, 前の節で議論したIRTモデルでは, 位置とスケールの両方に識別不能性がありました. 一様事前分布を使うと, スケールと位置の両方の項でパラメータは変動するでしょう. これが推定にもたらす問題については, 20.1節に簡単な例がありますので, 参照してください.

生徒の能力のような1群の係数に標準正規分布(すなわちNormal(0, 1))の事前分布をを与えると, 識別不能性は解決されます. 生徒の能力に標準正規分布の事前分布を与えると, IRTモデルは識別され, 生徒の能力のパラメータについての1群の推定値が事後分布により生成されます. その標本平均は0に近く, 標本分散は1に近い値となるでしょう. 問題の難易度と識別性のパラメータには, 広がった, 理想的には階層的な事前分布を与えるべきです. そうすると, 生徒の能力のパラメータとの相対的な位置とスケールにより, これらパラメータは識別されるでしょう.

共線性

事前分布により識別可能になる別の例として, 線形回帰における共線性の場合があります. 線形回帰では, 2つの予測変数が共線的(すなわち一方が他方の線形関数となっている)ならば, 事後分布ではそれらの係数の相関係数は1(または-1)でしょう. これは識別不能になります. 係数の事前分布を正規分布とすることで識別できるようになる場合があります. そのような事前分布を使うと, 例えば, 全く同じ値をもつ説明変数が2つある場合に(共線性があることは明らかです), 最も尤度が高くなるような2つの係数の値は, 片方の説明変数だけを使って推定して得た値の半分の値となります.

分離可能性

ロジスティック回帰では, 結果変数の値が1で予測変数が正のときや, 結果変数の値が0で予測変数が負のときには, こうした予測変数の係数の最尤推定値は無限に発散します. 係数に事前分布を与えることでこうした発散を制御することができます. これにより, 推定値を0に向けて「縮小」することで, 事後分布についてモデルは識別可能になるでしょう.

同様の問題は非正則平坦事前分布からのサンプリングでも発生します. このときのサンプラーは非常に大きな値を抽出しようとするでしょう. 事前分布を与えることで, 事後分布は有限の値のまわりに集中し, サンプリングはうまくいくでしょう.

6.12. 階層モデルにおける多変量の事前分布

階層回帰モデルでは(他の状況でもありますが), いくつかの個体レベルの変数に階層事前分布を割り当てることがあります. 例えば, 複数の変動する切片や傾きを含むモデルでは, 多変量の事前分布を割り当てることでしょう.

例として, 人を個体レベルとして, 収入を結果変数に, 教育水準と年齢を予測変数とし, 州などの地理区分を群としましょう. 切片のほか, 教育水準と年齢の効果も州ごとに変わりうるとします. さらに, 州ごとの収入や失業水準の平均といった, 州レベルの予測変数もあるとしましょう.

多変量回帰の例

Gelman and Hill (2007)の13章・17章では, N個体がJ群にまとめられる階層モデルについて議論しています. 各個体は, 長さKの列ベクトルxnからなる予測変数を持ちます. 記法の統一のため, xn, 1 = 1として, 切片を「1に固定された予測変数」にかかる係数として扱います. 群への所属をコード化するため, 個体nは群jj[n] ∈ 1 : Jに属するとしています. 各個体nはまた, 実数値の観測結果ynを持ちます.

尤度

このモデルは, 群によって切片と係数が変動する線形回帰となりますので, βjは, 群jについてのK次元ベクトルの係数です. 個体nについての尤度関数は以下のとおりです.


yn ∼ Normal(xnβjj[n], σ) すべてのn ∈ 1 : Nについて

係数の事前分布

GelmanとHillは, 係数ベクトルβjを, 平均ベクトルμと共分散行列Σを持つ多変量分布から抽出されるとモデリングしています.


βj ∼ MultiNormal(μ, Σ) すべてのj ∈ 1 : Jについて

以下, GelmanとHillのフルモデルについて議論しますが, このモデルではμをモデリングするのに群レベルの予測変数を使っています. ここでは, μは単純なベクトルのパラメータと仮定します.

超パラメータ

階層モデリングでは, 群レベルの平均ベクトルμと共分散行列Σ自身に事前分布を与えなくてはなりません. 群レベルの平均ベクトルには, 独立した係数に合理的な弱情報事前分布を与えることができます.


μj ∼ Normal(0, 5)

もちろん, 係数βj, kの期待値について知識があれば, その情報をμkの事前分布に取り入れることができます.

共分散行列の事前分布についてGelmanとHillは, 対角成分の標準偏差のスケールを分離した逆Wishart分布を使うことを提案しています. それを選ぶ理由は主として, 多変量の尤度関数に対して共役であり, Gibbsサンプリングが単純になるという利便性にあります.

Stanでは, 多変量の事前分布に共役の制限はありませんし, 実際のところやや違った手法をお勧めしています. GelmanとHillと同様, 事前分布をスケールと行列に分解しますが, 実際の変数のスケールと相関行列に基づいた, もっと自然な方法でできます. 具体的には以下のように定義します.


Σ = diag_matrix(τdiag_matrix(τ)

ここでΩは相関行列で, τは係数のスケールのベクトルです.

スケールのベクトルτの要素には, スケールの事前分布として合理的ならどのようなものでも与えることができますが, 下の, 小さいスケールの半コーシー分布のような弱情報のものをお勧めします.


τk ∼ Cauchy(0, 2.5) すべてのk ∈ 1 : Jについて, かつτk > 0

事前平均については, 群間の係数の変動のスケールについて情報があるなら, τの事前分布に取り入れるべきでしょう. 交換可能な係数が多数あるときは, τ自体の要素自体に(おそらく切片は除いて)階層事前分布が与えらえるでしょう.

最後に, 相関行列Ωには, 形状パラメータν > 1のLKJ事前分布を与えることをお勧めします.


Ω ∼ LKJcorr(ν)

LKJ相関分布は51.1節で定義していますが, モデリングの基本的なアイデアは, νが大きくなるにつれ, 事前分布が単位相関行列のまわりに集中するようになる(すなわちβjの要素間の相関を低くする)ということです. ν = 1のとき, LKJ相関分布は相関行列に対して一様分布(訳注: 原文では'identity distribution'ですが, 'uniform distribution'の誤りと思われます)を設定するのと等価になります. したがって, LKJ事前分布は, パラメータβj間の相関について期待する量を制御するのに用いることがあります.

事前平均についての群レベルの予測変数

GelmanとHillのモデルを完成させるため, 各群j ∈ 1 : Jには, 群レベルの予測変数ujL次元の行ベクトルを与えるとします. するとβjの事前平均は, L次元の係数ベクトルgammaを使って, それ自体を回帰としてモデリングできます. 群レベルの係数の事前分布は以下のようになります.


βj ∼ MultiNormal(ujγ, Σ)

群レベルの係数gammaには, それ自体に弱情報事前分布を独立に与えてもよいでしょう.


γl ∼ Normal(0, 5)

いつもと同様に, 群レベルの平均についての情報は事前分布に取り入れるべきでしょう.

Stanでのモデルのコーディング

群レベルの係数についての多変量の事前分布と, 群レベルの事前平均を与えた完全な階層モデルの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を使うバージョンの方が高速のはずです. 特別な行列演算についてさらに知るには34.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);
}

6.13. 予測, フォアキャストとバックキャスト

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から取り除かれ, 正規分布から擬似乱数を使って抽出されるようになっています.

6.14. 多変量の結果変数

ほとんどの回帰の設定では, 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} $$

ここで, xnJ次元の予測変数の行ベクトル(x(N × J)行列です), ynK次元の観測値のベクトル, βは回帰係数の(K × J)行列(ベクトルβkは, 結果変数のベクトルのk番目の要素に関する係数を収容します), Σは誤差を決める共分散行列です. いつものように切片は, 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);
}

効率性のため, 平均のベクトルの配列を前もって計算し, 同じ共分散行列は共有して, 多変量正規分布はベクトル化しています.

6.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ブロックで, コレスキー因子から完全な相関または共分散行列を再構築してもよいでしょう.

多変量プロビット回帰

多変量プロビットモデルは, 見かけ上無関係な回帰の結果変数に階段関数を適用することにより一連の論理値の変数を生成します.

観測値ynは, 論理値(0を偽, 1を真とコーディングします)からなるD次元ベクトルです. ynの値は, 見かけ上無関係な回帰モデル(前の節を参照)から抽出される潜在変数znによって決まります.


$$ \begin{array}{rl} z_{n} &= x_{n}\beta + \epsilon_{n}\\ \epsilon_{n} &\sim \mathsf{MultiNormal}(0, \Sigma) \end{array} $$

次に, 階段関数を適用して, 以下で定義される要素からなる論理値のD次元ベクトルynを生成します. (訳注: 原文では'K-vector znとありますが, 誤りと思われるので上のように修正しています. 以下の式でもkdに修正しています. )


yn, d = I(zn, d > 0)

ここで, I()は指示関数で, 引数が真なら1の, そうでなければ0の値をとります.

見かけ上無関係な回帰モデルの場合とは異なり, ここでは共分散行列Σは単位標準偏差をとります(すなわち, 相関行列になります). 通常のプロビット回帰やロジスティック回帰と同様に, スケールが変動するとモデル(スケールではなく, 0についての切断点によってのみ定義されています)が識別不能になります(Greene (2011)を参照).

多変量プロビットモデルはStanでは, Albert and Chib (1993)によって導入されたトリックを使ってコーディングできます. この方法では, 基礎となる連続値のベクトルynが切断パラメータとしてコーディングされます. 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_posN_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);
}

もちろん, 前の節の見かけ上無関係な回帰でも同じことができるでしょう.

7. 時系列モデル

時系列データは, 時間軸に沿って得られるデータです. この章では2種類の時系列モデルを紹介します. 1つは, 自己回帰および移動平均モデルといった回帰に似たモデル, もう1つは隠れマルコフモデルです.

15章ではガウス過程を紹介しますが, これも時系列(と空間)データに使えるでしょう.

7.1. 自己回帰モデル

正規ノイズの1次自己回帰モデル(AR(1))では, 各点ynは次式のように生成される系列yにまとめられます.


yn ∼ Normal(α + βyn − 1, σ)

すなわち, ynの期待値はα + βyn − 1で, ノイズの大きさはσです.

AR(1)モデル

傾き(β), 切片(α), ノイズのスケール(σ)のパラメータに非正則平坦一様分布を設定するなら, 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を掛けるのにはベクトル計算を使っています.

AR(1)モデルの拡張

回帰係数とノイズのスケールには, さまざまな別の分布族の正則事前分布を設定することもできるでしょう. 正規分布ノイズのモデルは, スチューデントのt分布はじめ, 上下限のない分布に変えることができます. 複数の観測系列があるようなら, このモデルは階層的にもできるでしょう.

強制的に定常AR(1)過程として推定するなら, 傾きの係数betaには以下のように上下限に制約をつけてもよいかもしれません.

real<lower=-1,upper=1> beta;

実際には, こうした制約はおすすめしません. データが定常でないなら, モデルのあてはめのときにそれがわかるというのが最善です. betaの値が零近辺に集まるような事前分布を設定することで, 定常パラメーターが推定しやすくなるでしょう.

AR(2)モデル

このモデルの次数を拡張するのも簡単です. 例えば, 2次の係数gammaを入れて, 以下のモデル文のようにAR(2)モデルをコーディングできるでしょう.

for (n in 3:N)
  y[n] ~ normal(alpha + beta*y[n-1] + gamma*y[n-2], sigma);

AR(K)モデル

次数自体もデータとして与えるような一般モデルは, 係数を配列に入れ, 線形予測子をループ内で計算させるようにしてコーディングできます.

data {
  int<lower=0> K;
  int<lower=0> N;
  real y[N];
}
parameters {
  real alpha;
  real beta[K];
  real sigma;
}
model {
  for (n in (K+1):N) {
    real mu;
    mu <- alpha;
    for (k in 1:K)
      mu <- mu + beta[k] * y[n-k];
    y[n] ~ normal(mu, sigma);
  }
}

ARCH(1)モデル

計量経済学と財政学の時系列モデルでは不等分散を仮定するのが普通です(すなわち, 系列を定義するノイズ項のスケールが時間的に変化してもよいとします). そのようなモデルで最も単純なのがARCH(AugoRegressive Conditional Heteroscedasticity, 自己回帰条件付き不等分散)モデルです(Engle, 1982). 自己回帰モデルAR(1)では, 系列の平均が時間的に変化しますが, ノイズ項は固定されたままです. ARCH(1)モデルでは, これとは異なり, ノイズ項のスケールが時間的に変化する一方で平均項は固定されたままです. もちろん, 平均もスケールも時間的に変化するとモデルを定義することもできるでしょう. 計量経済学の文献には多種多様な時系列モデリングの選択肢があります.

ARCH(1)モデルは典型的には以下の一連の式で示されます. ここで, rtは時点tにおける収益の観測値, μ, α0, α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}$$

ノイズ項σt2が正であることを保証するため, α0, α1 > 0と, スケール係数は正に制約されています. 時系列の定常性を保証するため, α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実際には, この制約を外してみて, 非定常な係数の組み合わせの方がデータによく当てはまるかどうかを試すのが有用なこともあります. あるいはまた, 当てはめから外れているトレンドがあるなら明らかに非定常でしょうから, モデルにトレンド項を加えることもあります.

7.2. 時間的不等分散性のモデリング

一揃いの変数について, 分散がすべて同じなら, 等分散ということなります. 一方, 分散がすべて同じというわけでないなら, 不等分散ということになります. 不等分散の時系列モデルでは, ノイズ項が時間的に変化してもよいとします.

GARCH(1,1)モデル

基本的なGARCH(Generalized AutoRegressive Conditional Heteroscedasticity, 一般化自己回帰条件付き不等分散)モデルであるGARCH(1,1)はARCH(1)モデルを拡張したもので, 一期前の時点t − 1での収益の平均との差の2乗を, 時点tのボラティリティの予測変数に含みます.


σt2 = α0 + α1at − 12 + β1σt − 12

スケール項が正であることと時系列が定常であることを保証するため, 係数については, α0, α1, β1 > 0, かつ傾きについてα1 + β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行にまで減りました. rsigmaの長さはTですので, すべてのデータが直接モデル化されています.

7.3. 移動平均モデル

移動平均モデルは, 過去の誤差を将来の結果の予測変数として使います. 次数Qの移動平均モデルMA(Q)には, 全体的な平均パラメータμと, 過去の誤差項についての回帰係数θqがあります. 時点tにおける誤差をϵtとして, 結果ytについてのモデルは次のように定義されます.


yt = μ + θ1ϵt − 1 + … + θQϵt − Q + ϵt

結果ytについての誤差項ϵtは正規分布としてモデル化されています.


ϵt ∼ Normal(0, σ)

正則なベイズモデルでは, μ, θ, σにはすべて事前分布を与える必要があります.

MA(2)の例

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);
}

誤差項ϵtは, 観測値とパラメータを使って変換パラメータ(transformed parameter)として定義されています. (尤度を定義する)サンプリング文の定義もこれと同じ定義を使っていますが, n > Qynにのみ適用可能です. この例では, パラメータにはコーシー事前分布(σには半コーシー分布)を与えています. もっとも, ほかの事前分布も同じくらい簡単に使えます.

modelブロック中のサンプリング文をベクトル化すると, このモデルはもっと速くできるでしょう. ループの代わりにドット乗算を使ってϵtの計算をベクトル化しても高速化できるでしょう.

ベクトル化したMA(Q)モデル

サンプリング確率をベクトル化した一般的な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);
}

ここではすべてのデータがモデル化されています. 不足する項は単に, 誤差項の計算の際に回帰から除かれます. 両方のモデルともとても速く収束し, 収束した連鎖はよく混ざっています. ベクトル化したモデルの方がちょっとだけ速いのですが, これは繰り返しあたりについてのことで, 収束についてではありません. というのも両者は同じモデルだからです.

7.4. 自己回帰移動平均モデル

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 を参照してください.

7.5. 確率的ボラティリティモデル

確率的ボラティリティモデルは, 離散時間の潜在確率過程に従って, 証券購入のオプションのような資産収益のボラティリティ(すなわち分散)を扱います(Kim et al., 1998). データは, T個の等間隔時点における原資産に対する平均修正(すなわち中央化)収益ytからなります. Kimらは, 以下の回帰に似た式を使って典型的な確率ボラティリティモデルを定式化しています. ここで, 潜在パラメータhtは対数ボラティリティを, パラメータμは平均対数ボラティリティを, ϕはボラティリティ項の持続性をしめします. 変数ϵtは時点tにおける資産収益に対するホワイトノイズショック(すなわち乗法的誤差)で, δ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}$$

最初の行を変形すると, ϵt = ytexp( − ht/2)となり, ytのサンプリング分布は以下のように書けます.


yt ∼ Normal(0, exp(ht/2))

ht + 1についての再帰式には, δtのスケーリングとサンプリングを組み合わせることができて, 次のサンプリング分布が得られます.


ht + 1 ∼ Normal(μ + ϕ(ht − μ), σ)

この定式化はそのままコーディングできて, 以下の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のモデルではパラメータϕ, σ, μに事前分布を与えています. ショック項ϵtδtはモデル中には明示的には現れないことに注意してください. とはいえ, generated quantitiesブロックで効率的に計算することは可能でしょう.

このような確率的ボラティリティモデルの事後分布で事後分散が大きくなるのは普通のことです. 例えば, μ =  − 1.02, ϕ = 0.95, σ = 0.25として上のモデルで500データ点のシミュレーションを行なうと, 95%事後区間はμが(-1.23,-0.54), ϕが(0.82,0.98), σが(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_stdNormal(0, σ)という分布になるようにリスケールして, これを一時的にhに代入しています. 2番目の代入では, h[1]の事前分布がh[2]からh[T]までの事前分布とは異なるようにh[1]をリスケールしています. その次の代入ではmuにオフセットをつけており, これによりh[2]からh[T]までがNormal(μ, σ)という分布になります. この移動はh[1]のリスケーリングの後に行なう必要があることに注意してください. 最後のループは, h[2]からh[T]までがphimuと比較して適切にモデル化されるように移動平均に加算します.

最後の改良として, h[1]のサンプリング文と, h[2]からh[T]のサンプリングのループを, 1行のベクトル化した標準正規分布のサンプリング文に置き換えます.

model { ...
  h_std ~ normal(0,1);

元のモデルでは, 数百から時には数千回の繰り返しが収束に必要となることがありますが, 再パラメータ化したモデルでは数十回の繰り返しで信頼できる収束に至ります. 連鎖の混ざり具合も劇的に改善され, 繰り返しあたりの有効サンプルサイズも多くなります. 最後に, 各繰り返しの時間は元のモデルのおおよそ4分の1になります.

7.6. 隠れマルコフモデル

隠れマルコフモデル(HMM: Hidden Markov Model)は, T個の出力変数ytからなる系列を, これに並行する, 潜在カテゴリカル状態変数zt ∈ {1, …, K}の系列を条件として生成します. 与えられたzt − 1について, ztが他の変数と条件付き独立であるように, この「隠れ」状態変数はマルコフ連鎖を形成すると仮定します. このマルコフ連鎖は推移行列θによりパラメータ化されます. ここで, θkはK次元単体(k ∈ {1, …, K}です. 状態zt − 1から状態ztへの推移確率は次式のようになります.


zt ∼ Categorical(θz[t − 1])

時点tにおける出力ytは潜在状態ztに基づいて条件付き独立に生成されます.

この節では, 出力yt ∈ {1, …, V}に単純なカテゴリカルモデルを適用してHMMを記述します. 潜在状態kについてのカテゴリカル分布はV次元単体ϕkによりパラメータ化されます. 時点tにおける出力の観測値ytは, 時点tにおける隠れ状態のインジケータztに基づいて生成されます.


yt ∼ Categorical(ϕz[t])

つまり, 混合成分のインジケータが潜在マルコフ連鎖を形成するような離散混合分布モデルをHMMは形成しています.

教師付きパラメーター推定

隠れ状態が既知の状況では, パラメータθおよびϕの当てはめのため, 以下のナイーブモデルを使うことができます. 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]]);
}

θkϕkには明示的なディリクレ事前分布を与えています. この2文を除くと, 有効な単体全体についての一様事前分布が暗黙のうちに使われることでしょう.

3このプログラムは, Stan例題モデルのリポジトリで入手できます. https://github.com/stan-dev/example-models/tree/master/misc/gaussian-process を参照してください.

開始状態と終了状態の確率

動くことは動きますが, 上のようにHMMを記述しても完全ではありません. 開始状態z1がモデル化されていないからです(添字は2からTまでです). データを長い過程の部分系列と考えるなら, z1の確率は, マルコフ連鎖の定常状態確率に合わせるべきでしょう. この場合, データには明確な終了がなく, 系列がzTで終了する確率をモデル化する必要はありません.

もうひとつ別の概念として, HMMは有限長の系列のモデルとして考えることもできます. 例えば, 自然言語の文には明確な開始の分布(通常は大文字)と, 終了の分布(通常は何らかの句読点)とがあります. 文の境界をモデル化する最も単純な方法は, 新しい潜在状態K + 1を加え, パラメータのベクトルθ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]と, 潜在状態が時点tkに等しいことの対数周辺確率であるとして定義されます. このとき, その前の潜在状態は周辺化消去されます. 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の各事前確率を掛け合わせるだけです. ただし, 対数スケールにおいて算術的に安定な方法で行います.

中括弧で, 局所変数accgammaのスコープを示しています. もっと前に宣言することもできたのですが, 使用される場所の近くに宣言を置いておく方がわかりやすいでしょう.

予測推論

推移パラメータθk, kおよび出力パラメータϕk, v, それに観測系列u1, …, uT ∈ 1, …, 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を生成したものとして最も可能性の高い状態を見つけるのにパラメータが使われています.

8. 欠測データと部分的に既知のパラメータ

ベイズ推定では欠測データに対してごく一般的な手法を使えます. すなわち, 欠測データの項目すべてが, 事後分布で推定されるパラメータとして表されます(Gelman et al., 2013). 欠測データが明示的にモデル化されないのであれば, たいていの回帰モデルの予測変数でそうですが, 結果は, 欠測している予測変数を表すパラメータに非正則事前分布を設定することになります.

観測データと欠測データを配列に混ぜる方法はStanに取り入れるのは難しいことがあります. これは一部には, 離散の未知量をモデル化する方法がStanではトリッキーなことがあることによりますし, また一部には, ほかのいくつかの統計言語(例えば, RやBUGS)とは違ってStanでは, 観測量と未知量はモデル中の別の場所で定義する必要があることによります. そのため, Stanプログラムでは, データ構造中にある観測部分と欠測部分とを組み合わせて一緒にするコードを含める必要がある場合があります. 例はこの章でこの後から紹介します.

8.1. 欠測データ

Stanでは, datatransformed 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_obsN_misにデータとしてコーディングされています. 観測データは, array型のデータの変数y_obsとして与えられています. 欠測データはarray型のパラメータy_misとしてコーディングされています. 通常のパラメータは推定されるので, 位置muとスケールsigmaもパラメータとしてコーディングされています. このモデルをもっとうまく書く方法はベクトル化することです. そうすると本体は次のようになります.

  y_obs ~ normal(mu,sigma);
  y_mis ~ normal(mu,sigma);

このモデルには, 観測データと欠測データにそれぞれ1つのループがあります. これはモデル指定としていささか冗長ですが, そのおかげで, Stanでの欠測データ問題としては, 次の節で記述するもっと一般的なテクニックよりもはるかに効率的なサンプリングが可能になります.

1もっと意味のある推定の例は, 予測変数を使った観測された観測値と欠測の観測値の回帰を含むものでしょう. 観測された観測値に対応する予測変数も欠測の観測値に対応する予測変数も既知で, dataブロックで指定されているものです.

8.2. 部分的に既知のパラメータ

多変量の確率関数で, 一部の結果やパラメータだけが観測されているといったような状況では, 既知量(データ)と未知量(パラメータ)とを混ぜたベクトルを作る必要があります. 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回だけです.

8.3. 効率性についての注意

はじめの節の欠測データの例は, 次の節の, 部分的に既知のパラメータの例の方法と同じように, データとパラメータとを混ぜた配列としてプログラミングすることもできるでしょう. 正しく動くでしょうが, 計算には無駄が多くなります. parametersあるいはtransformed parametersブロックで宣言される各パラメータは自動微分の変数を使います. 単純なデータの変数と比較すると, 記憶容量と勾配計算の時間の点でこれはより高くつきます. さらに, そのコピーが余分な容量と余分な時間を使います.

8.4. 因子分析の負荷行列

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調整が必要となるでしょうから, まったく便利ではないでしょう. 正確な調整は, 共分散行列を扱う56.1節の小節で示します.

9. 切断あるいは打ち切りデータ

測定値に切断や打ち切りがあるデータは, Stanでは以下のそれぞれの確率モデルのようにコーディングできます.

9.1. 切断分布

Stanで切断を使える分布は, 1変量分布で, 対応する対数累積分布関数(cumulative distribution function: CDF)と対数相補累積分布関数(complementary cumulative distribution function: ccdf)があるものに限られています. 切断分布とcdf, ccdfについてもっと知るには, 26.3節の切断分布の項を参照してください.

9.2. 切断データ

切断データとは, ある下限より上か, 上限より下か, あるいは上下限の間にある測定値しか報告されないようなデータです.

切断データはStanでは切断分布を使ってモデリングできるでしょう. 例えば, 切断データynは, yn < 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になります. したがって対数確率は − ∞と評価されるでしょう. 例えば, 変量yが次の文でサンプリングされているとします.

for (n in 1:N)
  y[n] ~ normal(mu,sigma) T[L,U];

このとき, y[n]の値がLの値より小さいか, Uの値よりも大きければ, サンプリング文は確率0の推定値を生成します. ユーザー定義の切断では, このように切断範囲外では0とするように明示的に処理しなければいけません.

変数が切断範囲外にそれるのを防ぐには, 適切な制約が必要です. 例えば, yが上のモデルのパラメータならば, LUの値の間になるように宣言で制約をつけるべきです.

parameters {
  real<lower=L,upper=U> y[N];
  ...

上のモデルで, LUがパラメータで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の最小値以下と宣言されています. また, 上方の切断点Uyの最大値以上と宣言されています. この宣言はデータに依存していますが, データが両切断点の間にあるという制約しか課していません. ただし, Nint<lower=1>という型で宣言されていますので, 少なくとも1つのデータ点があるはずです. LUよりも小さいという制約は, 空ではないデータによって間接的に保証されるのです.

下限Lと上限Uの事前分布については省略していますが, このモデルにおいてLmin(y)のまわりに強く集中したりUmax(y)のまわりに強く集中したりしないように, 情報のある事前分布を設定すべきでしょう.

9.3 打ち切りデータ

打ち切りデータとは, 点の値が大きすぎたり, 小さすぎたり, あるいはその両方の場合で, 値が不明になるようなデータです. 切断データと異なるのは, 打ち切られたデータ点の数が分かっていることです. 教科書に載っている例としては, 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)$$

ここで, Φ()は標準正規分布の累積分布関数です. 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)$$

ここで, 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));
}

10. 有限混合分布

結果変数が有限混合分布となるモデルは, 結果変数が複数の分布のうちのひとつから抽出され, どの分布から抽出されるかは, 混合させるカテゴリカルな分布により制御されると仮定します. 混合モデルは通常, 多峰の分布となり, その山は混合成分の最頻値に近くなります. 混合分布モデルはいくつかの方法でパラメータ化できます. 以下の節でそれを記述します.

10.1. 潜在離散値のパラメータ化

混合分布モデルをパラメータ化する方法の1つは, 結果変数を負担する混合成分を示す潜在カテゴリカル変数を使うことです. 例えば, K個の正規分布があり, その位置はμk ∈ ℛ, スケールはσk ∈ (0, ∞)とします. ここで, これらを割合θで混合させるとします. θk ≥ 0かつ$\sum_{k=1}^{K}\theta_{k}=1$, すなわちθK次元単体です. 各結果変数ynには潜在変数zn ∈ 1, …, Kがあり, これはθによりパラメータ化されるカテゴリカル分布に従うとします.


zn ∼ Categorical(θ)

変数ynは, 混合成分znのパラメータに従って分布します.


yn ∼ Normal(μz[n], σz[n])

離散パラメータznがあるので, Stanではこのモデルは直接扱うことができませんが, 次の節で記述するようにzパラメータを総和で消去することによりμσをサンプリングできます.

10.2. 負担率パラメータを総和で消去

前の節であらましを見た混合正規分布モデルは, 総和により離散パラメータをモデルから消去することによりStanで実装できます. YK個の正規分布の混合分布で, 正規分布のが位置μk, スケールσk, K次元単位単体に従う割合θで混合される場合は, 次式のように表されます.


$$p_{Y}(y \mid \theta,\mu,\sigma) = \sum_{k=1}^{K}\theta_{k}\mathsf{Normal}(\mu_{k},\sigma_{k})$$

10.3. 指数の和の対数: 対数軸での線形の和

指数の和の対数関数は, 対数軸での混合分布を定義するのに使われます. 2つの入力値の場合は以下のように定義されます.


log_sum_exp(a, b) = log(exp(a) + exp(b))

abが確率を対数軸にしたものなら, 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になり, 他方は負になります. これにより, 主項でオーバーフローやアンダーフローが発生する可能性をなくし, 演算での算術精度を可能な限り確保します.

例として, Normal( − 1, 2)Normal(3, 1)とが確率θ = (0.3, 0.7)で混ざる混合分布は, 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個のデータ点があります. 混合割合のパラメータthetaK次元単位単体として宣言されており, 成分の位置のパラメータmuとスケールのパラメータsigmaはともに大きさKの配列として定義されています. スケールの配列sigmaの値は非負に制約されており, このモデルでは弱情報事前分布を与えています. モデル中で, 大きさKの局所配列変数psが宣言されており, 混合成分からの寄与を積算するのに使われています.

これは例題なので, 位置とスケールは単純な事前分布から抽出されていますが, Stanでサポートされることなら何でもできるでしょう. 混合成分は階層的にモデリングすることさえ可能でしょう.

作業の中心は, データ点nについてのループ中にあります. 各点について, θk × Normal(yn ∣ μk, σk)の対数が計算され, 配列psに加えられます. それから, それらの値の指数の和の対数だけ対数確率を増加させます.

10.4. 混合分布のベクトル化

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])));

この定義では, 各観測値ynが混合成分のいずれかから得られると仮定しています. 以下のように定義されます.


$$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番目の定義は, 観測値の系列y1, …, yn全体が一方の成分か, 他方の成分かから得られていることを意味します. 別の密度を定義しているのです.


$$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})$$

10.5. ゼロ過剰モデルとハードルモデル

ゼロ過剰モデルとハードルモデルはともに, ポアソン分布と, ベルヌーイ確率質量関数との混合分布で, 結果変数が0になる確率をより柔軟にモデリングできます. ゼロ過剰モデルは, Lambert (1992)により定義されたもので, 零値の結果変数に対して追加の確率質量を与えるものです. 一方, ハードルモデルは零値と非零値の結果変数の純粋な混合分布として定式化されます.

ゼロ過剰モデルとハードルモデルは, ポアソン分布以外の離散分布についても定式化できます. ゼロ過剰はStanでは連続分布でうまくいきません. これは, 導関数の問題によるもので, 特に, 回帰係数の事前分布として正規分布をゼロ過剰にするようなときに, 連続分布に対して点の質量を加算する方法がありません.

ゼロ過剰

以下のゼロ過剰ポアソン分布の例について考えます. ここではパラメータthetaを使って, 確率θで0が抽出され, 確率1 − θPoisson(λ)から抽出されるとしています. 確率関数は以下のとおりです.


$$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の値に関わらずe1e2の両方を評価するので, これはおすすめできません.

ハードルモデル

ハードルモデルはゼロ過剰モデルに似ていますが, もっと柔軟で, 零値の結果変数が過剰のときのみならず少なすぎるときも扱うことができます. ハードルモデルの確率質量関数は以下のように定義されます.


$$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}$$

ここでPoissonCDFは, ポアソン分布の累積分布関数です. ハードルモデルはStanではさらに直接的にプログラミングできます. 明示的な混合分布とする必要はありません.

(y[n] == 0) ~ bernoulli(theta);
if (y[n] > 0)
  y[n] ~ poisson(lambda) T[1,];

ポアソン分布のあとの[1,]は1未満の値が切断されることを示しています. これについては40.5節を参照してください. 変数y[n]が2回「サンプリング」されていますが, 対数確率関数への効果は(対数軸での)定義に従っています.

Julian Kingは, 以下のように記述するとモデルが高速化するかもしれないと指摘しました.


logPoissonCDF(0 ∣ λ) = log(1 − exp( − λ))

こうすると, 切断ポアソン分布は以下のようにコーディングされます.

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では宣言の時に大きさが必要ですので, 零と非零の場合の数を計算する関数をこのときに使う必要があるためです.

11. 測定誤差とメタアナリシス

統計モデルで使われる量のほとんどは測定で得られたものです. こうした測定にはほとんどの場合, 何らかの誤差があります. 測定された量に対して測定誤差が小さければ, モデルには通常あまり影響しません. 測定された量に対して測定誤差が大きいときや, 測定された量について非常に精密な関係を推定する可能性があるときは, 測定誤差を明示的に取り入れたモデルが役に立ちます. 測定誤差の一種には丸め誤差もあります.

メタアナリシスは統計的には, 測定誤差モデルと非常によく似ています. メタアナリシスでは, 複数のデータセットから導かれた推定が, それら全体についての推定にまとめられます. 各データセットについての推定は, 真のパラメータの値から, 一種の測定誤差があるものとして扱われます.

11.1. ベイズ測定誤差モデル

ベイズ統計の手法では, 真の量を欠測データとして扱うことにより, 測定誤差を直接的に定式化できます(Clayton, 1992; Richardson and Gilks, 1993). これには, 真の値から測定値がどのように導かれるのかというモデルが必要です.

測定誤差のある回帰

測定誤差のある回帰を考える前にまず, 予測変数xnと結果変数ynを含む, 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);
}

ここで, 予測変数xnの真値が既知ではないとします. ただし, 各nについて, xnの測定値xnmeasは分かっています. 測定誤差をモデリングできるならば, 測定値xnmeasは, 真値xnに測定ノイズを加算したものとモデリングできます. 真値xnは欠測データとして扱われ, モデル中の他の量と同時に推定されます. 非常に単純な方法としては, 測定誤差が既知の偏差τで正規分布すると仮定する方法があります. 以下のような, 測定誤差が一定の回帰モデルになります.

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);
  ...
}

回帰係数のalphabeta, 回帰ノイズのスケールsigmaは前と同じですが, データではなくパラメータとしてxが新たに宣言されています. データはx_measになり, 真のxの値からスケールtauのノイズを含めて測定されているとなっています. そしてこのモデルは, 真値x[n]についての測定誤差x_meas[n]が偏差tauの正規分布に従うと指定しています. さらに真値xにはここでは階層事前分布が与えられています.

測定誤差が正規分布でない場合には, もっと複雑な測定誤差モデルを指定することもできます. 真値の事前分布も複雑にできます. 例えば, Clayton (1992)は, 既知の(測定誤差のない)リスク要因cに対する, 未知の(ただしノイズ込みで測定された)リスク要因xについての暴露モデルを紹介しています. 単純なモデルでは, 共変量cnとノイズ項υからxnを回帰するというようになるでしょう.


xn ∼ Normal(γc, υ)

これはStanでは, ほかの回帰とまったく同様にコーディングできます. もちろん, さらにほかの暴露モデルも使えます.

丸め

測定誤差でよくあるのは, 測定値を丸めることに由来するものです. 丸めのやり方はたくさんあります. 重さをもっとも近いミリグラムの値に丸めたり, もっとも近いポンドの値に丸めたりします. もっとも近い整数に切り下げるという丸めもあります.

Gelman et al. (2013)の演習3.5(b)に以下の例題があります.

3.5 ある物体の重さを5回はかるとします. 測定値はもっとも近いポンドの値に丸められ, 10, 10, 12, 11, 9となりました. 丸める前の測定値は正規分布するとして, μσ2には無情報事前分布を使います. (b) 測定値が丸められていることを考慮して, (μ, σ2)についての正しい事後分布を求めなさい.

ynの丸められていない測定値をznとします. すると, 述べられている問題は以下の尤度を仮定することになります.


zn ∼ Normal(μ, σ)

丸めの過程により, zn ∈ (yn − 0.5, yn + 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の解答では, 分散σ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)));
}

別のやり方として, 丸められていない測定値znについての潜在パラメータを使ってモデルを定義する方法もあるでしょう. この場合のStanのコードは, 制約zn ∈ (yn − 0.5, yn + 0.5)を満たしつつznの尤度をそのまま使います. Stanでは, ベクトル(あるいは配列)の要素についての上下限は不変でなくてなりませんので, そのパラメータは丸め誤差y − zとして宣言され, ztransformed 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を周辺化消去した前のモデルと, μσについて同じ事後分布を生成します. どちらのやり方とも連鎖が良く混ざりますが, 潜在パラメータを使うバージョンは, iterationあたりの有効サンプルの点で2倍ほど効率的です. また, 丸められていないパラメータの事後分布も得られます.

11.2. メタアナリシス

メタアナリシスは, いくつかの学校での指導プログラムの利用や, いくつかの臨床試験での薬を使った治療といった, いくつかの研究からのデータをプールすることを目的としています.

ベイズの枠組みはメタアナリシスには特に便利です. というのは, 興味ある真の量をノイズ込みで測定したものとして, 各先行研究を扱うことができるからです. このとき, モデルはそのまま2つの部分から成ります. つまり, 興味ある真の量についての事前分布と, 解析する研究のそれぞれについての測定誤差タイプのモデルです.

対照群と比較する臨床試験における治療の効果

治療群と対照群について対応のある二項分布のデータが得られている研究が全部でM個あるところから問題のデータが得られているとします. 例えばデータは, イブプロフェン投与下での外科手術後の痛みの軽減や(Warn et al., 2002), ベータブロッカー投与下での心筋梗塞後の死亡率(Gelman et al, 2013, Section 5.6)といったものでしょう.

データ

この臨床データはJ個の臨床試験から成り立っています. それぞれの臨床試験について, 治療群に割り付けられた人数はnt, 対照群に割り付けられた人数はnc, 治療群で改善した(成功した)人数はrt, 対照群で改善した(成功した)人数はrcです. このデータは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ブロックで定義できますが, 整数除算にならないように注意が必要です(31.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が固定されており, Normal(y ∣ θ, σ) = Normal(θ ∣ y, σ)ですので, モデルを正則にするためにどうしても必要というわけではありません.

階層モデル

治療の効果が臨床試験によって変動しうる, いわゆる「ランダム効果」をモデリングするには, 階層モデルを使うことができます. パラメータには, 試験ごとの治療の効果と, 階層事前分布のパラメータを含め, ほかの未知の量をともに推定します.

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のサンプリング文もベクトル化されており, 超パラメータmutauはそれ自身が, データのスケールと比較して幅の広い事前分布を与えられています.

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)は, 二項データの変換の際に対数オッズ比の代替法を使うと, モデリングにどのような影響があるかを考察しています.

試験に特有の予測変数が利用できるならば, 試験ごとの治療の効果θjとして回帰モデルに直接含めることができます.

12. 潜在離散パラメータ

Stanは離散パラメータのサンプリングをサポートしていません. そのため, BUGSやJAGSのモデルで, 離散パラメータ(すなわち, 離散値をとる確率変数のノード)のあるものをそのまま移植することはできません. それでも, 離散パラメータを周辺化消去することにより, 上下限のある離散パラメータを含むたくさんのモデルをコーディングすることができます. 1この章では, 潜在離散パラメータを含むモデルのうち広く使われているものいくつかをコーディングする方法を紹介します. この後の14章では, クラスタリングモデルについて, 潜在離散パラメータを含むモデルについてさらに検討します.

1この計算は, 期待値最大化(EM: Expectation Maximization)アルゴリズムに含まれるものに似ています(Dempster et al., 1977).

12.1 周辺化の利点

離散パラメータを周辺化消去するには, 同時確率関数の何らかの代数計算が必要になりますが, この計算のうれしい副産物が, 周辺化された変数の事後期待値です. その変数がモデル中で関心のある量であることが多いですし, また事後期待値は, 離散パラメータのサンプリングによって推定された期待値ではなく, とりうる値すべてについての期待値が用いられます. そのため, 分布の裾もしっかりと探られるほか, 個々のiterationベースでも, より効率的なサンプリングが可能になります.

期待値最大化(EM: Expectation Maximization)アルゴリズムはじめ, 標準的な最適化アルゴリズムは多くが, 最尤推定アルゴリズムを記述する応用統計学の論文として公表されています. Stanでモデルをコーディングするのに必要な周辺化は, まさにこのような論文に由来します.

12.2 変化点モデル

最初の例は, 1851年から1962年の, イギリスの炭鉱災害のモデルです. 2

2このデータの出典は(Jarrett, 1979)ですが, この論文自体は, 前からあるデータ集を訂正した短信です.

潜在離散パラメータのあるモデル

Fonnesbeck et al. (2013)の3.1節では, 年tにおける災害発生数Dt訳注: 原文は'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, Dt)の対数を定義するモデルを作らなくてはなりません. フル同時確率は以下のようになります.


$$\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 ∣ 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でのモデルのコーディング

変化点モデルのStanのプログラムは図12.1に示します. 変換パラメータ(transformed parameter) lp[s]logp(s, D ∣ e, l)の値を格納します.

MCMCによるモデルの当てはめ

このモデルは, デフォルト設定の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));
}

図12.1: 変化点モデル. 災害発生数D[t]は, 変化点の前ではある発生率eに, 変化点の後では別の発生率lに従います. 変化点自身はsであり, 本文の記述のように周辺化消去されています.

離散変化点の事後分布

あるiterationにおけるlp[s]の値は, そのiterationにおける初期発生率eと後期発生率lの値を用いて, logp(s, D ∣ e, l)により与えられます. 収束後はiterationごとに, 初期および後期災害発生率eおよびlが, 事後のp(e, l ∣ D)からMCMCサンプリングにより抽出され, 関連するlpが計算されます. lpの値は, 各iterationにおけるその時点でのelの値にもとづいて, p(s ∣ 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の値を表します. 抽出全体を平均すると, elの方が周辺化消去され, ある繰り返しでのelの値には結果は依存しなくなります. 最終的に正規化すると, 求めたい量, すなわちデータDを条件とする, sが変化点であることの事後確率が得られます.


$$p(s \mid D) = \frac{q(s \mid D)}{\sum_{s'=1}^{T}q(s' \mid D)}$$

Stan 2.4のデフォルトのMCMC実装を使って計算したlogp(s ∣ D)の値のグラフを図12.2に示します.

変化点の事後推定値のグラフ
変化点の事後推定値のグラフ

図12.2: 変化点の事後推定値. 左) lpを使って解析的に計算した, 各年が変化点である対数確率. 右) lpを使って生成した事後分布における変化点の抽出の頻度. 左のグラフは対数スケールで, 右のグラフは線形スケールです. 右側のグラフではサンプリングのために年の範囲が狭くなっていることに注意. sの事後平均はおよそ1891です.

離散サンプリング

generated quantitiesブロックを利用すると, 組込みの擬似乱数発生器を使って離散パラメータ値を抽出することができるでしょう. 例えば, lpを上のように定義すると, 繰り返しごとのsのランダムな値を抽出するプログラムは以下のようになります.

generated quantities {
  int<lower=1,upper=T> s;
  s <- categorical_rng(softmax(lp));
}

sの抽出の事後分布のヒストグラムを図12.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でベクトルに戻すことができます.

12.3 標識再捕獲モデル

生態学の野外調査法で広く応用されているものに, 動物を捕獲(または視認)し, (タグなどで)標識して, それから放すという方法があります. このプロセスはさらに1回以上繰り返され, 多くの場合は調査継続中の集団(訳注:'population'は, 生態学では「個体群」と訳すのが普通ですが, 遺伝学では「集団」と訳しますし, その方がわかりやすいでしょうから, ここでは「集団」と訳しています. )に対して行なわれます. 結果のデータは, 集団サイズを推定するのに使うことができます.

最初の小節では, 潜在離散パラメータを含まないごく単純な捕獲再捕獲モデルを記述します. その後の小節では, 動物の死についての潜在離散パラメータを含むCormack-Jolly-Seberモデルを記述します.

単純な標識再捕獲モデル

もっとも単純な場合では, 1段階の標識再捕獲調査から以下のデータが得られます.

目的の推定対象は以下です.

上の説明と話が違うのですが, このモデルではNを連続パラメータとしましょう. 集団は有限でなくてはならないといっても, それを表すパラメータはその限りではありません. このパラメータは, 集団サイズの推定値を実数値で求めるのに使います.

Lincoln-Petersen法(Lincoln, 1930; Petersen, 1896)を集団サイズの推定に使います.


$$\hat{N} = \frac{MC}{R}$$

この集団推定値は, 再捕獲された動物の数が二項分布に従うという確率モデルに基づいたものでしょう.


R ∼ Binomial(C, M/N)

ここでは, 2回目に捕獲された動物の数の合計(C)と, 再捕獲率M/Nを与えています. 再捕獲率は, 全個体数Nのうち, 最初に標識された個体の割合に等しいとしています.

Lincoln-Petersen推定量を確率的にしたものは, 図12.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);
}

図12.3: Lincoln-Petersen推定量の確率的定式化. 1段階標識再捕獲調査からのデータに基づいて集団サイズを推定します. ありえない値を効率的に除くため, Nの下限は必要です.

最尤推定値が確実にLincoln-Petersen推定値になるように, Nには非正則一様事前分布が使われています. 可能であれば, 調査している集団についての知識に基づいて, もっと情報のある事前分布を使うこともできるでしょう(そしてそうすべきです).

このモデルでトリッキーなところは, 集団サイズNの下限C − R + Mでしょう. これより小さい値では, 再捕獲されたC匹の動物からRだけのサンプルを抽出することができないので, この下限より小さな値は取りえないのです. 変換された(制約のない)空間でのパラメータに制限がかからないようにして, サンプリングと最適化が制約なく行なわれるのを確実にするため, この下限を実装することは必要です. Cについて宣言されたこの下限は, 次の変数変換を暗黙のうちに伴います: f : (C − R + M, ∞) → ( − ∞,  + ∞), ff(N) = log(N − (C − R + M))と定義されます. 下限を宣言する変数に使われる変換についてさらに知るには56.2節を参照してください.

離散パラメータのあるCormack-Jolly-Seberモデル

Cormack-Jolly-Seber (CJS)モデル(Cormack, 1964; Jolly, 1965; Seber, 1965)は, 死亡により集団が時間的に変化する開放集団モデルです. ここで紹介している例は(Schofield, 2007)から多くを引いてきています.

基本データは以下のとおりです.

各個体は少なくとも1回は捕獲されていると仮定されます. これは, 最初に捕獲された後という条件付きでしか個体が情報に寄与しないからです.

このモデルには2個のベルヌーイパラメータがあります.

これらパラメータには一様事前分布を与えますが, 実際には情報を使って事前分布を狭くするべきでしょう.

CJSモデルはまた, 潜在離散パラメータzi, tを使います. これは, 各個体iが時点tで生存しているかどうかを示すものです.


zi, t ∼ Bernoulli(zi, t − 1 ? ϕt − 1 : 0)

訳注:原文では0とϕt − 1が逆になっていますが, 条件演算子の定義からするとこちらの方が正しいはずです.

この条件によりゾンビが発生することを防ぎます. つまり, 動物はいったん死んだらならば, ずっと死んだままになります. データの分布はzを条件として単純に以下のように表されます.


yi, t ∼ Bernoulli(zi, t ? pt : 0)

訳注:これも上の式と同様です.

この条件により, 死亡した動物は捕獲されることがないという制約がつけられます.

集合Cormack-Jolly-Seberモデル

この小節では, 3回の捕獲調査を行なった場合について, 個体の捕獲プロファイルの別を計数するというモデルの実装を紹介します. このモデルは, どの動物も同じ捕獲率と同じ生存率を持つという交換可能性を仮定しています.

潜在離散パラメータz < sub > i, t < /sub > の周辺化を簡単にするため, Stanのモデルでは, ある時点tで生存していた個体(死亡していれば, 再捕獲率は0です)がもう2度と捕獲されない確率χ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になります. そのため, これが再帰のベースになります. χt + 1から, 再帰的にχtを定義しますが, この場合には2つの確率が含まれます. (1)確率(1 − ϕt)で, 次回調査時点まで生存しない, (2)確率ϕtで, 次回調査時点まで生存するが, 次回調査時点では確率(1 − pt + 1)で捕獲されず, かつ, 確率χt + 1で, 時点t + 1で生存していた後に2度と捕獲されない.

3回の捕獲調査の場合, ある個体についての捕獲/非捕獲のプロファイルが3つあることになります. これは2値の数字として以下のように自然に符号化されます.

プロファイル 捕獲/1 2 3 確率
0 - - - n/a
1 - - + n/a
2 - + - χ2
3 - + + ϕ2ϕ3
4 + - - χ1
5 + - + ϕ1(1 − p2)ϕ2p3
6 + + - ϕ1p2χ2
7 + + + ϕ1p2ϕ2p3

履歴0, すなわち動物が1度も捕獲されない場合は観測不可能です. 捕獲された動物だけが観測されるからです. 履歴1, すなわち最終回にのみ動物が捕獲される場合は, CJSモデルでは情報は得られません. 捕獲/非捕獲の状態から情報が得られるのは, 前の捕獲を条件としたときだけだからです. 残りの場合には, 最後の列に示したように尤度への寄与があります.

χを使って直接こうした確率を定義することで, 動物が時点tで生きているかどうかを示す潜在2値パラメータは必要なくなります. このχの定義は, CJSモデルでの尤度(すなわち潜在離散パラメータを周辺化消去した)の定義では典型的です(Schofield, 2007, 9ページ).

Stanのモデルでは, パラメータϕpに基づいて変換パラメータ(transformed parameter)としてχを定義しています. 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];
}

図12.4: Cormack-Jolly-Seber標識再捕獲モデルのStanプログラム. 3回の捕獲調査での観測のありなしという観測履歴ごとに個体の数をまとめています.

識別可能性

パラメータϕ2p3, すなわち時点2での生存率(訳注:原文ではprobability of deathとなっていますが, ϕの定義は「生存率」です)と時点3での捕獲率とは識別可能ではありません. これは, 時点3で捕獲されなかったことの説明にこの両方ともが使えるからです. これらの積β3 = ϕ2p3は識別可能です. Stanのモデルではbeta3を生成量(generated quantity)として定義しています. 識別不可能なパラメータは, Stanのサンプラーの適応(adaptation)に問題を引き起こします. パラメータに制限があって, 正則一様分布が使われていたため, このモデルでは適応に大きな問題は発生しませんでしたが, 識別可能なパラメータとなるように定式化した方が良いでしょう. そのためには, pϕのパラメータについて階層モデルとして定式化するのもひとつの方法でしょう.

個体Cormack-Jolly-Seberモデル

この小節では, 前の小節で紹介したような集合的なものではなく, 個体レベルで動くバージョンのCormack-Jolly-Seber (CJS)モデルを紹介します. また, これによりモデルは, 任意の調査回数に対応することができるようになります. データは, 捕獲調査の回数T, 個体数I, 個体iが時点tで観測されたかを示す論理値フラグyi, tからなります. Stanでは以下のように記述されます.

data {
  int<lower=2> T;
  int<lower=0> I;
  int<lower=0,upper=1> y[I,T];
}

個体レベルモデルの利点に, 生存率や捕獲率に影響する個体の「ランダム効果」を含めることが可能になります. そのほか, T回の捕獲があった場合2T個の観測履歴を展開した組み合わせを使わなくて良いという点もあります.

ユーティリティ関数

この個体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;
  }
}

この関数はχ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;
}
個体効果への一般化

このモデルは, 個体すべてが同じ捕獲率と持つというふうにモデル化されていますが, 一般化は簡単にできるでしょう. その場合, 個体レベルの入力値を予測変数として使って, これに基づくロジスティック回帰を使います.

12.4 データ符号化と診断正答率のモデル

かけはなれた仕事のように見えますが, アイテムをカテゴリーに分けて評価/符号化/注釈するのと, 病気か病気でないかを診断検査することとの間には共通する特徴があり, 両者の統計的特性は同じようにモデル化できます.

診断正答率

さまざまな感度と特異度の条件で診断検査を行なうとします. 感度とは, 検査をうける人が疾患ありの時に正しく検査が陽性となる確率です. 特異度は, 検査をうける人が疾患なしの時に正しく検査が陰性となる確率です. 乳癌の検査方法であるマンモグラムと穿刺生検を例にします. マンモグラムの感度は高く, 特異度は低いのですが, これは偽陽性が多いことを意味します. 一方, 穿刺生検は反対で, 感度は低く特異度は高いので, 偽陰性が多くなります.

こうした調査ではいくつかの推定対象があります. 疫学調査では, 集団内でマラリアのようなある種の感染症に罹っている患者数に関心があるでしょう. 検査法を開発する研究では, 新しい検査法の診断正答率に関心があるでしょう. 健康管理従事者が行なう検査では, ある患者の病状に関心があるでしょう.

データ符号化

データに符号をつける(評価や注釈も同様)仕事を与えられることはよくあります. 例えば, 論文誌や助成金の審査員は提出物を評価しますし, 政治学の研究では, キャンペーンコマーシャルが攻撃的広告か否かを符号化したりするでしょう. 自然言語処理では, ツイートが全体的な感情に照らしてポジティブかネガティブか注釈をつけるでしょうし, X線画像を見る歯科医は患者が虫歯かどうかを分類します. このような場合, データに符号をつける人は診断検査の役を演じており, すべての場合に共通するような推定対象があります. すなわち, データにつけた符号の正答率とバイアス, 符合をつけたアイテムの真のカテゴリー, データ中のアイテムのさまざまなカテゴリーはそれぞれいくつあるか, といったことです.

ノイズのあるカテゴリー測定モデル

この小節では, カテゴリー評価だけに絞って, 離散パラメータを周辺化消去してStanでモデリングすることを考えます.

Dawid and Skene (1979)は, データ符号化にノイズのある測定モデルを導入して, 医師による記録から患者の履歴についてわかることを符号化するという疫学的状況に応用しています. 同じモデルは診断法についても使用できます.

データ

データは, J評価者(診断検査), Iアイテム(患者), Kカテゴリー(状態)の注釈からなり, yi, j ∈ 1 : Kは, アイテムiについて評価者jがつけた評価です. ある状態についての診断検査の状況では, 評価者は診断法であり, 多くはK = 2, つまり疾患があるかないかの信号の値になります. 5

全評価者が全アイテムを1度だけ評価するとは限らないという状況にDawid and Skeneのモデルを拡張するのは比較的素直にできます.

5診断法では順序尺度となることも多くあります. 腫瘍学的診断での癌の段階や, 歯科診断での虫歯の深刻度といったものです. Dawid and Skeneのモデルはそのままでも使えますし, 順序ロジスティック回帰として, 潜在連続値の評価とカットポイントを使い, 順序をつけて評価するように自然に一般化してもよいでしょう.

モデルパラメータ

このモデルは3つのパラメータを持ちます. 最初のひとつは離散的です.

ノイズのある測定モデル

あるアイテムの真のカテゴリーは, アイテムの出現率をもとに単純なカテゴリカル分布で生成されると仮定します.


zi ∼ Categorical(π)

アイテムiについての評価者jの評価yi, jは, カテゴリーziのアイテムに対する評価者i訳注: jが正しい?)のカテゴリカルな応答としてモデル化します. 6

6添字のz[i]は, ziを読みやすくしたものです.


yi, j ∼ Categorical(θj, πz[i])

事前分布と階層モデリング

Dawid and Skeneはθπの最尤推定値を提供しています. これにより, 各ziの確率の推定値を生成できます.

Dawid and Skeneの最尤モデルをまねるため, パラメータθj, kπにはK次元単体についての一様事前分布を与えます. 一般化してディリクレ事前分布にするのも簡単です.


π ∼ Dirichlet(α)

および


θj, k ∼ Dirichlet(βk)

αβは固定された値を持つハイパーパラメータです. θj, kの事前分布はkによって変わることができるようにしないといけません. そうすると例えば, 偶然よりも良い注釈をする注釈者が, ランダムまたは反対に注釈する注釈者よりも高い事前確率を持てるようにβk, kを大きくできるようになります.

Jだけ評価者がいるので, βに階層事前分布をつけて, 評価者の正答率とバイアスの推定値を部分的に合算するようにモデルを拡張するのは自然なことでしょう.

真のカテゴリーの周辺化消去

真のカテゴリーのパラメータzは離散的ですので, 同時事後分布から周辺化消去して, Stanでサンプリングあるいは最尤推定ができるようにします. 同時事後分布の因子は以下のようになります.


p(y, θ, π) = p(y ∣ θ, π)p(π)p(θ)

ここで, p(y ∣ θ, π)は, 次式から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関数を使ってそのままコーディングできます.

Stanでの実装

Dawid and SkeneモデルのStanプログラムを図12.5に示します. NUTSを使うStanのモデルは, ばらばらの初期値から素早く収束し, よく混合されます. 離散パラメータをギブズサンプリングするように実装した同等のモデルではそうはいきません. 適切な弱情報事前分布として, αk = 3, βk, k = 2.5K, βk, k = 1k ≠ kのとき)としています. αβ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]));
}

図12.5: Dawid and Skene (1979)の評価(あるいは診断精度)モデルのStanプログラム. このモデルは, 離散パラメータzを周辺化消去し, 非正規化条件付き確率logq(zi = k ∣ θ, π)log_q_z[i,k]に格納しています.

真のカテゴリーの推定

log_q_z[i]は変換パラメータ(transformed parameter)として定義されます. これは, p(zi ∣ θ, π)の対数(正規化されていません)をコード化したものです. 繰り返しごとに, その繰り返しでのθπとの値を条件とした値が得られます. softmax関数をlog_q_z[i]に適用することで, 事後のziの確率質量関数に相当する単体が得られます. これを各繰り返しについて平均することで, それぞれのziについての事後確率分布が得られます.

13. まばらなデータ構造と不ぞろいなデータ構造

Stanは, まばらなデータ構造も不ぞろいなデータ構造も直接はサポートしていません. しかし, プログラミングの努力をいくらか払うことで両方とも扱うことができます. 35章では, 特別な目的のための疎行列と密ベクトルとの積を紹介しており, 適用可能なところではこれを使うことができますが, この章ではもっと一般的なデータ構造を対象とします.

13.1 まばらなデータ構造

まばらなデータ構造のコーディングは難しくなく, 行列状のデータ構造をデータベース状のデータ構造に変換するのと同じです. 例えば, 6.10節で議論したIRTモデルのまばらなデータを考えます. J人の生徒とK問の問題があり, すべての生徒がすべての問題を解くとすると, データは, J × 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は未定義の値をサポートしていないからです. 図13.1は, J = 3K = 4の場合の例で, 欠測の応答はRと同じくNAとしています. StanにはRのNA値のサポートはありませんので, このデータ構造を直接使うことはできません. かわりに, データベースのような「長い形式」に変換する必要があります. そのためには, 値とは別に, jおよびkのインデックスを示す列を持たせます. 例えば, jjkkをインデックスのために使うと(Gelman and Hill, 2007による), データ構造は, 図13.1の右側の例のようにコーディングできます. この例では, y1, 1 = 0, y1, 2 = 1などとなっており, 最後はy3, 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}$$

図13.1: まばらな配列をStanでコーディングした例. 左側は, R由来のNA記法を使った疎行列yの定義です(これはStanではサポートされていません). 右側は, 同じ疎行列yをデータベース状にエンコーディングしたもので, これはStanで直接扱うことができます. 最初の2列, jjkkはインデックスを示し, 最後の列yがその値を示します. 例えば, 右側のデータベース状のデータ構造の5行目はy2, 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種のモデルの定式化は完全に同じ対数事後密度を生成します.

13.2. 不ぞろいなデータ構造

不ぞろいな配列とは, 長方形になっておらず, 項目によってサイズが違うような配列のことです. この種の構造は, 配列ごとに観測回数が違うときに出てきます.

不ぞろいなデータ構造を扱う一般的な方法は, 前の節で議論したように, 完全なデータベース状のデータ構造に移行することです. より簡潔に, 線形の配列に何らかのインデックスをつける方法もあります.

例えば, 3群があって, それぞれの観測回数が異なるようなデータ構造を考えます.

とても単純な, 切片が変化するモデルを仮定します. ベクトル化した記法を使うと, 尤度は以下のようになります(訳注: 原文では積()になっていますが, 対数にしているので和()のはずです).


$$\sum_{n=1}^{3}\log\mathsf{Normal}(y_{n}\mid\mu_{n},\sigma)$$

これを直接Stanでエンコードする方法はありません.

まばらなデータの例のように, 完全なデータベース型の構造を使うことができるでしょうが, これは非効率です. 不要なインデックスがスペースを無駄にしますし, ベクトルによる密度の演算ができません. このデータをコーディングするもっと良い方法は, 単一のリストに値を持たせ, 各部分配列のサイズを示すデータ構造を別に用意するというものです. 図13.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}$$

図13.2: 不ぞろいな配列をStanでコーディングした例. 左側は, サイズの異なる3行(y1はサイズ3, y2はサイズ2, y3はサイズ4)からなる不ぞろいなデータ構造yの定義です. 右側は, Stanでこのデータをコーディングする方法の例です. 単一のベクトルyを使ってすべての値を保持し, 整数値の別の配列sに, 群ごとの行のサイズを持たせています. この例ではy1 = z1 : 3, y2 = z4 : 5, y3 = z6 : 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()によるベクトルのスライシング演算によって生じるコピーのコストに見合うものです.

14. クラスタリングモデル

データを教師なしでグループにまとめる方法は, 総じてクラスタリングと呼ばれます. 本章では, 広く用いられている統計的なクラスタリングモデルの内2つ, ソフトK-means法と潜在ディリクレ配分法(LDA)に関して, Stanでの実装について説明します. 本章にはさらに, 教師ありの場合のクラスタリングの一種とみなせるナイーブベイズ分類法が含まれます. 通常これらのモデルは, クラスタの割り当てに離散パラメータを用いて表現されます. しかしながら, Stanでは離散パラメータを周辺化消去することで, これらのモデルを混合モデルのように実装することができます(10章も参照してみてください).

14.1. ソフトK-Means法

K-meansクラスタリングとは, D次元のベクトルで表わされるデータをクラスタリングする方法です. 具体的に言うと, 分類される項目がN個存在し, その各々はベクトルyn ∈ ℝDで表わされます. K-means法の「ソフト」版では, クラスタの割り当てが確率的になります.

幾何的なハードK-Meansクラスタリング

K-meansクラスタリングは以下のアルゴリズムの様に, 通常幾何的な観点で記述されます(クラスタ数Kとデータベクトルyは入力として仮定します).

  1. 1 : Nの各nに対し, ベクトルyn1 : Kのとあるクラスタにランダムに割り当てます
  2. 以下の処理を繰り返します
    1. 1 : Kの各クラスタkに対し, そのクラスタに割り当てられたベクトルを平均してクラスタの重心μkを計算します
    2. 1 : Nの各nに対し, ynからμkへの(ユークリッド)距離が最小となるクラスタkに, ynを再度割り当てます
    3. どのベクトルもクラスタが変わらなくなったら, そのクラスタ割り当てを返します

このアルゴリズムは, 停止することが保証されています.

ソフトK-Meansクラスタリング

ソフトK-meansクラスタリングでは, クラスタの割り当ては複数のクラスタに渡る確率分布に基づき扱われます. ユークリッド距離と共分散が固定された多変量正規モデルの間の関係から, ソフトK-means法は, 多変量正規分布の混合モデルとして(Stanのコードも)表現することができます.

この全生成モデルでは, 1 : Nの各データ点nに, 次のような対称で一様な確率でクラスタzn ∈ 1 : Kが割り当てられます.


zn ∼ Categorical(1/K), 

ここで1K次元の単位ベクトルですので, 1/Kは対称なK単体となります. このようにして, このモデルではクラスタが割り当てられると, そこからおのずと各データ点が抽出されてゆくことが仮定されます. 「ソフト」さは, どのクラスタがデータ点を生成するかという不確実性のみから発生します.

データ点自体は, 割り当てられたクラスタznから決定されるパラメータを持つ, 多変量正規分布より生成されます.


yn ∼ Normal(μz[n], Σz[n])

本節での実装例では, 共分散行列には全クラスタkに共通して固定された単位行列を仮定します.


Σk = diag_matrix(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法に対する空間的な観点では, 上式のかっこの内側の項が, まさにクラスタ平均μkからデータ点ynまでのユークリッド距離(を半分にして符号を反転した値)になっていることに注意して下さい.

ソフトK-Means法のStanでの実装

K-meansクラスタリングを実装した, 次のStanのプログラムを考えましょう. 4

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関数を用いて規格化された単体に戻すことができます(34.11節もご参照ください).

ソフトK-Means法の一般化

共分散行列が単位行列の多変量正規分布は, ユークリッド距離(すなわちL2距離)に比例する対数確率密度を生成します. ここで分布を変えると, 関連する幾何的な距離も別なものに変わります. 例えば, 正規分布を二重指数(ラプラス)分布に変えると, L1距離(すなわちマンハッタン距離もしくはタクシー距離)に基づくクラスタリングモデルが生成されます.

K-means法を多変量正規分布の視点でとらえる限り, 共分散行列を単位行列からクラスタに共通する別の行列に変更しても, 結局その共分散行列の逆行列で変換された空間で定義されるユークリッド距離で動作しているのと同じことになります.

なお空間との大域的な類似性はありませんが, 共分散行列がクラスタ毎に異なるソフトK-means法も一般的です. この様な場合には, 共分散行列に対して階層的な事前分布が用いられる事があります.

14.2. クラスタリングにおけるベイズ推定のむずかしさ

クラスタリングモデルのフルベイズ推定はほぼ実行不可能であり, これはパラメータの識別可能性の欠如と事後分布の極度な多峰性の2つの問題によるものです. 20.2節には, ラベルスイッチングに起因する識別不可能性について追加で議論が行われています.

識別不可能性

クラスタの割り当ては, そもそも識別することができません. これは, クラスタの平均ベクトルmuを入れ替えたとしても, 尤度の同じモデルが導れるためです. 例えば, muの最初の2つのインデックスと各soft_z[n]の最初の2つのインデックスを入れ替えても, 尤度は(事後分布も)同じになります. (訳注: かっこの中は原文priorですが, posteriorの誤記と捉えました)

このような識別可能性の欠如は, 複数のマルコフ連鎖の間でクラスタのパラメータが比較できないことを意味しています. ソフトK-means法におけるパラメータは一つですが, このパラメータが識別されないため, 実際収束をモニタする際に問題が生じます. 単一の連鎖の中であっても, 連鎖が長すぎたりデータがきれいに分離されないような場合にインデックスの入れ替えが伴うと, クラスタの識別に失敗する可能性すらあります.

多峰性

クラスタリングモデルのもう一つの問題は, 事後分布が非常に多峰的になる点にあります. ある種の多峰性は識別不可能であり, インデックスの入れ替えを招きます. しかしながらたとえインデックスの問題がなかったとしても, 事後分布は非常に多峰的になります.

多峰性が高い場合, ベイズ推定は失敗します. これは, 事後分布のあらゆる峰を適切な比率で訪れる方法がなく, 従って事後予測の推定に含まれる積分を評価する方法もなくなるためです.

これら2つの問題の観点からクラスタリングモデルのあてはめに関してよく聞くアドバイスは, 初期値をたくさん変えて試してみる事であったり, 全体的に最も高い確率をもつサンプルを選択する事であったりします. なお, 期待値最大化法や変分ベイズ法のような最適化に基づく点推定量を用いるのもポピュラーであり, サンプリングに基づくアプローチよりずっと効率的な場合があります.

14.3. ナイーブベイズ分類法およびクラスタリング法

ナイーブベイズ法は一種の混合モデルであり, 観測される項目のラベルにより, 分類もしくはクラスタリング(または両者のミックス)に使用することができます. 5

混合多項分布モデルは「ナイーブベイズ」ともよばれます. これはこのモデルが, カテゴリを決める多項分布が独立しているという仮定が明らかに成り立たないような分類問題にも適用されてしまうことが多いためです.

ナイーブベイズによる分類とクラスタリングは, 多項分布で表せるような構造(以降ではこれを, 多項的な構造と呼びます)をもつあらゆるデータに適用できます. 典型的な例は自然言語テキストの分類とクラスタリングであり, これはこの後例として使用します.

この場合の観測データは一連のM本の文書から構成され, その文書はV種の異なる語彙から抽出される単語の多重集合(bags of words)からなっています. 文書mの単語数はNmであり, 単語にはwm, 1, …, wm, N[m] ∈ 1 : Vというインデックスが振られています. 文書中の単語のインデックスには順番がありますが, このモデルはその順番を考慮しないため, 人間の自然言語のモデルとしては明らかに欠点があります. なお, トピック(もしくはカテゴリ)の数はKに固定されています.

混合多項分布モデルでは, 各文書m ∈ 1 : Mに対して, カテゴリカル分布に従い単一のカテゴリzm ∈ 1 : Kが生成されます.


zm ∼ Categorical(θ).

K単体のパラメータθは, データにおける各カテゴリの出現頻度を表しています.

続いて文書のカテゴリに基づき, 各文書の単語が, その文書中の他の単語や他の文書中の単語と条件付き独立になるように生成されます. つまり, 文書mの単語nは次のように生成されます.


wm, n ∼ Categorical(ϕz[m]).

ここでパラメータϕz[m]V単体であり, これはカテゴリzmの文書に関する語彙における各単語の確率を表しています.

パラメータのθϕの事前分布には, 通常対称ディリクレ分布が用いられます. なお出現頻度θは, 各カテゴリk ∈ 1 : Kが等確率となるように固定される場合もあります.

不ぞろいな(Ragged)配列のコード化

前節におけるナイーブベイズモデルの規定では, 単語wの表記に不ぞろいな配列を使用しました. Stanは不ぞろいな配列をサポートしていないため, このモデルのコード化には別のやり方(全単語のリストから定まる語彙種別のインデックスを, 各単語に付与する方法)が用いられます. このデータは次のように構成されており, 一列目は出現した単語を全て並べて全文書で通して付けたインデックスn, 二列目は単語nに対する語彙種別のインデックス, 三列目は単語nが含まれる文書のインデックス, となります.

n w[n] doc[n]
1 w1, 1 1
2 w1, 2 1
N1 w1, N[1] 1
N1 + 1 w2, 1 2
N1 + 2 w2, 2 2
N1 + N2 w2, N[2] 2
N1 + N2 + 1 w3, 1 3
N${}=\sum_{m=1}^{M} N_m$ wM, N[M] M

関連するプログラムの変数は, 全文書における総単語数N, 単語の配列w, 文書を特定する配列docとなります.

カテゴリのラベルが付与された訓練データが存在する場合の推定

カテゴリが既知の文書が訓練データとして与えられた場合, ナイーブベイズモデルで単体上のパラメータを推定するStanのコードは, 次のように書けます. 6

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]]]);
}

トピックの識別子zmがデータとして宣言され, カテゴリの割り当てが尤度関数の一部として含まれていることに注意してください.

カテゴリのラベルが付与された訓練データが存在しない場合の推定

ナイーブベイズモデルは, 多項的な構造のデータを, 教師なしのやり方で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}). $$

γが与えられた下で, 文書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). $$

変数gammatransformed 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ブロックに移行すると, この様な振る舞いを行う実装が可能になります. この場合その変数はもはや対数確率には寄与しなくなるので, モデルパラメータの推定においても, もはや同時には影響しないことになります.

14.4. 潜在ディリクレ配分法

潜在ディリクレ配分法(Latent Dirichlet Allocation, 略してLDA)は, ナイーブベイズを一般化した, 混合メンバーシップの多項クラスタリングモデル(Blei et al., 2003)です. LDAの説明で一般的なトピックと文書という用語を用いれば, まず各文書が複数のトピックを持つ様にモデル化され, その混成比に基づいて, あるトピックから各単語が抽出されることになります.

LDAモデル

このモデルの基本型では, まず各文書が, 固定の超パラメータに基づいて独立に生成されると仮定されます. そして文書mに対する最初のステップとして, K個のトピックに渡るトピック分布である単体θmが抽出されます.


θm ∼ Dirichlet(α).

ここで事前分布の超パラメータαは固定されており, これはK個の正の値のベクトルとなります. 文書中の単語は, この分布θmが与えられた条件の下で, 各々独立に生成されることになります. 具体的にはまず最初に, 文書に固有のトピック分布に基づき, 単語に対するトピックzm, n ∈ 1 : Kが抽出されます.


zm, n ∼ Categorical(θm).

そして, トピックzm, nにおける単語の分布に従って, 最終的に単語wm, nが抽出されます.


wm, n ∼ Categorical(ϕz[m, n]).

なお, トピックkにおける単語の分布ϕkにも, ディリクレ事前分布が設定されます.


ϕk ∼ Dirichlet(β)

ここでβは固定であり, 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} $$

LDAの実装

前節で導出した周辺分布に対し, 本節でデータ構造をあてはめて記述すると, 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の変種が提案されており, そこでは文書毎のトピックに関する事前分布が, ディリクレ分布から多変量のロジスティック正規分布に置き換えられています.

この論文の筆者らは, 超パラメータが固定の場合を扱っています. なお論文では共分散のL1正則化推定値が用いられていますが, これは事前分布に二重指数分布を設定した場合の最大事後確率推定値と等価です. 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);    // 制約により半コーシー分布
  ... 単語のサンプリングに関する記載は前と同じ ...
}

形状パラメータがα > 0LkjCorr分布は, 相関行列(すなわち, 対角成分が1の対称な正定値行列)と同じ台を持ちます. この密度は次のように定義されています.


LkjCorr(Ω ∣ α) ∝ det(Ω)α − 1

α = 2というスケールでは, この分布は単位相関行列寄りの弱情報事前分布となります. したがって, 多変量のロジスティック正規分布の共分散行列Σにこの事前分布を適用すると, 対角成分への集中度がいくらか増す一方で, そのスケールはsigmaの事前分布から決定されるため, これらの複合効果がもたらされることになります.

16. 方向, 回転, 超球面

方向統計は, 方向という制約のあるデータあるいはパラメータ, もしくはその両方を扱います. 方向を組み合わせると球面となります. 球面のジオメトリは, ユークリッド空間のジオメトリにすんなりとマッピングすることができません. 球面は, 1周まわって元に戻るということも可能だからです. そういうわけで, 平面の紙の上に地図を作るのに, 地球上の互いに近い地点ならどこでも, 地図上でも互いに近くにあるというようにはできません. この根本的な問題は2次元では容易に可視化できます. 円周に沿って移動していると, 初めの場所に戻ってしまいます. 言い換えると, 0度と360度は(0と2πラジアンでも同じことですが)同じ点を指しますし, 359度と2度との間の距離は137度と140度との距離と同じです.

Stanには単位ベクトルのデータ型があり, 方向統計に対応しています. 単位ベクトルの値は, 超球面(2次元では円周, 3次元では球面)上の点を決定します.

16.1. 単位ベクトル

ベクトルx ∈ ℝKの長さは次式で与えられます.


$$ \|x\| = \sqrt{x^{\top} x} = \sqrt{x_1^2 + x_2^2 + \cdots + x_K^2} $$

単位ベクトルは, 単位長(すなわち長さ1)を持つベクトルと定義されます.

以下のように変数宣言します.

unit_vector[K] x;

xの値は, 単位長を持つサイズKのベクトルに制約されます. 58.7節に, Stanのアルゴリズムで使っている, 単位ベクトルに制約されているパラメータを制約のない空間に変換する方法の詳細があります.

16.2. 円, 球面, 超球面

n次元球面をSnと書くことにしますが, これは(n + 1)次元の単位ベクトルの組で定義されます.


Sn = {x ∈ ℝn + 1 : ∥x∥ = 1}

Snは, (n + 1)次元の点で作られるにもかかわらず, n次元の多様体でしかありません. 例えば, S23の点の組で定義されますが, そうした点は緯度・経度により一意に記述することができます. 幾何学的には, 3S2で定義される表面は, 局所的には平面つまり2に似ています. しかし, S2の全体的な形は, コンパクト(すなわち, 点間に最大距離が存在する)というところで平面とは違っています. 地球を「直線」(すなわち測地線)に沿って回り始めると, 最終的には初めの場所に戻ってしまいます. 球面(S2)の測地線が「大円」と呼ばれるのはこれが理由です. そしてまた, 円周あるいは球面統計には何らかの上手な表現が必要となる理由でもあります.

Sn − 1が局所的にはn − 1に似ているとしても, 両者をすんなりとマッピングする方法はありません. 例えば, 緯度と経度は, (自然単位では2πで一回りする)モジュラ基底に基づいていますので, すんなりマッピングできません.

上下限のある区間(a, b)のように, あらゆる2点間の距離には上限があることから, 幾何学的意味で球面はコンパクトです.

16.3. 制約のないパラメータへの変換

Stanは, Marsaglia (1972)の方法で補助変数を使い, K + 1における任意の点をSKの点へと(逆)変換します. 点y ∈ ℝKは, 点x ∈ SK − 1に以下のように変換されます.


$$ x = \frac{y}{\sqrt{y^{\top} y}} $$

このマッピングの問題は, 多対1であることです. 原点から出ているベクトル上の点はすべて, 球面上の同じ点に投影されます. Marsaglia (1972)は, このマッピングの補助変数の解釈を行い, xが超球面上で一様に分布することを示しました. 詳細は58.7節を参照してください.

警告: ゼロでは未定義

上のnからSnへのマッピングはゼロでは定義されません. サンプリング中はこの点の結果は測度ゼロであり, そのため無視されるでしょう. 同様に単位ベクトルのパラメータはゼロで初期化できません. 単純な回避法は, ゼロに近い非常に狭い区間で初期化することです. これはStanのインターフェイスすべてに組み込まれているオプションです.

16.4. 単位ベクトルと回転

単位ベクトルはそのまま角度に対応していますから, 回転にも対応します. これは2次元で見るとわかりやすいでしょう. 円周上のある点は, コンパス上の方角を決定します(等価ですが, 角度θを決定します). ある角度θが与えられたとすると, 左からの掛け算で角度θだけ回転させるような行列を定義できます. (2次元の)角度θについては, 2 × 2回転行列は以下のように定義されます.


$$ R_{\theta} = \left[\begin{array}{cc} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{array}\right] $$

2次元ベクトルxが与えられたとすると, Rθxxを(原点のまわりで)θ度だけ回転させます.

16.5. 日と年の円周表現

24時間時計は, 深夜0時から正午を経て, 1回転して再び0時に戻るという, 1日の時間の進み方を自然に表しています. したがって, 円周上を24時間に分割した点は, 1日の時刻を自然に表したものです. 同様に, 1年は季節をめぐって, また元の季節に戻ります.

人為的なものには慣習による時間効果がよく見られます. こうしたものも, 休日や週末を示すアドホックな予測変数によって, あるいは, 夏時間を自然のスケールに戻すようなデータの正規化によって, 直接モデリングできます.

17. 再パラメータ化と変数変換

BUGSと同様に, Stanでは直接的な再パラメータ化がサポートされています. また, Stanでは変数変換もサポートされており, 変数変換のヤコビアンの対数を対数確率の合計に直接加えることで実現します.

17.1. 理論的かつ実践的な背景

ベイズの事後分布は専門的には確率「測度」であり, それはパラメータ化によって不変な, 抽象数学のものです. 7他方, Stanのモデリング言語は確率「密度」を定義します. 確率密度はNから + に写す関数であり, ユニークではなく, パラメータ化に依存します. 実際には, これは与えられたモデルをStanで表現する方法は複数あり得ることを意味し, 表現が異なれば計算のパフォーマンスも異なることを意味します.

パラメータ化とベイズモデリングの関係を議論したGelman (2004)で指摘されたように, パラメータ化を変えるとどのようにモデルが変わるかに関する示唆が得られる場合がしばしばあります. 私たちは特定の自然なクラスの事前分布を多用する傾向があります. それゆえ, 再パラメータ化の恩恵は, サンプリングをしたいと決めた分布に対する計算の助け「だけ」にはとどまらないのです. さらに, いったん再パラメータ化をして事前情報を加えると, モデル自体も典型的には変わりますし, しばしば有益な方向に変わります. 8

17.2. 再パラメータ化

再パラメータ化は素直に実装できます. 例えば, ベータ分布は2つの正のカウントを表すパラメータα, β > 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)を使って超事前分布を定めた方がしばしばより自然です. ベータ分布の場合, 明らかな再パラメータ化のやり方は平均パラメータ


ϕ = α/(α + β)

とカウントの合計を表すパラメータ


λ = α + β

を使う方法です. (Gelman et al., 2013, Chapter 5)に従うと, 平均パラメータの事前分布に一様分布を設定し, カウントの合計を表すパラメータの事前分布にp(λ) ∝ λ − 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);
  ...

新しいパラメータphilambdaparametersブロックで宣言され, ベータ分布のパラメータであるalphabetatransformed parametersブロックで宣言されて定義されます. もし, alphabetaの値に興味がなければ, 代わりに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));
  ...
}

もし, alphabetaの値に興味があるならば, これらをtransformed parametersブロックで定義してからmodelブロックで使うとよいでしょう.

ヤコビアンは必要ない

分布を与えるのではなく, 変換パラメータ(transformed parameter)を使う場合は, 変換に対するヤコビアンの調整は不要です. 例えば, ベータ分布の例ではalphabetaは適切な事後分布を持ちます.

17.3. 変数変換

パラメータの変換が確率分布によって特徴づけられるときに, 確率の調整が必要な「変数変換」となります. 標準的な教科書の例は対数正規分布です. 確率変数y > 0の分布が対数正規分布に従うとき, yの対数であるlogyは正規分布に従います. 分布がlogyに割り当てられている点に注意です.

変数変換は変換によるゆがみを考慮に入れるため, 確率の調整が必要となります. この調整がうまくいくためには, 一変量の変数変換が台(support)において単調かつ至るところで微分可能でなければなりません.

一変量の変数変換では, 変換の微分の絶対値を使って確率をスケーリングする必要があります(一変量の変数変換のより正確な定義については58.1節を見てください).

対数正規分布の場合, yの対数が平均μ・標準偏差σの正規分布に従うとすると, 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はアンダーフローを防ぐため, 対数スケールで動作します. そのため, 以下の形で扱います.


logp(y) = logNormal(logy ∣ μ, σ) − logy

Stanでは変数変換はサンプリング文において適用されます. 曲率を調整するために, 変換の微分の絶対値の対数が対数確率の合計に足しこまれます. Stanでは対数正規分布は次のように直接的に実装できます. 9

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では定数を除いた対数確率だけが必要となるからです.

変数変換 vs. 単純な変換パラメータ

この節では変数変換と単純な変換パラメータの違いを説明します. 単純な変換パラメータを使う場合, パラメータをサンプリングし, そのあとでサンプリングした値を変換します. ところが変数変換の場合, パラメータを変換し, そのあとでサンプリングします. 後者だけがヤコビアンの調整を必要とします.

確率関数がサンプリング文で表現されているかどうかは関係ありません. 例えば, 以下のようなサンプリング文で表現しても

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$$

多変量の変数変換

多変量の変数変換の場合には, 変換のヤコビアンの対数を対数確率の合計に足しこまなければなりません(多変量の変換とヤコビアンのより正確な定義については58.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));
  ...

17.4. 変化する境界をもつvector

Stanではコンテナの型に対する制約は, 一つの下限と一つの上限しか宣言できません. しかし, あるvector型のパラメータの各要素の下限値が, 同じようにvector型で与えられているとしましょう. すると, 要素ごとの変換とそのヤコビアン(これらは58章に記述があります)をStanで計算する必要があります.

例えば, 下限を表すvectorLを持つ, パラメータのvectorαを考えてみましょう. 以下のプログラムでは制約のないraw(生の)パラメータを宣言し, それからヤコビアンを考慮してrawパラメータを明示的にαに変換します.

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); // ヤコビアンの対数
  ...

調整項は, αrawからα = L + exp(αraw)への変換に関するヤコビ行列の行列式の対数になります. この場合はヤコビ行列が対角行列になるのでシンプルになります(詳しくは58.2節をみてください). ここで, Lαrawに依存しないパラメータを含むことすらできます. もし境界がαrawに依存するならば, 依存性を考慮に入れてヤコビアンを計算しなおす必要があります.

18. 自作の確率分布関数

自作の確率分布もStanの中で直接実装することができます. 必要なことは対数確率の合計を累積する(インクリメントする)ことだけです. 以降では2つの例を扱います.

18.1. 例

三角分布

単純な例は三角分布です. その密度関数は二等辺三角形のような形をしており, 指定された境界において角をもち, 密度を積分すると1になるという制約から高さが決まります. もし, α ∈ ℝβ ∈ ℝが境界で, α < βとすると, y ∈ (α, β)は以下で定義される密度を持ちます.


$$\mathsf{Triangle}(y \mid \alpha,\beta) = \frac{2}{\beta - \alpha}\left( 1 - \left| y - \frac{\alpha + \beta}{\beta - \alpha} \right| \right)$$

もし, α =  − 1, β = 1, y ∈ ( − 1, 1)ならば, この式は以下のように簡単になります.


Triangle(y ∣  − 1, 1) = 1 − |y|

Triangle( − 1, 1)からサンプリングするため, 以下のStanの実装を考えてみましょう. 10

parameters {
  real<lower=-1,upper=1> y;
}
model {
  target += log1m(fabs(y));
}

唯一のスカラーのパラメータであるyが区間(-1,1)に入るように宣言されています. 対数確率の合計に, すべてのパラメータの同時対数確率(すなわちlogTriangle( − 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)の対数確率(すなわち負の無限大)を定義することで, 全体の値に拡張した対数確率関数を考えてみましょう.

target += log(fmax(0.0,1 - fabs(y)));

yに制約を課した元のプログラムと比べて, これは非効率で遅くて数値計算上不安定です. しかし, yについての制約を取り除いても, プログラムはコンパイルされ, 算術例外が発生することなしに実行されるでしょう. しかし, 適切にサンプリングはされないでしょう. 11

指数分布

もし, 仮にStanに指数分布が用意されていないとすると, 指数分布は以下の代入文を用いて直接コーディングできます.

target += log(lambda) - y * lambda;

ここでlambdaはスケールの逆数で, yはサンプリングされた確率変数を表します. このコーディングは任意のlambdayについてうまくいきます. これらの両方もしくはどちらか一方がパラメータでもデータでもよいですし, ローカル変数でも大丈夫です.

前の段落の代入文は, 以下のサンプリング文によって生成されるC++コードと非常によく似たC++コードを生成します.

y ~ exponential(lambda);

注目すべき違いが二つあります. 一つ目は, サンプリング文はlambdaが正でyが非負であることを確認するため入力をチェックします(どちらも非数でないかもチェックします).

二つ目の違いは, もしlambdaがパラメータ・変換パラメータ(transformed parameter)・modelブロックの局所変数のいずれでもなければ, サンプリング文は賢いので定数であるlog(lambda)の項を落とします. 結果は同じ事後分布になります. なぜなら, Stanは付加定数を除いた対数確率だけを必要とするからです. もし, lambdayの両方とも定数ならば, サンプリング文は両方の項を落とします(しかし, 入力が妥当かどうかのチェックは変わらず行います).

19. ユーザー定義関数

この章ではユーザーの視点から例を交えてユーザー定義関数を説明します. 厳密な仕様は28章を見てください. ユーザー定義関数を使うと, 計算をカプセル化して一つの名前をつけることができ, その名前を使ってどこでも呼び出すことができます. 同様に, 関数を使うと, 複雑な手続きをより理解しやすい構成要素に分解することができます. 適切な名前をつけた関数を使用したモジュール性の高いコードは, 大きな一枚岩のプログラムよりも理解しやすいです. たとえ一枚岩のプログラムにコメントを多くつけたとしてもです. 12

19.1. 基本的な関数

ここではユーザー定義関数の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文で終わる必要があります. これがどのように実行されるかについての詳細は28.7節を見てください.

reject

Stanのreject文は, プログラムの実行中に遭遇したエラーや問題のある値を報告するための仕組みを提供します. reject文では, 引用符のついた文字列またはStanの式を任意の数だけ引数にとることができます. この文は, 何らかの処理で妥当でない結果が出たことを検出するために, 典型的には条件文の中で使われます.

この文の使い方を説明するために, 19.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;
  }
}

棄却の効果は関数が呼び出されたブロックに依存します. 詳しくは27.9節を見てください.

関数のための型の宣言

関数の引数と返値の型の宣言に, 変数のサイズは書きません. また, 値の制約も含みません. 図19.1に一覧にしましたので見てください.

型の宣言
型の宣言

図19.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が使われます. 13関数の宣言において返値の型や引数の型に上下限や制約のある型を使うことは許されていません.

関数の宣言における配列の型

引数としての配列は独自の構文を持っており, その構文はこのマニュアルにおいても関数を区別するために使われています. 例えば, 2次元配列に作用して1次元配列を生成する関数は以下のように宣言されるでしょう.

real[] baz(real[,] x);

1次元配列(上のコードの返値)には[ ]という記法が使われており, 2次元配列には[ , ]という記法が使われています. 3次元配列には[ , , ]という記法が使われ, 4次元配列以降も同様です.

関数はmatrix型とvector型を含む任意の型の配列をサポートしています. 他の型と同様に, 制約をつけることはできません.

19.2. ステートメント(文)としての関数

場合によっては, 値を返さない関数にするのが理にかなっています. 例えば, 行列の下三角成分を表示する処理は以下のように定義されるでしょう.

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);
  ...

19.3. 対数確率を累積する機能にアクセスする関数

_lpで名前が終わる関数の中では, サンプリング文とtarget +=文を使うことができます. 他の関数名では使うことはできません. このアクセスのため, _lpで名前が終わる関数はtransformed parametersブロックとmodelブロックの中でしか使うことができません.

以下の関数の例では, 係数のベクトルの事前分布に標準正規分布を設定し, 位置(分布の中心)とスケールの事前分布も設定します. そして, 引数のベクトルbeta_rawを中心muとスケールsigmaに従って平行移動してスケーリングして返します. 中心化に関するさらなる情報は22.2節を見てください.

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);
  ...

19.4. 乱数生成器として振る舞う関数

_rngで終わる名前をつけることで, (疑似)乱数生成器(pseudo random number generator, 略してPRNG)として振る舞うようにユーザー定義関数を宣言することができます. _rngで終わる名前をつけると, その関数の中ではすべてのPRNG関数を含むビルトイン関数にアクセスでき, _rngで終わるユーザー定義関数にもアクセスすることができます. _rngで名前が終わる関数だけが, その中でビルトインのPRNG関数にアクセスすることができます. そのため, _rngで名前が終わる関数は, 他のPRNG関数と同様にgenerated quantitiesブロックの中でしか使うことができません.

例えば, 以下の関数は(N × 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をその多変量正規分布から抽出するかもしれません.

19.5. ユーザー定義の確率分布関数

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番目の引数は離散値でなければならない(整数値または整数値の配列である)ことです.

19.6. 多重定義の関数

Stanではユーザー定義関数の多重定義は許されていません. 同じ関数名で引数の型が異なる, 2つの別の関数を定義することはできないということです.

19.7. 関数にドキュメントをつける

理想はインターフェースのレベルで, 関数にドキュメントをつけることでしょう. 関数の説明文のためのStanのスタイルガイドは, Doxygen(C++)とJavadoc(Java)の自動文書化システムで使われているのと同じフォーマットに従います. これらのフォーマットでは, 何らかの説明文からはじまり, それから引数の変数とそれらの型および返値を表示します.

例えば, 19.4節で扱ったデータの行列を生成する関数のドキュメントは以下のようになるでしょう.

/**
* データの行列を返す. 行はアイテムに対応する. また1番目の列は切片を表す1で埋まり,
* 残りの列は標準正規分布から抽出された乱数で埋まっている.
*
* @param N は行数で, データのアイテムに対応する.
* @param K はアイテムあたりの切片項込みの予測変数の数.
* @return シミュレーションで生成された予測変数の行列.
*/
matrix predictors_rng(int N, int K) {
  ...

コメントは/**で始まって*/で終わります. そしてコメントの各行にアスタリスク(*)があります. @paramのあとに引数の変数名が続き, 関数の引数を説明します. @returnは返値を表します. Stanは(まだ)JavadocやDoxygenのような自動文書生成システムを持っていないので, Stanのパーサにとってはこのコメントは単に/*から始まって*/で終わる大きなコメントに見えます.

例外を発生させる関数については, @throwsを使って例外の説明をしましょう. 14例えば,

...
* @param theta
* @throws もしthetaの要素が1つでも負なら例外を発生します.
*/
real entropy(vector theta) {
  ...

たいてい例外のタイプもドキュメントにしますが, Stanの言語の一部として用意されていないので, 書く必要はありません.

19.8. 関数の型の要約

関数は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ブロックだけで使うことができます.

19.9. 再帰関数

Stanは再帰関数の定義をサポートしています. 再帰関数は場合によっては有用です. 例えば, 行列の累乗(An)を考えましょう. (An)は正方行列(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が矛盾なく使えるように, 関数の定義の前に宣言だけは必要になります. 15次の条件節を加えることで, ベースケースまで再帰が到達しないようになり, より効率的になるでしょう.

else if (n == 1)
  return a;

20. 微分方程式を解く

Stanでは常微分方程式(ordinary differential equations, 略してODE)の系を解くビルトインの仕組みがあります. Stanでは2つの異なるソルバーが用意されています. 1つはstiffでない系を, もう片方はstiffの系を解くためにチューニングされています.

stiffな常微分方程式の系に関する議論は20.4節を見てください. 短く言えば, stiffな系のソルバーは遅いけれどより頑健です. どれくらいそういう傾向があるかは, 解く系やパラメータ空間の領域に依存します. StanのODEソルバーの引数と返値については38章に記載があります.

20.1. 例:単純な調和振動子

ODEの系の具体例として調和振動子を考えましょう. 調和振動子の系は平衡状態の位置とそこからのズレに比例して戻ろうとする力(摩擦の影響を含む)で特徴づけられます. 系の状態は位置と運動量を表すペアである(y = (y1, y2))で記述されます(すなわち相空間上の点になります). 時刻による系の変化は次の微分方程式で与えられます. 16


$$ \frac{d}{dt}y_{1} = y_{2} \quad \frac{d}{dt}y_{2} = -y_{1} - \theta y_{2} $$

この状態式はある与えられた時刻における系の状態を, 初期状態・初期状態からの経過時間・系のパラメータの関数として暗黙のうちに定義しています.

初期条件が与えられた場合の解法

系のパラメータ(θ)の値と時刻(t0)における初期状態(y(t0))が与えられると, ある時刻の系列(t0 < t1 < t2 < ⋯)における(y(t))を求めるために, 数値計算で解の時間発展をシミュレーションできます.

20.2. 常微分方程式の系をコーディングする

Stanでは厳密に引数と返値が決められた関数によって, ODEの系は直接的にコーディングされます. 例えば, (20.1)式で与えられた単純な調和振動子は, Stanでは次のようにコーディングされるでしょう(ユーザー定義関数のコーディングに関するさらなる情報は19章を見てください).

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;
}

この系を表す関数は, 時刻treal型の値), 系の状態yreal型の配列), 系のパラメータthetareal型の配列), 実数値のデータである変数x_rreal型の配列), 整数値のデータである変数x_iint型の配列)を引数にとります. この関数は系の状態yの時間についての微分の値の配列を返します. 微分の値は時刻t・状態yで評価したものです. ここでコーディングした単純な調和振動子は時間に依存する方程式ではありません. すなわち, tdtdtの定義に現れません. 単純な調和振動子はreal型およびint型のデータを使いません. しかし, 上のコードで示した関数の引数と返値の型に厳密に一致するように, これらの使わない引数も系を表す関数の引数として宣言に含める必要があります.

厳密な関数の引数と返値の型

系を定義する関数はこれらの引数と返値の方を必ず持っている必要があります. このため, 系がデータやパラメータを含まない場合には, データやパラメータに0の長さの配列を渡すことになるかもしれません. データを表す変数に全く依存しない単純な調和振動子の完全な例は図20.1にあります.

不連続なODEの系の関数

ODEのソルバーは状態yの関数に不連続点があっても積分できます. ただし, 不連続点の近くの点の精度は問題になるかもしれません(多数の小さな計算ステップを要します). そのような不連続点の例は薬物動態モデルの中のラグです. 体内の薬物濃度は, あるラグ時間を(t)とすると, (0 < t < t)を満たす時刻tに対しては濃度がゼロとなる一方で, (t ≥ t)を満たす時刻tに対しては非ゼロになります. 例を挙げると, コードは以下のようになるでしょう.

if (t < t_lag)
  return 0;
else
  ... 非ゼロの値を返す ...;

さまざまな初期時刻

StanのODEソルバーでは引数の初期時刻は定数である必要があります(すなわち, データか変換データ(transformed data)か定数の関数となっている必要があります). これは, 一般に, 初期時刻をパラメータにしてintegrate_ode関数を使うことはできないことを意味します. したがって, 一般に, 測定値からODEの系の初期時刻を推定することはできないことを意味します.

20.3. 測定エラーモデル

有限の時点における, ノイズを含む系の状態の測定結果が与えられた場合に, 統計モデルや微分方程式は力学系のパラメータと初期状態の両方あるいはいずれか一方を推定するために使うことができるでしょう.

例えば, 単純な調和振動子において, パラメータの値が(θ = 0.15)で初期状態が(y(t = 0) = (1, 0))である場合を考えましょう. 今, 系が10時点(t = 1, 2, ⋯, 10)において測定されたとしましょう. それぞれの時点における(y(t))の測定には, (y1(t))(y2(t))のどちらの軸にも(Normal(0, 0.1))に従う独立な誤差が入るとしましょう. そのような測定のプロット例を図20.1に示します.

単純な調和振動子における軌跡
単純な調和振動子における軌跡

図20.1: 単純な調和振動子において, パラメータの値が(θ = 0.15)で初期状態が(y(t = 0) = (1, 0))である場合の軌跡. 横軸・縦軸の両方に…(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・解の時刻tstsで指定した時刻における状態が求まる)・パラメータtheta・実数値のデータx_r・整数値のデータx_iが与えられた際に, 関数shoで定義された系の解を求めています.

ここではODEソルバーはgenerated quantitiesブロックで呼ばれており, (10 × 2)の配列で解y_hatを生成しています. 解y_hatには, 正規分布に従う疑似乱数生成器であるnormal_rng関数を使って測定誤差が加えられています. 解の配列の行の数は, 指定した解の時刻であるtsの大きさと一致しています.

データ vs パラメータ

他の関数とは違い, 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, ⋯, 10)の10個の観測データ点は, 初期状態やノイズのスケールといったODEのパラメータを確実に推定するのに十分な数です.

20.4. stiffなODE

常微分方程式のstiffな系は, 傾きをもとにしたステップを使うソルバーで解こうとすると数値計算上難しいことでおおまかに特徴づけられます. stiffさは典型的には, 状態の座標空間でさまざまな曲率があることが原因です. 例えば, 1つの成分の時間変化が他の成分の時間変化よりもオーダーが異なるほど遅い場合です. 17

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でないソルバーを使おうとすると, 小さなステップ幅と非常に多数のステップを要するために実行は失敗するでしょう.

20.5. ODEソルバーの制御パラメータ

上で示したソルバーの呼び出しではデフォルトの制御パラメータの設定を使っていました. 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(106))です.

許容値

相対許容値と絶対許容値はソルバーによって生成される解の精度を制御します. 相対許容値は解の値と比べた場合の比を制御し, 一方で絶対許容差は解の値そのものの誤差の最大値を制御します. より小さな許容値を設定するとより精度のよい解となります. また, より小さな許容値はより多くの計算時間を要します.

感度分析

許容値は十分に小さく設定すべきです. それ以上小さくしてもStanのプログラムによって生成される事後サンプルの統計的な特徴はほとんど変わらないことが目安です.

最大ステップ数

最大ステップ数はシミュレーションが暴走するのを止めるために使われます. MCMCにおいては, 悪いジャンプが採択されたとき, 特にwarmup期間において, 暴走が起こる可能性があります. stiffでないソルバーを使う場合には, 悪いジャンプによって結果的に, パラメータ空間のstiffな領域にサンプラーが跳びこむかもしれません. stiffな領域では適度な許容値を満たすために非常に小さなステップサイズと非常に多数のステップが必要となって, シミュレーションが暴走するかもしれません.

21. 問題のある事後分布

数学的に言うと, 正則な事後分布であればベイズ推定はできていて, 話はそこで終わりです. 有限な分散さえも, あるいは有限な平均さえも必要ではありません. 必要なのは有限な積分だけです. それにもかかわらず, モデリングは厄介な仕事で, 経験を積んだモデル作成者でも非正則な事後分布18ができるようなモデルをコーディングしてしまうことがあります. さらに, 数学的には正しいにも関わらず, 実用的には挙動がおかしい事後分布もあります. この章では, 問題のある事後分布を推定してしまうモデルについて, ベイズ推定一般として, あるいはStanでの実用面から議論します.

21.1. 回帰での予測変数の共線性

この節では, 識別可能性に関する古典的な問題について議論します. この問題は, 事後密度を尾根状にし, サンプリングと推定の両方をめちゃくちゃにしてしまうというものです.

共線性の例

余分な切片

共線性の最初の例は, 余分な切片パラメータを含むという人工的なものです. 19 n ∈ 1 : Nについての観測値yn, 2つの切片パラメータλ1λ2, スケールパラメータσ > 0があり, サンプリング分布が以下のようであるとします.


yn ∼ Normal(λ1 + λ2, σ)

任意の定数qについて, qλ1に加え, λ2から引くならば, yのサンプリング密度は不変です.


p(y ∣ λ1, λ2, σ) = p(y ∣ λ1 + q, λ2 − q, σ)

その結果, 非正則一様事前分布p(μ, σ) ∝ 1からは非正則事後分布が導かれます. この非正則性が生じるのは, λ1 + q, λ2 − q 20の近傍では, どんなqについても質量が同じになるからです. したがって, λ1 = 1000000000かつλ2 =  − 1000000000の近傍でも, λ1 = 0かつλ2 = 0の近傍と同じ時間がサンプラーには必要になりますし, さらにもっと離れた値でも同様です.

このモデルの周辺事後分布p(λ1, λ2 ∣ y)はしたがって非正則です. 21 この非正則性は, 図21.1の左側に図示したように, 視覚的には事後密度の尾根として表されます. このモデルの尾根は, cをある定数として, λ2 =  − λ1 + cという直線22に沿ってできます.

このモデルを, 単一の切片パラメータμをもち, 以下のサンプリング分布をもつ単回帰と比較しましょう.


yn ∼ Normal(μ, σ)

この場合には, 非正則事前分布であっても, 異なる値を持つ少なくとも2つのデータ点ynがある限り事後分布は正則となります.

IRTモデルにおける能力と難易度

項目反応理論(IRT)モデルで, 生徒j ∈ 1 : Jが能力αjを持ち, 試験項目i ∈ 1 : Iが難易度βiを持つとします. 観測データはI × J次元配列で, エントリーyi, j ∈ {0, 1}は, yi, j = 1のとき生徒jが問題iに正答したことを示すようにコード化します. このデータのサンプリング分布は以下のようになります.


yi, j ∼ Bernoulli(logit − 1(αj − βi))

任意の定数cについて, 定数cを能力すべてに加え, かつ難易度すべてにも加えると, yの確率は変わりません. 23


p(y ∣ α, β) = p(y ∣ α + c, β + c)

このため, 上で議論した2つの切片を持つ回帰の多変量版が出現することになります.

一般的な共線性のある回帰予測変数

共線性の問題の一般型は, 回帰の予測変数が共線であるときに発生します. 例えば, 直線回帰のサンプリング分布を考えます.


yn ∼ Normal(xnβ, σ)

yN次元の観測値ベクトル, xN × Kの予測変数行列, βK次元の係数ベクトルです.

ここで, 予測変数行列の列kが列kの定数倍になっているとします. すなわち, すべてのnについてxn, k = cxn, kとなるような定数cが存在するとします. この場合, 係数βkβkは予測値を変えずに共変動できます. そのため, 任意のd ≠ 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) $$
24

予測変数行列の列が, 上の議論のような完全な共線ではなくとも, 共線に近ければ推定に同じような問題が発生します.

IRTでの乗数の問題

IRTモデルで, 各々の質問に識別力パラメータδiを加えるとします. データをサンプリングするモデルは次式です.


yi, j ∼ Bernoulli(logit − 1(δi(αj − βi)))

任意の定数c ≠ 0について, δc倍し, αβcで割っても, 尤度は同じです.


$$ p(y \mid \delta, \alpha, \beta) = p(y \mid c\delta, \frac{1}{c}\alpha, \frac{1}{c}\beta) $$

もしc < 0ならば, 密度を変えずに, αおよびβ, δのすべての成分の符号が反転します.

SoftmaxでのK vs. K-1パラメータ

K単体(すなわち, 合計すると1になる, 非負の値からなるK次元ベクトル)をパラメータ化するためには, K − 1個のパラメータだけが必要です. なぜなら, K番目のパラメータは, 最初からK − 1番目までの合計を1から引いた値になるからです. したがって, θK単体とすると次式が成り立ちます.


$$ \theta_K = 1 - \sum_{k=1}^{K-1}\theta_k $$

softmax関数(35.11節参照)は, 線形予測子のK次元ベクトルαK単体θにマッピングします. つまりθ = softmax(α)で, 定義は次式です.


$$ \theta_k = \frac{\exp(\alpha_k)}{\sum_{k'=1}^{K}\exp(\alpha'_k)} $$

softmax関数は多対1関数です. パラメータαに制約がないと, 識別可能性が失われます. とくに, すべてのαkに定数を足したり引いたりしても, おなじ単体θとなります.

不変さを軽減する

前の節で議論した例はすべて, データの確率密度を変えないままパラメータの平行移動や拡大縮小ができるというものです. この問題を軽減する方法はいくつかあります.

余分なパラメータや予測変数を取り除く

複数の切片, λ1λ2がある場合には, 余分な切片を取り除くのが最も単純な解決法です. これによりモデルは, 単一の切片パラメータμを持ち, サンプリング分布はyn ∼ Normal(μ, σ)となります. 同じ解決法を, 共線性の問題にも使えます. 予測変数行列xから列を1つ取り除くだけです.

ピン留めパラメータ

識別力パラメータのないIRTモデルは, パラメータの1つを固定した値(普通は0)にピン留めすることで固定することができます. 例えば, 最初の生徒の能力α1を0に固定することができます. このとき, その他の生徒すべての能力パラメータは生徒1に対する相対値と解釈できます. 同様に難易度パラメータは, 生徒1の解答能力に対する相対値と解釈されます.

この解決法は, 質問の識別力パラメータδiを導入すると発生する, 乗数による不変性を扱うには十分ではありません. この問題を解決するには, 識別力25パラメータの1つ, たとえばδ1にも制約をつけなくてはなりません. 乗数による不変性は, 加算とは異なり, 非零の値に制約しなければなりません. 都合が良いのは1とすることです. このとき, 識別力パラメータはすべて, 項目1の識別力に対する相対値として解釈されるでしょう.

softmax(α)の多対1の性質は, αの成分の1つをピン留めすることで軽減するのは普通です. 例えば, αK = 0と固定します. そうすると, K − 1次元の制約のないパラメータから, K単体へ, 1対1対応となります. これが, 単体として制約されるパラメータをStanで定義するおおまかな方法です. 正確な定義は58.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);
}
事前分布を加える

ここまで, パラメータの事前分布が非正則一様事前分布であるとしてモデルを議論してきました.

こうした不変性の問題に対する, より一般的なベイジアンの解決法は, パラメータに正則な事前分布を与えることです. 加法的でも, 乗法的でも, どちらの不変性に由来する問題でも, この方法を使って解決できます.

例として, 複数の切片に正規分布を事前分布として与えます.


λ1, λ2 ∼ Normal(0, τ)

τを定数値のスケールとすると, 事後最頻値がλ1 = λ2となる点に位置することが保証されます. なぜならこれにより, logNormal(λ1 ∣ 0, τ) + logNormal(λ2 ∣ 0, τ)が最小化されるからです. 26 2つの切片を持つモデルに事前分布を加えたものを図21.1の中央に示します. 図21.1の右側は, 単一の切片を持つように再パラメータ化した結果です.

図21.1 図21.1: 事前分布なしの2切片パラメータ化, 平均0標準偏差1の正規事前分布の2切片パラメータ化, 事前分布なしの1切片再パラメータ化のそれぞれの事後分布. 3つの場合とも, 平均0標準偏差1の正規分布から抽出された100データ点について事後分布をプロットしています. 左)2切片パラメータ化は, 北西方向と南東方向とに無限に伸びる尾根状の非正則事後分布27となります. 中)平均0標準偏差1の正規事前分布を切片に対して加えると正則事後分布になります. 右)事前分布なしの単一切片パラメータ化も正則事後分布が得られます.

K単体パラメータ化θ = softmax(α)を, 制約なしのK次元ベクトルαについて識別するための別の方法は, αの各成分が, ある固定した位置パラメータをもつ事前分布に従うとすることです(すなわち, 位置が変わるような階層事前分はとくに避けるようにします). αK = 0とピン留めする方法では, K番目の値への相対値としてK − 1個の値をモデリングしていました. 事前分布に基づく方法では, そうではなく, K個の値を等しく対称的に扱ってモデリングします. 一方で, ピン留めのパラメータ化の方が通常は, (事前分布で制約された)空間内の動きに余分な自由度がないので, 統計学的にはより効率的です.

漠然事前分布, 強情報事前分布, 弱情報事前分布

不変性を解決するために事前分布を加える際には注意が必要です. 事前分布の幅が広すぎると(すなわち漠然すぎると), 理論的には解決するとしても, 実際にはサンプラーがやはり動くのに苦労するということになるでしょう.

理想的には, モデリングする問題についての本質的な知識に基づいて, 現実的な事前分布を定式化するのがよいでしょう. そのような事前分布なら, 事前の知識に基づいた適切な強さを選択できます. 強い事前の情報があるときには, 強情報事前分布を使うとよいでしょう.

強い事前の情報がないときには弱情報事前分布を使って, 事後分布中でデータを圧倒しないことと, 推定の計算を制御することとの適切なバランスをとります. たいていの問題では, 推定値がどのくらいのスケールになるか, 少なくとも何らかの見解をモデル作成者は持っているでしょう. そして, データを圧倒しないような, しかし事後分布を十分に制御できるような, 識別可能性の目的にあう事前分布を選ぶことができます.

事前分布は, IRTモデルにおける加法的な不変性を制御するためにも同じように使えます. 典型的には単純に, スケールを制御する強い事前分布を生徒の能力パラメーターαに設定することで, 基本的なIRTモデルにおける加法的な不変性と, 質問項目の識別力パラメータを含む拡張モデルにおける乗法的な不変性を制御します. そのような事前分布は, 解析対象についての事前知識を増やすものではありません. そして, 質問項目の難易度についての事前分布は, 解析対象の事前知識に基づいて, 情報のある事前分布もしくは弱情報事前分布を選ぶとよいでしょう.

21.2. 混合分布モデルでのラベルスイッチング

回帰モデルにおいて共線性がある場合, 事後分布を最大化するパラメータの値は無限個あります. その一方で, 混合分布モデルの成分を入れ替えることができる場合, 事後分布を最大化するパラメータの値は有限個ですが, 複数あるために問題のある事後分布となります.

混合分布モデル

2つの位置パラメータμ1μ2をもつ正規混合分布モデルを考えます. スケールはともにσ > 0で, 混合率をθ ∈ [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(θ, μ1, μ2, σ ∣ y) = p((1 − θ), μ2, μ1, σ ∣ y)

この問題は, クラスタリングモデルのように, 混合成分Kの数が大きくなるにつれて悪化します. K!個の同一の事後最大値ができます.

収束モニタリングと有効サンプルサイズ

事後分布の収束と有効サンプルサイズの分析も混合分布モデルでは難しい問題です. 例えば, Stanが報告する$\hat{R}$収束統計量も, 有効サンプルサイズの計算も, ラベルスイッチングで不正確になります. 問題は, これらの計算の鍵となる要素である事後平均がラベルスイッチングの影響を受けることにあります. μ1の事後平均がμ2の事後平均に等しくなり, θの事後平均が, データによらず常に1/2になってしまいます.

不変の推定もある

ある意味, 混合成分のインデックス(あるいはラベル)は重要ではありません. 事後予測推測は, 混合成分が識別できなくとも可能です. 例えば, 新しい観測値の対数確率は, 混合成分が識別されるかに依存しません. ラベルスイッチングが起きるモデルにおいて正しいベイズ推定値とは, ラベルスイッチングが起きても不変なものだけです. パラメータの事後平均は, ラベルスイッチングに対して不変ではないので意味がありません. 例えば, 2成分の混合分布モデルにおけるθの事後平均は常に1/2となるでしょう.

非常に多峰な事後分布

この場合には, 理論的には, 事後予測推測に含まれる積分はすべてうまくいくでしょうから, 推定の問題はないはずです. 実用上の問題は計算にあります.

そのような不変の推定が実用的にも実行可能であるかどうかは, まったく別の問題です. たった1つの事後最頻値を見つけるのにも難儀するのがほぼ毎度のことです. ましてや, 確率質量に従って, 複数ある局所最大値のうちの隣のものと探索のバランスをとるなど, さらに難儀なことです. ギブズサンプリングでは, μ1が, 現在のμ2θの値に条件づけられてサンプリングされているときに, 今まで到達したことがない新たな局所最頻値へと移動することはなさそうです. HMCとNUTSではその場合, 2つの局所最頻値のまわりの2つの「くぼみ」のうちの1つにサンプラーがはまってしまうことにより, ランダムな運動量の割り当てからは, 一方の局所最頻値から他方の局所最頻値へと動くための十分なエネルギーを集められないということになります.

正則な事後分布であっても, 混合分布モデルで成分の数が増える場合のように, 指数関数よりも速く局所最頻値の数が増える場合には, 既知のサンプリングと推定の技法のすべてが非効率的となることが知られています.

修正のハック

いくつかのハック(つまり「トリック」)が, ラベルスイッチングにより起こる問題への実用的な対処のために提案され, 採用されています.

パラメータの順序の制約

よく使われる戦略の1つは, 成分を識別するようにパラメータに制約を課すことです. 例えば, 上で議論した2成分正規混合分布モデルにμ1 < μ2という制約を課すことを考えましょう. この方法では, 反対の順序μ1 > μ2に実質的な確率質量が存在する場合に問題が起こりえます. この場合, その制約によって事後分布が影響を受け, μ1μ2における真の事後分布の不確実性は, 制約のあるモデルではとらえることができません. さらに, ある事象の生起確率を事後に推定する標準的な方法においても問題が発生します. 例えば, Pr[μ1 > μ2]を推定するためM個の事後サンプルを使おうとすると失敗します. これは, 事後分布がモデルの制約を守るため, 次式の推定量による推定値が0になるからです.


$$ \Pr[\mu_1 > \mu_2] \approx \sum_{m=1}^{M}\mathrm{I}(\mu_1^{(m)} > \mu_2^{(m)}) $$

単一の最頻値のまわりでの初期化

よく使われる別の方法は, 1個の連鎖で動かすか, 現実的な値の近くでパラメータを初期化することです. 28 もっともな初期値を見つけて, マルコフ連鎖内でスイッチが起きなければ, これは, 強い制約を課す方法よりもうまくいくことがあります. 結果は, すべての連鎖が, 事後分布における特定の局所最頻値の近くに張り付きます.

21.3. 混合分布モデルで成分がつぶれる

サンプリングまたは最適化の間に, 混合分布モデルの2つの混合成分がつぶれて同じ値になることがあります. 例えば, K個の正規分布からなる混合分布で, i ≠ jについて, μi = μjかつσi = σjとなることがあります.

これは典型的には, MCMCの初期化または最適化によりサンプリングの初期に発生するか, あるいはMCMC中のランダムな移動により発生するでしょう. 与えられた抽出(m)において, いったん複数のパラメータが同じ値になってしまうと, 現在のパラメータの値と, 成分がつぶれていないときの値との間に質量の谷があるために, そこから出られなくなることがあります.

ウォームアップ中のステップサイズを小さくすることや, 各混合成分がどのインデックスになりやすいかを定める強い事前分布が役に立つかもしれません. もっと極端な手段は, いくつかのパラメータがつぶれる可能性を考えて, 混合成分を追加しておくことです.

一般にKが1よりも大きくなると, 混合分布モデルで, K個の正しい混合成分を正確に復元することはきわめて困難です. (そうです. 2成分の混合分布でさえ, この問題は起こりえます. )

21.4. 上下限のない密度での事後分布

場合によっては, パラメータがある極や境界に近づいて, 際限なく事後密度が大きくなるということがあります. そのようなときは, 事後最頻値はありませんし, サンプリングされたパラメータが制約の境界に近づくので, 数値的安定性の問題が起こることがあります.

スケールが変わる混合分布モデル

そのような例の1つは, スケールσ1σ2が成分によって変わる2項混合分布モデルです. 位置はμ1μ2とします. この状況で, あるnについて, σ1 → 0かつμ1 → ynで, 密度が際限なく大きくなります. すなわち, 混合成分の1つの質量のすべてが, 単一のデータ項目ynの周囲に集中するのです.

ゆがみのあるデータと弱い事前分布のベータ二項モデル

際限のなく大きくなる密度のもうひとつの例は, Beta(ϕ ∣ 0.5, 0.5)のような事後分布で発生するものです. これは非常に「弱い」ベータ事前分布がデータのない群に使われているときに発生することがあります. この密度はϕ → 0かつϕ → 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で, 各々のyn = 1であれば, 事後分布はBeta(ϕ ∣ 9.5, 0.5)になります. この事後分布はϕ → 1で際限なく大きくなります. にもかかわらず, 事後分布は正則で, 事後最頻値がないにも関わらず, 事後平均は正確に0.95と明確に定義されます.

制約のあるスケール vs. 制約のないスケール

この問題ではStanは, 制約のある(0, 1)空間を直接サンプルすることはしませんし, 制約のない密度の値を直接あつかうこともしません. そうではなく, 確率の値ϕが( − ∞, )にロジット変換されます. 境界の0と1はそれぞれ − ∞になります. ヤコビアンの調整はStanが自動的に行なうので, 制約なしの密度は正則であることが保証されます. (0, 1)の特定の場合の調整はloglogit − 1(ϕ) + log(1 − logit − 1(ϕ))です. 29導出は58.4節を参照してください.

しかし, 2つの問題がまだ出てきます. 1つ目として, ϕの事後質量が境界の1近くにある場合には, ロジット変換されたパラメータは非常に長いパスを掃き出すしかなくなり, そのためno-U-turnサンプラーが課すU-turn条件ばかりが満たされ, うまくサンプリングできなくなることがあります. 2つ目の問題は, 制約のない空間から制約のある空間への逆変換のときに, 0にアンダーフローしたり, 1にオーバーフローしたりするおそれがあることです. これは, 制約のないパラメーターが無限でなくても発生します. 同様の問題は, ロジスティック回帰の期待値の項でも発生します. ベルヌーイ分布と2項分布ではロジットスケールのパラメータ化がより安定なのはこの理由によります.

21.5. 上下限のないパラメータでの事後分布

場合によっては, 事後密度は際限なく大きくはならないのに, 密度のとる値を次第に増加させながらパラメータが際限なく大きくなることがあります. 前の節で議論した, 密度が際限なく大きくなるモデルと同様に, そのようなモデルにも事後最頻値はありません.

ロジスティック回帰での分離可能性

N個の結果変数yn ∈ {0, 1}と, N × K行列の予測変数x, K次元の係数ベクトルβからなるロジスティック回帰モデルを考えます. サンプリング分布は次式です.


yn ∼ Benoulli(logit − 1(xnβ))

ここで, 予測変数行列のk列が, yn = 1のときだけにxn, k > 0となるとします. この条件は「分離可能性」として知られます. この場合, 観測データについての予測の正確さは, βk → ∞となるにつれ, 改善され続けます. yn = 1のとき, xnβ → ∞で, logit − 1(xnβ) → 1となるからです.

分離可能性の問題のあるときは, 尤度に最大値はありませんから, 最尤推定値もありません. ベイズの観点からいうと, 事後分布は非正則ですから, したがってβkの周辺事後平均も定義されません. ベイズモデルではこの問題は通常, βに正則事前分布を与えて, 事後分布が正則であることを保証することにより解決します.

21.6. 一様な事後分布

区間[0, 1]で定義され, 一様事前分布Uniform(ψ ∣ 0, 1)が与えられたパラメータψを含むモデルがあるとします. ここで, データにはψについての情報が何もないとすれば, 事後分布もUniform(ψ ∣ 0, 1)となります.

ψには最尤推定値がありませんが, 事後分布は閉じた区間で一様ですから, 正則です. 一様な事後分布[0, 1]の場合, ψの事後平均は1/2と明確に決まります. 事後最頻値はありませんが, それにもかかわらず事後予測の推定は問題なくできるでしょう. ψの予測値を[0, 1]のすべての点で単純に積分(すなわち平均)すればよいのです.

21.7. 問題のある事前分布によるサンプリングの難しさ

非正則な事後分布では, 事後分布を適切に探索することは理論的には不可能です. BUGSやJAGSで実行されるギブズサンプリングでも, そのような非正則な事後分布から適切にサンプリングすることは不可能です. しかし, それにも関わらず, 21.1節で議論し, 図21.1で図示した2切片モデルのような例に直面したとき, ギブズサンプリングは, Stanで実行されるハミルトニアンモンテカルロのサンプリングとは実際はまったく異なる挙動を示します.

ギブズサンプリング

BUGSやJAGSで実行されるギブズサンプリングは, この識別されないモデルについて効率的で, うまく動作するように見えるかもしれませんが, 前の小節で議論したように, 実際には事後分布を適切には探索しないでしょう.

初期値λ1(0), λ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}$$

ここで, λ1λ2の抽出も対称です)の抽出を考えます. このモデルでは共役であり, したがって非常に効率的に抽出ができます. このモデルでは, 次のλ1が抽出可能な範囲は, 現在のλ2σの値により非常に制限されています. ギブズサンプラーは非常に速く動き, 外見上はもっともらしいλ1 + λ2の推定値を与えるでしょう. しかし, 事後分布の範囲全体を探索はしません. 初期値付近をゆっくりランダムウォークしているに過ぎないのです. このランダムウォークの挙動は, 事後分布の相関が非常に大きいときのギブズサンプリングに典型的です. これが, 事後分布でパラメータが相関を持つモデルでは, ギブズサンプリングよりもハミルトニアンモンテカルロが好まれる主要な理由です.

ハミルトニアンモンテカルロのサンプリング

Stanが実行するハミルトニアンモンテカルロ(HMC)は, 事後分布でパラメータに相関があるモデルでの事後分布の探索をはるかに効率的に行ないます. とくにこの例では, ハミルトニアンダイナミクス(すなわち, 負の対数事後分布により定義される場において, ランダムな運動量で与えられる, 架空粒子の運動)はポテンシャルエネルギーにより定義される谷(対数事後確率における尾根が, ポテンシャルエネルギーにおける谷に相当します)に沿って登ったり降りたりします. 実際には, λ1λ2のランダムな運動量についてさえも, 対数事後確率の勾配により, 相関について調整が行なわれ, シミュレーションではλ1λ2が, 事後対数密度の尾根に相当する谷に沿って反対方向に動かされるでしょう(図21.1参照).

No-U-Turnサンプリング

Stanのデフォルトのno-U-turnサンプラー(NUTS)はさらにもっと効率的に事後分布を探索します(Hoffman and Gelman (2011, 2014)を参照). NUTSは, パラメータの値を表す架空粒子の運動を, それがUターンするまでシミュレートします. 問題のある事後分布からサンプリングする場合, Uターンしないでポテンシャルエネルギーの谷をいつまでも降下することになり, ほとんどの場合うまくいきません. 実際に起きることは, シミュレーションの多くのiterationでリープフロッグの最大ステップ数に到達し, 対数確率と勾配の評価回数(デフォルトのように最大tree depthが10ならば, 1000となります)が非常に大きな数となることです. したがって, サンプリングは非常に遅いように見えるでしょう. これは非正則事後分布を示すもので, NUTSアルゴリズムや実装のバグではありません. 単純に, 非正則な事後分布からサンプリングすることは不可能なのです. したがって, 非正則な事後分布の場合に明確に失敗し, 事後分布の非常に長いパスを掃き出すという分かりやすい結果を得ることは, 一般的なHMCと特定のNUTSの挙動としては当然で安心できるものです.

例: Stanでのあてはめ

識別不可能なモデルや弱くしか識別されないモデルからのサンプリングの問題を描写するため, パラメータの識別可能性の程度を増やしつつ, 3つのモデルに当てはめを行ないます. これらのモデルの事後分布は図21.1に図示したものです. 最初のモデルは, 21.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ブロックに, lambda1lambda2の事前分布を加えたものです.

  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);
}
2個のスケールパラメータ, 非正則事前分布
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
2個のスケールパラメータ, 弱情報事前分布
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
1個のスケールパラメータ, 非正則事前分布
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

図21.2: yn ∼ Normal(0, 1)から生成した100データ点を, デフォルトのパラメータを使ってStanであてはめを行なった結果. 上段は, 非正則一様事前分布と尤度yn ∼ Normal(λ1 + λ2, σ)を使った識別できないモデル. 中段は, 上段30と同じ尤度で, 事前分布をλk ∼ Normal(0, 10)としたもの. 下段は, 尤度をyn ∼ Normal(μ, σ)とした, 識別できるモデル. すべてのモデルで, μはおよそ0.16と推定され, モンテカルロ標準誤差は非常に小さいのですが, 事後標準偏差は0.1と大きくなっています. 真の値μ = 0は, 3つのモデルすべてで事後分布の90%区間に含まれています.

3つの例すべてについて, Stan 2.1.0でデフォルトのパラメータ(1000ウォームアップiteration, 1000サンプリングiteration, 最大tree depthが10のNUTSサンプラー)を用いて当てはめを行ないました. 結果は図21.2に示します. この出力から分かる主要な統計量は以下のとおりです.

最初の2点, 収束の欠如と, リープフロッグの最大ステップ数(最大tree depthも同じ)到達は, 非正則事後分布を示すものです. ギブズサンプラーで行なわれるような貧弱なサンプリングで問題を覆い隠すのではなく, ハミルトニアンモンテカルロは事後分布を探索しようとしますし, それが失敗するなら, モデルのどこかがおかしいことを明確に示しています.

23.再現性(Reproducibility)について

現代のコンピュータにおける浮動小数点演算は, IEEE 754に準拠した基礎的な数値演算が完全には規定されていないため, 追試が難しいことで有名です. 根本的な問題点は演算の精度がハードウェアプラットフォームやソフトウェアの実装により異なっていることにあります. Stanは完全な再現性を許容すべく設計されています. しかしながら, それはあくまで浮動小数点計算により課せられた外的制約により左右されます.

Stanの結果は以下のすべての要素が一致しているときにのみ厳密に再現可能となります:

これはStanの安定リリースを使っているか, 特定の Git ハッシュタグを持つバージョンを使っているかには関係ありません. インターフェイスやコンパイラなどについても同様です. 重要なのはもしこれらのどれか一つでも何らかの違いがあれば, 浮動小数点計算の結果は変わる可能性があるということです.

具体的には, もしあるStanプログラムをCmdStanでコンパイルするときに, 最適化フラグを(-O3 から -O2 または -O0へ)変更した場合, 変更前と変更後の一連の結果は必ずしも一致しません. このため, クラスターや, IT部門に管理されている, もしくは自動更新がONになっているデスクトップのように外部に管理されたハードウェア上で再現性を保証するのは極めて困難です.

しかしながら, もしStanプログラムを一組のフラグを使ってコンパイルし, そのコンピュータをインターネットから取り外して一切アップデートしないようにし, 10年後に戻ってきて同じように再コンパイルした場合, 同じ結果が得られます.

データについてもビットレベルで同じである必要があります. 例えば, もしRStanであればRcppがRの浮動小数点数とC++の倍精度小数の間の変換を行います. もしRcppが変換のプロセスを変更したり異なる型を使うと, 結果はビットレベルで同じであることは保証されません.

コンパイラとコンパイラの設定も同じ問題を起こす可能性があります. インテル製のプロプライエタリなコンパイラで再現性をいかにコントロールするかについての素敵な議論はCoden and Kreirzer(2014)を読んでください.

27. ステートメント (文)

Stan プログラムのブロック ( 28章を参照 ) は, 変数の宣言とステートメント (文) から成り立っています. BUGS と違って, Stan プログラム中の宣言とステートメントはそれらが 記載された順序で実行されます. 変数は, それが参照される前に何らかの値 (と何らかのデータ型の宣言) によって定義されていなければいけません --- そうでない場合, 実行結果は未定義となります.

BUGS と同じく, Stan の基本的なステートメントには代入とサンプリングの 2 種類があります. また, いくつかのステートメントは 連続した処理やfor-each ループの繰り返し処理にグルーピング できるでしょう. 加えて, Stan では ブロック内でのローカル変数の宣言や, セミコロンのみからなる 空のステートメントも許容されています.

27.1. 代入ステートメント

代入ステートメントは変数と式からなります. 変数はインデックス情報を持つ多変量の場合もあります. 代入ステートメントが実行されると, ステートメントの右辺にある式が評価され, その結果が左辺の変数 (インデックスがある場合は指定された箇所) に代入されます. 簡単な代入の例は以下のようなものです. 1

n = 0;

このステートメントが実行されると, 式 0, つまり 整数のゼロが評価され, 変数 n に代入 されます. 代入が成立するためには, 右辺の式のデータ型と左辺の変数のデータ型は一致していな ければなりません. 上の例では, 0 という式は int 型になるため, 変数 nint 型もしくは real 型で宣言されていなければなりません. 変数が real 型で宣言されている場合は, 整数のゼロは浮動小数点のゼロに変換されて 変数に代入されます. ステートメントが実行された後は, 変数 n はゼロ (変数のデータ型に応じて整数 もしくは浮動小数点) という値を持ちます.

1Stan の 2.10.0 より前のバージョンでは, 代入には等号 =ではなく <- 演算子が用いられていました. <- 演算子は現在では非推奨となり警告が表示されます. <- 演算子は将来のバージョンで削除されます.

文法的には, すべての代入ステートメントはセミコロンで終わっていなければなりません. それ以外では, トークンの間の空白は処理に影響しません (ここでのトークンは左辺の変数, 代入演算子, 右辺の式, セミコロンを指します).

右辺の式が最初に評価されるため, Stan でも C++ や他のプログラミング言語と同じように 変数を変数をインクリメントすることができます.

n = n + 1;

このような自己代入は BUGS では許されていません. なぜなら, 自己代入は 有向グラフィカルモデル中で循環を引き起こすからです.

代入ステートメントの左辺はarray, matrix, vectorといったデータ構造に対するインデックスを 含むことがあります. 例えば, matrixとして定義された Sigma に対して,

Sigma[1,1] = 1.0;

という代入ステートメントは, Sigma の 1 行 1 列目の値に 1 を代入します.

代入ステートメントにはあらゆるデータ型の複雑なオブジェクトを含むことができます. SigmaOmegamatrix, sigmavectorの場合, 次の代入ステートメントは 成立します. 変数のデータ型と式の結果はどちらも matrix 型になるためです.

Sigma
  = diag_matrix(sigma)
     * Omega
     * diag_matrix(sigma);

また, この例は複雑な代入ステートメントを複数の行に分割して記載する場合に望ましい例を 示しています.

Stan は より大きな多変量のデータ構造の一部に対する代入もサポートしています. 例えば, areal[ , ] 型のarray, breal[] 型のarrayの場合, 以下二つの 代入ステートメントはどちらも成立します.

a[3] = b;
b = a[4];

同じように, xrow_vector 型, Ymatrix 型の変数として 宣言されている場合, 次の一連の処理は成立します. 処理の結果, Y の最初の 2 行が 入れ替わります.

x = Y[1];
Y[1] = Y[2];
Y[2] = x;

左辺値

代入ステートメント中の左辺として適切な式を "左辺値" と呼びます. Stan では, 適切な左辺値はこの 2 種類しかありません.

インデックス指定された変数を左辺値として使うためには, その変数は少なくともインデックスと同じ数の 次元を持っていなければいけません. 実数や整数のarrayは宣言された数の次元を持ちます. matrixは 2 次元, vectorrow_vectorは 1 次元です. これは分散共分散行列 ( cov_matrix) や 相関行列 ( corr_matrix ), またそれらのコレスキー因子 ( cholesky_factor_cov, cholesky_factor_corr )や 昇順のベクトル ( orderd ), 昇順で正のベクトル ( positive_ordered ), 各要素が [0, 1]で合計が1となるベクトル (simplex ) といった 制約付きデータ型についてもあてはまります. 通常のarrayの次元と比べて, matrixarrayの次元は 2 多くなり, vectorrow_vectorarrayの次元は 1 多くなります. 左辺値のインデックスの数は左辺の変数の次元よりも 少なくてよいことに注意してください. この時, 右辺は多次元で左辺値の次元と一致している必要があります.

エイリアス

すべての代入は, 事前に右辺の式のをコピーしたかのようにして行われます. これにより, 「代入ステートメントの実行中に右辺の式の値が変更される」ことによって生じる, エイリアスの潜在的な問題を解決しています.

34. Array型の演算

34.1 Arrayから1つの値の作成

次の演算子はarrayをインプットとしてとり, 一つの値をアウトプットとして返すものです.  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を返す.

総和・総乗・Log Sum of Exp

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をとったものの総和の自然対数を返す. ただし, arrayが空の時は-∞を返す.

標本平均・標本分散・標本標準偏差

55. 点推定

この章では, ベイズ推定ではありませんが, 最尤推定と罰則付き最尤推定というよく使われる方法を定義し, 事後分布の平均値・中央値・最頻値を使って, それらをベイズ点推定に関連づけます. 最尤推定値は, 事後分布ではなく, モデルのパラメーターθについての単一の値から求められるので, 「点推定値」と呼ばれます.

どのような尤度関数も罰則関数もStanのモデリング言語でコーディングすることができるので, Stanのオプティマイザ(optimizer)を使って(罰則付き)最尤推定を実装することができます. Stanのオプティマイザはまた, 事後最頻値に基づくベイズの枠組みでの点推定にも使うことができます. Stanのマルコフ連鎖モンテカルロサンプラーは, 事後分布の平均値や中央値に基づくベイズモデルでの点推定を実装するのにも使えます.

55.1. 最尤推定

尤度関数p(y ∣ θ)と, 固定されたデータのベクトル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 ∣ θ) ≥ 0, かつ, すべてのa, b > 0について, a > bのときのみloga > logbです.

最尤推定値の存在

すべての関数がただひとつの最大値を持っているとは限りませんので, 最尤推定値が存在することは保証されるものではありません. 20章で議論したように, この状況は以下のようなときに起こります.

こうした問題は, 次の節で議論する罰則付き最尤推定でも, その後の節で議論するベイズ事後最頻値でもついて回ります.

例: 線形回帰

通常の線形回帰の問題を考えます. 観測値yN次元のベクトル, 予測変数が(N × K)次元のデータ行列x, 回帰係数がK次元のパラメータのベクトルβ, 実数値のノイズのスケールがσ > 0のとき, 尤度関数は次式のようになります.


$$\log p(y\mid\beta,x)=\sum_{n=1}^{N}\log\mathsf{Normal}(y_n \mid x_n \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での二乗誤差の最小化

線形回帰において二乗誤差を最小にするアプローチは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の定義にある分母のNN-1に変えると, より一般的に用いられるσ2の不偏推定量が計算できます. 推定の偏りの定義と, 分散の推定についての議論については53.6節を参照してください.

55.2. 罰則付き最尤推定

最適化を行なう能力に関する限り, 尤度関数については特別なことはありません. 非ベイズ統計では普通に行なわれますが, 対数尤度に「罰則」関数と呼ばれる関数を加えて, その新しく定義した関数を最適化する方法があります. 対数尤度関数logp(y ∣ θ)と罰則関数r(θ)からなる罰則付き最尤推定量は次式のように定義されます.


$$\hat{\theta}=\mathrm{argmax}_{\theta}\log p(y\mid\theta)-r(\theta)$$

最大化のとき, 推定値$\hat{\theta}$は, 対数尤度の最大化と罰則の最小化との間でつりあいをとるようになります. 罰則をつけることは「正則化」とも呼ばれます.

リッジ回帰

リッジ回帰(Hoerl and Kennard, 1970)の基礎は, 係数ベクトルβのユークリッド長さ(訳注: 二乗のこと)に罰則をつけるところにあります. 次式がリッジ罰則関数です.


$$r(\beta)=\lambda\sum_{k=1}^{K}\beta_{k}^2=\lambda\beta^{\top}\beta$$

ここでλは, 罰則の大きさを決める固定したチューニングパラメータです.

したがって, リッジ回帰の罰則付き最尤推定値は次式のとおりです.


$$(\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と同様, 係数βについてのリッジ回帰の推定値は最小二乗法として定式化することもできます.


$$\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$$

リッジ罰則関数を加えると, βについてのリッジ回帰の推定値がより短いベクトルとなる効果があります. すなわち$\hat{\beta}$が縮小されます. リッジ推定値は必ずしもすべてのkについてβkの絶対値が小さくなるとは限りませんし, 係数ベクトルが, 最尤推定値と同じ方向を指すとも限りません.

Stanでリッジ罰則を加えるには, 罰則の大きさをデータ変数として加え, 罰則自身をmodelブロックに加えます.

data {
  // ...
  real<lower=0> lambda;
}
// ...
model {
  // ...
  increment_log_prob(- lambda * dot_self(beta));
}

ノイズ項の計算はそのままです.

Lasso

Lasso (Tibshirani, 1966)はリッジ回帰の代替法で, 係数の二乗和ではなく, 係数の絶対値和に基づいて罰則を適用します.


$$r(\beta)=\lambda\sum_{k=1}^{K}|\beta_{k}|$$

Lassoは, L1ノルムとの関連から, L1縮小とも呼ばれます. L1ノルムは, タクシー距離あるいはマンハッタン距離としても知られています.

罰則の導関数はβ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

ナイーブ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(β)は罰則関数ですので, プログラム中では符号は負であることに注意してください.

Elastic Net (Zou and Hastie, 2005)は, ナイーブElastic Netで生成された当てはめ値$\hat{\beta}$から, 最終的なβの推定値を調整するようにしています. Elastic Netの推定値は次式です.


$$\hat{\beta}=(1+\lambda_{2})\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). 後者の手法は, ベイズ統計で普通に採用される階層モデルに似ています.

55.3. 事後最頻値の推定

観測値yが与えられたときのパラメータθの事後分布p(θ ∣ y)に基づいてベイズ点推定を行なうのに普通に使うやり方は3つあります. すなわち, 最頻値(最大値), 平均値, 中央値です. この節では, 事後分布を最大化するようなパラメータθをもととする推定値について述べ, 続いて次の節では平均値と中央値について議論します.

モデルの事後最頻値に基づく推定値は次式のように定義できます.


$$\hat{\theta}=\mathrm{argmax}_{\theta}\,p(\theta\mid y)$$

存在するならば, $\hat{\theta}$は, 与えられたデータのもとでのパラメータの事後密度を最大化します. 事後最頻値は, 最大事後(maximum a posteriori, MAP)推定値とも呼ばれます.

20章と53.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}$$

密度は正値をとり, 対数が厳密に単調であることから次式が保証されます.


argmaxθ p(y ∣ θ)p(θ) = argmaxθ logp(y ∣ θ) + logp(θ)

事前分布(正則でも非正則でも)が一様である場合, 事後最頻値は最尤推定値と同じになります.

普通に使われる罰則関数ほとんどについて, 確率的に同じものが存在します. 例えば, リッジ罰則関数は係数への正規事前分布に対応しますし, Lassoはラプラス事前分布に対応します. この逆も常に真です. 対数事前分布に負号をつけたものは常に罰則関数と見なすことができます.

55.4. 事後平均値の推定

標準的なベイズ法では点推定には(あると仮定して)事後平均値が使われます. 定義は次式です.


$$\hat{\theta} = \int \theta p(\theta\mid y)d\theta$$

事後平均値はまさにベイズ推定量とよく呼ばれます. 推定値の期待二乗誤差を最小にする推定量だからです.

各パラメータの事後平均値の推定値は, Stanのインターフェイスから返されます. インターフェイスとデータフォーマットの詳細はRstan, CmdStan, PyStanのユーザーズガイドを参照してください.

事後最頻値が存在しない場合でも, 事後平均値が存在することは少なくありません. 例えば, Beta(0.1, 0.1)の場合, 事後最頻値はありませんが, 事後平均値はきちんと定義されて, 値は0.5となります.

事後平均値が存在しないのに, 事後最頻値は存在するという状況のひとつは, 事後分布がコーシー分布Cauchy(μ, τ)の場合です. 事後最頻値はμですが, 事後平均値を表す積分は発散します. そのような幅の広い事後分布(訳注: 原文はpriorだがposteriorの誤り)は, 実際にモデリングを使うときにはめったに出てきません. パラメータにコーシー分布の事前分布を使うときでも, データより十分な制約が与えられるので, 事後分布は行儀が良くなり, 平均値も存在するようになります.

事後平均値が存在しても, 意味がないものであることもあります. 混合分布モデルで起きる多峰の事後分布の場合や, 閉区間での一様分布の場合がそれに当たります.

55.5. 事後中央値の推定

事後中央値(すなわち50番目の百分位点または0.5分位)は, ベイズモデルの報告によく使われる, もうひとつの点推定値です. 事後中央値は, 推定値の誤差の期待絶対値を最小化します. こうした推定値は, さまざまなStanのインターフェイスで返されます. フォーマットについてのさらに情報を得るにはRStan, PyStan, CmdStanのユーザーズガイドを参照してください.

事後中央値が意味のないものになることもありえますが, 事後平均値が存在しないようなときでも多くの場合, 事後中央値は存在します. コーシー分布もこれにあてはまります.

55.6 推定値の誤差, バイアス(偏り), 分散

推定値$\hat{\theta}$は, 特定のデータyのほか, 対数尤度関数logp(y ∣ θ), 罰則付き尤度関数logp(y ∣ θ) − r(θ), 対数確率関数logp(y, θ) = logp(y ∣ θ) + logp(θ)訳注: 原文はlogp(y, θ) = logp(y, θ) + logp(θ)だがこれは誤り)のうちのいずれかに依存します. この節では, $\hat{\theta}$という記法は推定量を示すものとしても定義します. このときの推定量とは, データと(罰則付き)尤度あるいは確率関数の非明示的な関数です.

推定値の誤差

真のパラメータθにしたがって生成された特定の観測値のデータセットyについて, パラメータの推定値と真の値との差が推定誤差です.


$$\mathrm{err}(\hat{\theta}) = \hat{\theta} - \theta$$

推定値のバイアス(偏り)

特定の真のパラメータの値をθ, 尤度関数をp(y ∣ θ)とすると, 推定量のバイアスとは推定誤差の期待値です.


$$\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 ∣ θ)で重みづけてデータセットyの取りうる範囲全体について積分したものです.


$$\mathbb{E}_{p(y\mid\theta)}[\hat{\theta}] = \int \left(\mathrm{argmax}_{\theta'}p(y\mid\theta')\right)p(y\mid\theta)dy$$

バイアスはθと同じ次元の多変量です. この期待推定誤差が0であれば推定量は不偏ですが, そうでなければバイアスがあります.

例: 正規分布の推定値

正規分布から抽出された, n ∈ 1 : Nについての観測値ynからなるデータセットがあるとします. これは, yn ∼ Normal(μ, σ)というモデルを仮定しています. ここで, μσ > 0はともにパラメータです. 尤度は次式のとおりです.


$$\log p(y\mid\mu,\sigma) = \sum_{n=1}^{N}\log\mathsf{Normal}(y_{n}\mid\mu,\sigma)$$

μの最尤推定量はちょうど標本平均, すなわち標本の平均値となります.


$$\hat{\mu} = \frac{1}{N}\sum_{n=1}^{N}y_{n}$$

この平均についての最尤推定量は不偏です.

分散σ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 × 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}]$$

標本データから正規分布の平均と分散を推定する例での計算では, 最尤推定量(すなわち標本平均)は, 分散を最小とするような平均μの不偏推定量です. そのことは, 正規ノイズの仮定のもとで最小二乗推定について, また等価ですが, 最尤推定について, ガウス-マルコフの定理によって一定の一般性をもって証明されました. Hastieら(2009)の3.2.2節を参照してください.

56. ベイズデータ解析

Gelmanら(2013)はベイズデータ解析の特徴を以下のように述べています.

ベイズデータ解析と言うときは, 観測する量や, 知りたい量について, 確率モデルを使ってデータから推定を行なう実用的な方法という意味で言っています.

続けて, ベイズ統計が頻度主義の方法とどのように異なるかを記述しています.

ベイズ法の本質的な特徴は, 統計解析に基づく推定の際, 不確実性を定量化するのに確率を明示的に使うところにあります.

厳密な頻度主義者は, 観測値の相対頻度の極値を確率と見なしますから, パラメータについて確率的に言明することを禁止します. パラメータは固定したもので, 確率変数ではないとされるのです.

ベイズ主義者も, パラメータを固定した, 未知のものと扱います. しかし頻度主義者とは異なり, パラメータの事前分布と, パラメータの事後分布の両方を使います. これら事前分布・事後分布と事後予測確率は, パラメータと将来の観測値についての知識を特徴づけるためにあります. 以下で述べるように, 事後分布はベイズ推定の基礎を形づくるものです.

56.1 ベイズモデリング

Gelmanら(2013)は, 応用ベイズモデリングを以下の3ステップに分解しています.

  1. すべての観測可能な量と観測不可能な量とについてのフル確率モデルをつくります. このモデルは, モデリングするデータとそれがどのように集められたかということについて, 既存の知識と合致するべきです.

  2. 観測された量を条件として未知の量の事後確率を計算します. 未知の量には, パラメータのような観測できない量や, 将来の観測の予測値のような潜在的には観測可能な量といったものが含まれるでしょう.

  3. データに対するモデルの当てはまりを評価します. これには, 事後分布の効果を評価することも含まれます.

典型的には, 3番目のステップでじゅうぶんな当てはまりが得られるまでこのサイクルは繰り返されます. Stanは, 2番目と3番目のステップに含まれる計算を自動化します.

56.2 ベイズ推定

基本的な量

ベイズ推定のメカニズムはそのままベイズの定理に従っています. 記法を定めて, yでデータのような観測された量を表し, θでパラメータや将来の観測値のような未知の量を表すとします. yθの両方が確率変数としてモデリングされるでしょう. 定数や, ハイパーパラメータ, 予測変数のような, 既知ですがモデリングされない量をxで表すとします.

確率関数

確率関数p(y, θ)は, データyとパラメータθの同時確率関数です. 定数および予測変数xは, 明示されていませんが条件に含まれていることになっています. パラメータθが与えられたときのデータyの条件付き確率関数p(y ∣ θ)はサンプリング確率関数と呼ばれます. また, yxを固定してθの関数と見たときには尤度関数とも呼ばれます.

定数xが与えられたときのパラメータについての確率関数p(θ)は事前分布と呼ばれます. というのは, データがまだまったく観測されていないときのパラメータの確率を特徴づけるものだからです. 条件付き確率関数p(θ ∣ 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(θ ∣ y)を「ひっくり返し」て, 尤度p(y ∣ θ)と事前分布p(θ)だけからなる項で表します(ここでも, 定数および予測変数xは明示していません). 最後のステップがStanにとって重要で, 必要なのは定係数を除いた確率関数の部分のみです.

予測推定

(与えられたモデルで)データyから推定されるパラメータθの推定値の不確実性は, 事後分布p(θ ∣ 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}$はともに明示されていません. 事後確率自身と同様, 予測推定は確率的に特徴づけられます. パラメータθの点推定値を使うのではなく, データy(と定数x)が与えられたときのθの事後確率p(θ ∣ y)によって重みづけつつ, θの範囲全体について予測値を平均することにより, 予測が行なわれます.

事後分布は, イベントの確率を推定することにも使われます. 例えば, パラメータθkが0より大きいという確率は, 次式により確率的に特徴づけられます.


Pr[θk > 0] = ∫ΘI(θk > 0)p(θ ∣ y)dθ

指示関数I(ϕ)は, 命題ϕが真ならば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ブロックで乱数発生器を使って複製データを生成します.

57. マルコフ連鎖モンテカルロサンプリング

BUGSと同じく, Stanも推定のため, 事後分布からのサンプルを生成するのに, マルコフ連鎖モンテカルロ(MCMC)という技術を使います.

57.1. モンテカルロサンプリング

モンテカルロ法は, 解析的には解けずとも, 積分された関数の評価はできるような積分を数値的に近似するために開発されました(Metropolis and Ulam, 1949).

例を挙げると, 確率密度p(θ)の平均μは次の積分で定義されます.


μ = ∫Θθ × p(θ)dθ

非常に複雑というほどではないベイズモデルでも, 事後密度p(θ ∣ y)は, 解析的には評価できない積分になってしまいます. 事後分布は定数と予測変数xにも依存しますが, これ以降は省略して既知のものとします.

ここで, p(θ)から独立なサンプルを抽出できるとして, θ(1), θ(2), …, θ(N)N個のそのようなサンプルとします. p(θ)の平均μのモンテカルロ推定値$\hat{\mu}$は標本平均として与えられます.


$$ \hat{\mu} = \frac{1}{N}\sum_{n=1}^{N} \theta^{(n)} $$

確率関数p(θ)が有限の平均と分散を持つなら, 大数の法則により, サンプルサイズが増加するとモンテカルロ平均は正しい値に収束することが保証されます.


$$ \lim_{N \to \infty} \hat{\mu} = \mu $$

有限の平均と分散を仮定すると, 推定誤差は中心極限定理に従うので, Nの平方根に反比例して減少します.


$$ |\mu - \hat{\mu}| \propto \frac{1}{\sqrt{N}} $$

したがって, 平均を1桁正確に推定するためには, 100倍のサンプルが必要になります. 2桁精度を上げるには1万倍のサンプルということになります. モンテカルロ法が, 非常に正確な推定値というよりも数桁以内の大雑把な推定に向いているのはこの理由によります. 実用上も, ある量を点推定するのに, 元になるデータのサンプルが持つ不確実性よりも精度が上がることはありません. そのため, 何桁もの精度が得られないことが, 統計モデルの実用の際に問題になることは滅多にありません.

57.2. マルコフ連鎖モンテカルロサンプリング

マルコフ連鎖モンテカルロ(MCMC)法は, 独立したサンプルを単純には抽出できないような状況のために開発されました(Metropolisら, 1953).

マルコフ連鎖は, 確率変数θ(1), θ(2), …の系列であり, ある変数は, 直前の値が与えられたとき, それ以外の変数とは条件付き独立になります. したがって, θ = θ(1), θ(2), …, θ(N)ならば次式のようになります.


$$ p(\theta) = p(\theta^{(1)}) \prod_{n=2}^{N} p(\theta^{(n)} \mid \theta^{(n-1)}) $$

Stanでは, その次の状態を生成するのに, 60章で述べる方法でハミルトニアンモンテカルロ法を使っています.

Stanおよび他のMCMCサンプラーが生成するマルコフ連鎖は, マルコフ連鎖の中心極限定理に必要という意味でエルゴード的です. 大雑把にいうと, θのある値に他の値から適当な可能性で到達するという意味です. このマルコフ連鎖はまた定常的です. これは, 連鎖中の別の位置でも推移確率は異ならないという意味です. したがって, n, n′ ≥ 0について, 確率関数p(θ(n + 1) ∣ θ(n))p(θ(n′ + 1) ∣ θ(n′))と同じです(慣例に従って確率変数や束縛変数は使い回しをしており, 確率関数は引数で区別しています).

定常マルコフ連鎖は, それぞれが同じ周辺確率関数を持つような状態について平衡分布を持ちます. したがって, p(θ(n))p(θ(n + 1))と同じ確率関数です. Stanでは, 平衡分布p(θ(n))は, サンプリングされる確率関数p(θ)であり, 一般にはベイズ事後密度です.

MCMC法を使うには, 独立サンプルのモンテカルロ法にはない2つの難題があります. 1つめの問題は, ランダムに初期化されたマルコフ連鎖が, いつその平衡分布に収束したかを決定することです. 2つめの問題は, マルコフ連鎖からの抽出に相関があることです. そうなると, 中心極限定理による推定誤差への制約はもはや当てはまりません. これらの問題は次の2つの節で扱います.

57.3. 初期化と収束モニタリング

マルコフ連鎖が目的分布からのサンプルを生成するのは, 平衡に収束した後だけです. 残念ながら, これが保証されるのは理論的には極限においてのみです. 実用的には, マルコフ連鎖が収束したかどうかをモニタリングするための診断が必要になります.

Potential Scale Reduction

連鎖が平衡分布に収束したかどうかをモニタリングする方法の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本のマルコフ連鎖の組θmについて定義されます. このとき, 各連鎖はNサンプルθ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}} $$

不ぞろいな連鎖に対する一般化$\hat{R}$

ここで, 各連鎖が異なる数のサンプルからなっていても良いとします. Nmを連鎖m内のサンプルの数とします. このとき, 連鎖mについての連鎖内平均の式には連鎖のサイズNmを使います.


$$ \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}$の式に直接組み込めるでしょう.

分割した$\hat{R}$による非定常性の検出

potential scale reduction 統計量$\hat{R}$を計算する前に, 各連鎖を半分ずつ2つに分割することもできます. これにより追加の平均が得られ, 連鎖内での非定常性を検出できます. 1本の連鎖が次第に増加するような値を取っており, もう1本の連鎖が次第に減少する値を取っているとき, それらはうまく混合してはいないのに, $\hat{R}$の値は1に近い値を取ることがありえます. この場合, 各連鎖を2つに分割すると, 各連鎖の前半と後半とは混合していないので, $\hat{R}$の値は1よりもかなり大きくなるでしょう.

収束は大域的である

よくある疑問は, パラメータあるいは生成量の一部だけの収束をモニタリングしても良いかどうかというものです. 端的に言うと答えは「いいえ」ですが, これについてはこの節でさらに詳しく述べます.

例えば, lp__の値を考えましょう. これは, 対数事後密度(定数は除く)です. もしlp__が収束していないなら, いかなる実用的な意味においても収束したと結論づけるのは誤りです. これは, 全ての連鎖は実際にパラメータ空間の別々の部分に存在しているからです. とはいえ, lp__の収束を測定するのは, 以下に書くようにとりわけトリッキーです.

漸近的かつ過渡的な状態 vs. 平衡

マルコフ連鎖の収束は, モニタリングするパラメータの関数をどう選ぶかに依存しないという意味で大域的な特性です. 収束前の「過渡的な状態」と収束後の「平衡」との間には明確な境界はありません. ここで起きていることは, 連鎖内の状態の数が無限に近づくにつれ, 連鎖内の取り得る状態の分布が目的分布に近づき, その極限では, あらゆる積分可能なモンテカルロ推定量の期待値が真の期待値に収束するということです. ここではウォームアップのようなものはありません. というのは, 極限では初期状態の影響は完全に消えているからです.

多変量の場合の関数の収束

$\hat{R}$統計量は, あるマルコフ連鎖とある関数の合成を考えていることになりますので, マルコフ連鎖が収束していれば, マルコフ連鎖と関数の合成は全て収束するでしょう. 多変量の場合の関数の収束は, Cramer-Woldの定理により, 確率変数のあらゆる周辺和が収束することと等価です.

制約のない空間から制約のある空間への変換は単にもう一つ別の関数となるため, 収束には影響ありません.

異なる関数は異なる自己相関を持つ可能性があります. しかしマルコフ連鎖が平衡に達していれば, マルコフ連鎖と関数の合成はすべて一貫して収束するはずです. 正式には, 収束していないように見える関数があれば, どんなものであれ不安材料となります. あらゆる関数を検証するのは合理的ではないでしょうが, 少なくともlp__とその他の測定された量は一貫して収束しているべきです.

lp__が目立って異なる点は, 位置が急速に変化する傾向があり, そのため外れ値の影響を受けやすいことです.

有限な状態の数

状態の数が有限だとどうなるでしょう? 強い幾何学的エルゴード性を証明できるなら(これは, サンプラーと目的分布に依存します), 連鎖が初期状態を忘れるような有限な時間が, 高い確率で存在することを示すことができます. これは自己相関時間とウォームアップ時間の両方についてです. しかし, それが存在して有限であること示せるとしても(ほとんど不可能ですが), 実際の値を解析的に計算することはできません.

したがって実際には, 期待値がかなり正確と言えるほどに, 有限の抽出数が十分に大きいことを期待することしかできません. ウォームアップのiterationを取り除くことは期待値の正確さを改善しますが, どれだけの有限な数のサンプルを取り除けば十分であるかは保証されません.

なぜ$\hat{R}$が一貫しない?

ここで心配すべきことは2つあります.

1つめとして, 上に記したように, 有限の抽出数ではどれほどであっても, 何がしかの初期状態の効果が常に残るでしょう. これは, いくらかの小さな確率(あるいは, 自己相関時間がとても大きければ大きな確率)で大きな外れ値を持つという形で現れます. そのような外れ値に頑健な関数(例えば分位数)なら, より安定して見えますし, より良い$\hat{R}$を持つでしょう. そのような外れ値に脆弱な関数なら, 脆さが見える結果となるかもしれません.

2つめとして, $\hat{R}$統計量の使用には非常に強い仮定を置いています. とりわけ, 関数が正規分布とみなせると仮定していますし, 最初の2つのモーメント(訳注: 平均と分散)のみを使い, ある種の独立性を仮定しています. ポイントは, この強い仮定が常に成り立つわけではないということです. とくに, 対数事後密度(lp__)の分布はほとんど正規分布に見えることがありません. Nが非常に大きくなっても, 裾が長くて$\hat{R}$が大きくなる可能性があります. 生の値の代わりに分位数を使うといった小技により, 興味のあるサンプルをより正規分布に近づけ, その結果$\hat{R}$をより正確にする傾向を高めることができます.

収束モニタリングについて最後に

「収束」は大域的な特性であり, すべての積分可能な関数について同時に当てはまりますが, $\hat{R}$統計量を採用することには追加の仮定が必要であり, 関数すべてに同じようにうまくいくとは限りません.

連鎖間で期待値を比較するだけでも, マルコフ連鎖の正規分布への漸近性に関する信頼性を確認することができ, 標準的な検定も適用できることに注意してください.

57.4. 有効サンプルサイズ

MCMC法がもたらす技術的な難題の2つめは, 一般に連鎖内でサンプルが自己相関を持つことです. これにより, 平均や分散, 分位数といった, 事後分布において興味のある量を推定する際に不確実性が大きくなります.

MCMCの結果の解析一般について, またとくに有効サンプルサイズについての良い入門的な参考文献がGeyer (2011)です. Stanがまさに使っている計算は分割$\hat{R}$の計算に従っていて, これには連鎖をプールした計算(平均)と連鎖内の計算(自己相関)の両方が含まれています. これらはこのマニュアルで紹介されますが, Gelmanほか(2013)でより詳細に説明されています.

有効サンプルサイズの定義

連鎖内の自己相関が推定値の不確実性をどのくらい増やしているかは有効サンプルサイズ(ESS)で測ることができます. 独立なサンプルならば, サンプルの数Nに基づいて中心極限定理が推定値の不確実性を制限します. 独立でないサンプルの場合は, 独立なサンプルの数は有効サンプルサイズNeffに置き換えられます. これは, N個の自己相関のあるサンプルと同じ推定能力を持つ独立なサンプルの数です. 例えば, 推定誤差は, $1/\sqrt{N}$ではなく$1/\sqrt{N_\mathrm{eff}}$に比例します.

ある系列の有効サンプルサイズは, その系列におけるラグの異なる自己相関で定義されます. 平均μ, 分散σ2の同時確率分布p(θ)を持つ連鎖の, ラグt ≥ 0での自己相関は次式のように定義されます.


$$ \rho_{t} = \frac{1}{\sigma^2}\int_{\Theta}(\theta^{(n)}-\mu)(\theta^{(n+t)}-\mu)p(\theta)d\theta $$

これはまさに, 2本の連鎖の間の相関に, 位置tだけオフセットをつけたものです. MCMCの設定上, θ(n)θ(n + t)とは同じ周辺分布を持つことがわかっていますから, 2つの差の項を掛け算して整理すると次式になります.


$$ \rho_{t} = \frac{1}{\sigma^2}\int_{\Theta}\theta^{(n)}\theta^{(n+t)}p(\theta)d\theta $$

自己相関ρtを持つ過程から生成されたNサンプルの有効サンプルサイズは次式で定義されます.


$$ N_\mathrm{eff} = \frac{N}{\sum_{t=-\infty}^{\infty}\rho_{t}} = \frac{N}{1+2\sum_{t=1}^{\infty}\rho_{t}} $$

有効サンプルサイズの推定

実用的には, 問題とする確率関数は簡単には積分できませんし, したがって自己相関も有効サンプルサイズも計算できません. そのかわり, こうした量はサンプル自身から推定される必要があります. この節の残りでは, バリオグラムに基づいた自己相関の推定量, それから複数の連鎖に基づいた有効サンプルサイズについて記述します. 簡単のため, 各連鎖θmの長さはどれもNであると仮定しましょう.

有効サンプルサイズを推定する方法の1つは, ラグt ∈ {0, 1, …}におけるバリオグラムVtに基づくものです. バリオグラムは, (1変量の)サンプルθm(n)について以下のように定義されます. ここで, m ∈ {1, …, M}は連鎖であり, Nmは連鎖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を, ρT′ + 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)を参照してください.

サンプルの間引き

一般的な状況では, 自己相関ρtは, ラグtが増加するにつれて減少します. このようなとき, サンプルを間引くことにより, 自己相関が減るでしょう. 例えば, 以下の2つの方法のうちの1つで1000サンプルを生成するとします.

  1. 収束後に1000サンプルを生成し, そのすべてを保存する.
  2. 収束後に10000サンプルを生成し, 10個ごとにサンプルを保存する.

両方とも1000サンプルを生成するとはいえ, 間引きのある2番目の方法がより有効なサンプルを生成するでしょう. これは, 間引かれた系列の自己相関ρtは, 間引かれていないサンプルのρ10tに相当するからです. そのため, 自己相関の和は小さくなり, そして有効サンプルサイズは大きくなるでしょう.

一方, メモリとデータの容量に問題がないなら, 10000サンプルすべてを保存する方が, 1000サンプルに間引くよりも有効サンプルサイズは大きくなるでしょう.

60. ハミルトニアンモンテカルロサンプリング

ここではStanで実装されているアルゴリズムの実装の詳細とそれをどのように設定するかを説明します. この章ではハミルトニアンモンテカルロ(Hamiltonian Monte Carlo, HMC)アルゴリズムとその適応的な変種であるno-U-turn sampler(NUTS)をStanでの実装と構成に基づいて説明します. 次の2つの章ではStanのオプティマイザとdiagnostics(診断の方法)について説明します.

60.1 ハミルトニアンモンテカルロ

ハミルトニアンモンテカルロ(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(ρ, θ) = p(ρ|θ)p(θ0)

から値が抽出されます. Stanを含むHMCのほとんどの応用では補助分布としてはパラメータθに依存しない多変量正規分布


ρ ∼ MultiNormal(0, σ)

を用います. 共分散行列Σは目標とする分布の回転とスケール変換のためのユークリッド計量として振舞います(幾何学の詳細についてはBetancourt and Stein, 2011を参照). Stanではこの行列はidentity matrix(単位行列)またはwarmup期間のサンプルから推測され, 制限された対角行列になります. 逆行列
Σ − 
は質量行列(mass matrix)という名前で知られ, Σが単位, 対角, 密行列のいずれかであればそれと同じ性質を持ちます.

ハミルトニアン

結合密度p(ρ,θ)はハミルトニアン

ハミルトニアン
ハミルトニアン

で定義され, 項T

運動エネルギー
運動エネルギー

は"運動エネルギー",項V

ポテンシャルエネルギー
ポテンシャルエネルギー

は"ポテンシャルエネルギー"と呼ばれます. このポテンシャルエネルギーはStanでは対数密度の定義から求まります.

遷移の生成

パラメータθの現在の値から新しい状態への遷移はメトロポリス法の受理ステップとその前の2つの手順で生成されます.

最初に運動量の値が現在のパラメータの値とは独立に分布


ρ ∼ MultiNormal0(0, σ)

から抽出されます. 運動量はiterationのたびに新しい値になり以前の値に影響されません.

次に現在のパラメータの値θと新しい運動量ρを結合した系(θ, ρ)をハミルトン方程式

ハミルトン方程式.PNG
ハミルトン方程式.PNG

にしたがって発展させます. 運動量密度と目標の密度関数は独立, つまりp(ρ|θ)=p(ρ)なので運動量の時間微分のはじめの項∂T/∂θ=0になります. よって時間微分の式は


$$ \dot{\theta},\dot{\rho} $$

となります.

蛙飛び積分(Leapfrog Integrator)

このセクションの最後に2状態の微分方程式を解く作業が残されています. Stanは他のほとんどのHMCの実装と同様に蛙飛び積分(Leapfrog Integrator)を使っています. これはハミルトン方程式の結果を安定させるために調整された数値積分アルゴリズムです.

ほとんどの数値積分と同じ様に蛙飛びアルゴリズムは小さな時間間隔εの離散的なステップごとに行われます.

蛙飛びアルゴリズムは新しい運動量の項をパラメータθとは独立に抽出するか前の運動量の値


ρ

から始め, ステップの半分だけの運動量の更新と1ステップ分の位置の更新を交互に行います.


Leapfrogofρ, θ

L回の蛙飛びステップでLεだけの時間の動きがシミュレートされます. この結果(上記の3つのステップのL回繰り返し)で得られる状態を(ρ)と書きます.

蛙飛び積分の誤差は1ステップあたりの時間間隔(step size)であるεの3乗, 大域的にはεの2乗のオーダーになります. Leimkuhler and Reich(2004)には蛙飛び積分の誤差の範囲の導出を含めたハミルトン系の数値積分の詳細な解析が紹介されています.

メトロポリス受理ステップ

蛙飛び積分が完全に数値的であれば運動量ベクトルをランダムに生成するのとは別に遷移ごとにランダム化を施す必要はなくなります. しかし実際には積分による数値誤差をメトロポリス受理ステップを適用する際には考慮する必要があります. (ρ,θ)からの遷移で生成された提案値(ρ)が受理される確率は


min(1, exp(H(ρ, θ) − H(ρ * , θ * )))

であり, もし提案分布が受理されなければ前のパラメータの値が次の値としてサンプルされ, 次のiterationにも使われます.

アルゴリズムのまとめ

ハミルトニアンモンテカルロアルゴリズムは初期設定されたパラメータの組θからはじめられます. Stanではこの値はユーザーが設定することもランダムに生成することも出来ます. そして指定された数だけ新しい運動量のサンプリングとパラメータθの現在の値が 時間間隔εで離散化された蛙飛び積分がL回実行が繰り替えされることで更新されます. その後メトロポリス受理ステップが適用され, 提案された状態値(ρ)が使われるか元の状態値のままにするかが決定されます.

60.2 HMCアルゴリズムのパラメータ

ハミルトンモンテカルロ法は以下の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期間を分割する動機は適応をより安定したものにすることで, 各段階は以下のようになります.

これらの期間は以下のような調整パラメータにのっとって制御されます. 各パラメータは正の整数である必要があります.

パラメータ 説明 デフォルト値
初期バッファ 最初の速い期間の長さ 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)に打ち勝つことができます.

step sizeのゆらぎ

数値積分を使った全ての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累積を用いて推定されます.

warmup回数と質量行列の推定

質量行列は事後分布の線形(ここでは大域的と同義になる)な相関を補正し, いくつかの問題ではHMCのパフォーマンスを劇的に改善します. これには大域的な相関を知る必要があります.

複雑なモデルでは大域的な相関は大抵は難しく, 不可能でなければ解析的に導かれます. 例えばデータのスケーリングがねじれているような非線形なモデルではデータの正規化(standardizing)は必ずしも助けには成りません. それゆえStanは適応的なwarmupでオンラインに補正を行います. 強い非線形性(ここでは局所性と同義)の相関は正規化を行った場合でさえ学習が遅くなり得ます. これがStanでなぜwarmupが必要でしばしばそれが長い時間を必要とするかの究極的な理由であり, 十分長いwarmupが実質的なパフォーマンスの改善をもたらすことが出来るかの理由でもあります.

非線形性

質量行列は事後分布での線形(ここでは大域的または位置に依存しないことと等価)な相関のみを補正します. 階層的なパラメータ一方では階層的なモデルでよく現れるたちの悪い非線形(ここでは局所的または位置に依存したことと等価)な相関しています. ((リーマニアンHMCだけが計量を実現でき, それによって(パラメータ空間での)位置に依存した質量行列, 非線形な相関の補正が有効になり始めます. ))

密な質量行列の最も大きな問題は質量行列それ自体の推定が卵と鶏のような関係を引き起こすことです. 適切な質量行列をサンプリングから推定するには収束する必要があり, それには適切な質量行列が必要になります.

質量行列が密か対角か

サンプリングに問題があるような統計モデルは一般には密な行列で調節できるような線形相関だけに支配されている訳ではありません. むしろもっと複雑な非線形の相関に支配されていてリーマニアンHMC(Riemannian HMC)のようなより高度なアルゴリズムを用いたパラメータの採取するのがよいです.

warmup回数と曲率

MCMCの収束にかかる時間は大まかには自己相関時間と同じです. HMC(とNUTS)のチェーンは自己相関が低くなりがちなのでとても速く収束しがちだからです.

これが唯一の適用できる場合は事後分布の幅の中に一様に分布している場合で多くの複雑なモデルではこの仮定は破れています. 実に多くの場合事後分布の質量は良い性質を満たしていて分布の裾はの曲率は大きく, 別の言い方をすればwarmupがゆっくりな理由は分布の裾においてHMCのイテレーションのコストが高いからと言えます.

分布の裾における低性能は数回のwarmupのイテレーションではカバーできないような振る舞いです. 最初の数回のイテレーションの受理確率とstep sizeを見れば, 解こうとしている問題がいかに悪い問題であるかといったことや, 事前分布をより問題にあうようタイトに設定するとか, パラメータを再調整するなどのモデリングの努力が必要であるかがわかります.

NUTSとその調整

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つの値は常に


2treedepth − 1 < Nleapfrog < 2treedepth − 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はパラメータとして定義, 初期化することが出来ます.


  1. RやPythonなどの高レベルなスクリプト言語から使う場合, Stanのプログラムは動的にリンク可能なオブジェクトファイルに変換されます.

  2. これらの推定された質量行列は大域的です. これは, サンプリングされるパラメータ空間の全ての点に同じ質量行列が適用されるという意味です. Riemann-manifold HMCではこれが一般化され, 質量行列によって示唆される曲率が位置ごとに変化することが許されます.

  3. 十分に有効サンプルが得られないことは, warmupの期間の長さが不十分であることがしばしば原因です. この再実行戦略は, 最初に正しいiteration数を推測するよりも, 高々約50%多いiterationを消費するだけです.

  4. このモデルはhttps://github.com/stan-dev/example-models/tree/master/misc/clusterから入手可能です.

  5. クラスタリングではあらゆる混合モデルにおいて識別不可能性が問題となりますが, 分類ではそのような問題はありません. なお, クラスタリングのフルベイズ推定がむずかしいにもかかわらず, 研究者に利用され続けているのは, 予測のためのモデル化というよりも探索的データ分析としての位置づけによるものです.

  6. このモデルはhttps://github.com/stan-dev/example-models/tree/master/misc/clusterから入手可能です.

  7. 対照的に(罰則付き)最尤推定は, パラメータ化によって不変ではありません.

  8. Gelmanの便利な統計用語集(http://andrewgelman.com/2009/05/24/handy_statistic/)ではピノキオの原則に言及しています. この原則は「計算上の理由だけで作られたモデルにも魂が宿り, ひとり歩きする可能性がある」というものです. この原則はGelmanがフォーク定理と呼んでいる経験則にも関係があります. その定理は「計算がうまくいかないときは, しばしばあなたのモデルに問題がある」というものです.

  9. この例は説明のためのものです. Stanで対数正規分布を実装するオススメの方法はビルトインの確率分布関数lognormalを使うことです(45.1節を見てください).

  10. プログラムはhttps://github.com/stan-dev/example-models/tree/master/basic_distributionsから入手可能です.

  11. 問題は三角分布の裾が(とても!)軽いことです. 標準のHMCやNUTSサンプラーは三角分布の角にうまく入っていくことができません. 一方はじめのStanコードでは, yの型をreal<lower=-1,upper=1>と宣言しているので, 制約のない変数に逆ロジット変換が適用され, その変換のヤコビアンの絶対値の対数が対数確率に足されます. 結果的に得られるロジット変換されたyの分布は問題なく振る舞います. Stanで使われる変換についてもっと情報が知りたい場合は58章を参照してください.

  12. コメントの主な問題は誤解を招く恐れがあることです. それはプログラマー側の誤解によるかもしれませんし, コメントがかかれたあとにプログラムの振る舞いが変えられたためかもしれません. プログラムは常にコードに書かれたように振る舞います. そのため, 複雑なコードをリファクタリングして理解しやすい部品にすることは単にコメントを加えることよりも好ましいのです.

  13. さまざまなビルトインのバリデーション方法はもうすぐStanにも実装されます!現状では代わりに, 単体の制約をチェックするためにreject文を使うことができるでしょう.

  14. Stan 2.10.0現在, ユーザー定義関数で例外を発生させる唯一の方法は, 関数が呼ばれる際に(サンプリング文で呼ばれる場合も含む)reject文を使って例外を発生させることです.

  15. Stanの将来のバージョンでは前もって宣言しなくても動くようになるでしょう.

  16. この例は, Stanがrk45ソルバーを実装するために使ったBoost Numeric Odeint library (Ahnert and Mulansky, 2011)のドキュメントからとってきている.

  17. 偶然の一致ではなく, 一般のStanのモデルの事後分布に曲率が高い箇所があると, ユークリッド距離を使うハミルトニアンモンテカルロ(HMC)のサンプリングにおいて同様の問題が起こります. 理由はHMCは, 蛙跳び積分のアルゴリズムや傾きをもとにしたステップを使う微分方程式のソルバーを使っているからです. その微分方程式のソルバーはポテンシャルエネルギーと運動エネルギーの項を分けることができるハミルトン系に特化しています.

  18. 訳注: 原文はpriorsとなっていますが, 文脈からすると事後分布です.

  19. この例は, Richard McElreathがStan users groupで提起しました. BUGSおよびJAGSで使われるギブズサンプリングと, Stanで使われるハミルトニアンモンテカルロ(HMC)およびno-U-turnサンプラー(NUTS)との挙動の違いについての質問の中でのことです.

  20. 訳注: 原文では, λ1 + q, λ1 − qですが, 誤植のようです.

  21. σの周辺事後分布p(σ ∣ y)はこの場合, 少なくとも異なる2つのデータ点がある限りは正則です.

  22. 訳注: 原文では, λ2 = λ1 + cとなっていますが, 誤りのようです.

  23. 訳注: 原文ではcを能力に加え, 難易度から引くとなっていますが, 上の式からすると双方に加えるのが正しいようです.

  24. 訳注: xkの係数は正しくは(βk + c(1 − d)βk)のようです.

  25. 訳注: 原文はdifficultyですが, discriminationの誤りと思われます

  26. ラプラス分布を事前分布にすること(あるいは罰則付き最尤推定のL1正規化)は加法的な不変性を取り除くには十分ではありません. 縮小はしますが, それ自体がパラメータを識別するというわけではありません. λ1に定数を加え, λ2からそれを引くと, 事前分布で同じ値となる場合があるからです.

  27. 訳注: 原文はpriorですが, 文脈からすると事後分布と思われます.

  28. テンパリング法が, 局所最頻値を探す自動化した方法と見られることもあります. しかし, ほとんどのMCMCテンパリング法は, 止めることが難しく, そのまま局所最頻値を探し続けます. Swendsen and Wang, 1986; Neal, 1996bを参照.

  29. 訳注: 原文は誤っているようです.

  30. 訳注: 原文はmiddleですが, 誤りと思われます.