Modelクラスの初期化処理 [e334115, d07338e, 93bc07b] - pymc4のソースコード読んでみた
TL;DR
- まずは
Model
クラスの初期化処理系のメソッドを読んでみます。__init__
: 初期化処理いろいろdefine
:self._f
を設定して変数初期化しているようですが、今のところテスト用のヘルパー関数に見えますconfigure
: 設定を上書きして変数初期化_init_variables
: 次回読みます
コミット
2018/06/09から2018/06/11の間のコミットです。
- tmp · pymc-devs/pymc4@e334115 · GitHub
- restructure + test point implementation · pymc-devs/pymc4@d07338e · GitHub
- fixes · pymc-devs/pymc4@93bc07b · GitHub
Model - pymc4/model/base.py
init
初期化処理内では、以下プロパティを設定しているみたいです。
- _cfg: 設定情報
- name: モデルの名称(任意)
- _f:
None
を設定してるので、後で使用されると推測 - _variables:
None
を設定してるので、後で使用されると推測 - _observed:
dict()
で初期化してるので、key(RandomVariableの名前)
とvalue(RandomVariableのインスタンス)
が入ると推測 - session: tensorflowの
session
を初期化もしくは引数から設定しているだけです。
class Model(object): def __init__(self, name=None, graph=None, session=None, **config): self._cfg = Config(**config) self.name = name self._f = None self._variables = None self._observed = dict() if session is None: session = tf.Session(graph=graph) self.session = session
configure
_cfg
を上書きして _init_variables
を呼んでいるだけです。
def configure(self, **override): self._cfg.update(**override) self._init_variables() return self
define
引数の f
を設定して、 _init_variables
を呼んでいるだけです。
def define(self, f): self._f = f self._init_variables() return f
pymc4/model/base.py
の最下段で以下のように define
が使われています。
@biwrap.biwrap def inline(f, **kwargs): model = Model(**kwargs) model.define(f) return model
inline
は tests/test_model.py
内の以下にある通り、まずはテスト実行時のヘルパー関数のように使用されています。
def test_testvalue(): @pm.inline def model(cfg): ed.Normal(0., 1., name='normal')
つまり、define
内の self._f
には def model(cfg)
の関数が設定されることがわかります(以下は擬似コードです)
self._f = def model(cfg): ed.Normal(0., 1., name='normal')
_init_variables
この辺りは interceptors
周りの動きが分からないと理解できなそうなので、次回読もうと思います。
def _init_variables(self): info_collector = interceptors.CollectVariablesInfo() with self.graph.as_default(), ed.interception(info_collector): self._f(self.cfg) self._variables = info_collector.result
参考資料
なぜPyMC4のバックエンドにTensorFlowが採用されたのか?
TL;DR
- 以下記事をもとに、PyMC4のバックエンドにtensorflowが採用された経緯をまとめました。
see: Theano, TensorFlow and the Future of PyMC – PyMC Developers – Medium
ポイント
- tensorflowには既に多くのユーザがいること(デファクトスタンダードであること)
- 確率プログラミングに必要な確率分布や変換処理が実装されているtensorflow probability(edward2)が使えること
- tensorflow probabilityは低レベルでフレキシブルなAPI、PyMC4は高レベルでユーザフレンドリーなAPIという棲み分けが上手くできること
PyMC4のバックエンドにtensorflowが採用された理由
PyMC3のバックエンドのTheanoが開発停止
まずは2018/05/18の以下記事でPyMC DevelopersからPyMC4のアナウンスが出されています。
see: Theano, TensorFlow and the Future of PyMC – PyMC Developers – Medium
PyMC3 is an open-source library for Bayesian statistical modeling and inference in Python, implementing gradient-based Markov chain Monte Carlo, variational inference, and other approximation methods. These algorithms currently rely on Theano for computation, specifically for providing gradients.
(PyMC4の前バージョンである)PyMC3のバックエンドは Theano に依存していたことがわかります。
Since the Theano team announced that it would cease development and maintenance of Theano within a year, we, the PyMC developers, have been actively discussing what to do about this.
しかし、Theanoのチームが年内の開発とメンテナンスをやめることをアナウンスしたため、PyMCとしては今後はTheano以外のバックエンドを採用する必要があり、そのディスカッションを行ったとのことです。
Theanoのgithubレポジトリを見ると、 MILA will stop developing Theano.
と記載されています。
また、開発・メンテナンスしていた Mila がTheanoを止めた理由が以下forumに投稿されています。
see: Forum: MILA and the future of Theano
要約すると、MILAチームは約10年程度をTheanoの開発に費やしたが現在の深層学習研究の状況が進んだことで様々なフレームワークが使用可能になったため、Theanoの役目は終わったということのようです。開発停止の告知文からは、Theanoによって様々なイノベーションを推進してきたという誇りが垣間見えます。
Tensorflow (Tensorflow Probability) を採用した理由
理由としては以下が挙げられています。
- tensorflowには既に多くのユーザがいること(デファクトスタンダードであること)
- 確率プログラミングに必要な確率分布や変換処理が実装されているtensorflow probability(edward2)が使えること
TFP(tensorflow probability)はフレキシブルな統計モデルを可能にしますが、低レベルのAPIしか提供しないためそこまでユーザフレンドリーではありません。その点を補完する、より使いやすい高レベルなAPIを提供するのがPyMCの役目とのことです。
なぜ他のライブラリが採用されなかったのか?
PyMC4のバックエンドに関するディスカッションの様子は以下から見ることができますが、ここでTheanoの代替として何をバックエンドに採用するかが議論されています。
選択肢として挙がっていたのは、主に以下でした。
- PyTorch
- Pyro: PyTorchをバックエンドとした確率プログラミングライブラリ(by Uber)
- TensorFlow
- Edward: TensorFlowをバックエンドとした確率プログラミングライブラリ
- Microsoft Cognitive Toolkit (CNTK)
- MxNet
ディスカッション内でのやり取りを見ると、Edwardの作者であるDustinがPyMC4のオーガナイズをしているtweickiと会話しており、この後実際にミーティングで直に話したようです。
細かい詳細はわかりませんが、やはりTFPは低レベルでフレキシブルなAPIを提供し、それをラップするPyMC4は高レベルでユーザフレンドリーなAPIを提供する、という役割分担の点で考えが一致したのが大きな理由のように思えます。
参考資料
biwrapを使用したinline関数 [e334115, d07338e, 93bc07b] - pymc4のソースコード読んでみた
TL;DR
コミット
2018/06/09から2018/06/11の間のコミットです。
- tmp · pymc-devs/pymc4@e334115 · GitHub
- restructure + test point implementation · pymc-devs/pymc4@d07338e · GitHub
- fixes · pymc-devs/pymc4@93bc07b · GitHub
inline - pymc4/model/base.py
model.define
を常に呼ぶようにwrapしたアノテーションのようです。 define
メソッドのの中身はおそらく初期化処理等と推測できます( Model
クラス内の処理についてはまだ読んでいないのでよくわかかっていません)。
@biwrap.biwrap def inline(f, **kwargs): model = Model(**kwargs) model.define(f) return model
- 使い方:
tests/test_model.py
参照
以下にある通り、まずはテスト実行時のヘルパー関数のように使用していることがわかります。
def test_testvalue(): @pm.inline def model(cfg): ed.Normal(0., 1., name='normal')
参考資料
アーキテクチャをPyMC3からPyMC4へ [e334115, d07338e, 93bc07b] - pymc4のソースコード読んでみた
TL;DR
- 今までのコミットの
Model
やRandomVariable
は削除され、アーキテクチャも変更されています。削除されたコードはpymc3とほぼ同等だったので、試しに書いたコードだったようです汗。ほとんど0から読み進める感じになるので、焦らず読みやすそうな箇所から辿っていきます。
コミット
2018/06/09から2018/06/11の間のコミットです。
- tmp · pymc-devs/pymc4@e334115 · GitHub
- restructure + test point implementation · pymc-devs/pymc4@d07338e · GitHub
- fixes · pymc-devs/pymc4@93bc07b · GitHub
以下のファイルが追加・変更・削除されています。多いので少しづつ読んでいきます。
- pymc4/init.py
- pymc4/distributions/base.py
- pymc4/inference/init.py
- pymc4/inference/sampling/sample.py
- pymc4/model.py
- pymc4/random_variable.py
- pymc4/sample.py → pymc4/sampling.py
- pymc4/util/init.py
- pymc4/util/graph.py
- pymc4/util/interceptors.py
- tests/test_model.py
- test/test_nothing.py → tests/test_nothing.py
- requirements.txt
どこから読むか?
他ドキュメントやブログ記事を読んでいると、とりあず以下のように Model
クラスのインスタンスを作成することがわかっているので、 pymc4/model
パッケージ内のファイルから読み進めてみます。
model = pm4.Model([引数])
pymc4/modelパッケージ
対象パッケージ内には以下2ファイルが存在します。
- init.py
- base.py
init.py
base.py
内の Model
クラスと inline
関数をimportしているだけです。
from .base import ( Model, inline )
base.py
base.py
内では以下3つが定義されています。
- Configクラス(
__all__
に含まれていないのでexportされていない) - Modelクラス
- inline関数
まずは簡単な Config
クラスから読みます。
Config - base.py
dict
を継承したコンフィグ用のクラスを定義しているだけです。exportされていないので、 Model
クラス内でしか使用されないことがわかります。
class Config(dict): def __getattr__(self, item): try: return self.__getitem__(item) except KeyError as e: error = KeyError(item, '"{i}" is not found in configuration for the model, ' 'you probably need to pass "{i}" in model definition as ' '\n`model = pm.Model(..., {i}=value)`' '\nor' '\n' '\n@pm.inline(..., {i}=value)' '\ndef model(cfg):' '\n # your model starts here' '\n ...'.format(i=item)) raise error from e
__getattr__
が呼ばれた際に、 __get__item
を代わりに呼んでいるだけです。
- 例)
c.a
=>c['a']
また __getitem__
は KeyError
が発生する恐れがあるので、エラー情報にusageを含めています。
"{i}" is not found in configuration for the model, you probably need to pass "{i}" in model definition as `model = pm.Model(..., {i}=value)` or @pm.inline(..., {i}=value) def model(cfg): # your model starts here' ...
使い方として以下の2パターンがあることがわかります。
pm.Model
を使う場合
model = pm.Model(..., {i}=value)
@pm.inline
を使う場合
@pm.inline(..., {i}=value) def model(cfg): ...
次回はModelクラスを読んでいきます。
参考資料
pymc4のソースコード読んでみた - 依存ライブラリの対応やlint [ac77b27, bec2985, ce1d895]
TL;DR
- 依存ライブラリである
tensorflow_probability
が原因でrequirements.txt
の現状の書き方では最新バージョンを上手く取得できない問題があったみたいです。新しいライブラリなので仕方ないですね。
コミット
2018/06/03のコミットです。
- Update requirements.txt · pymc-devs/pymc4@ac77b27 · GitHub
- Modify .travis.yml · pymc-devs/pymc4@bec2985 · GitHub
- Fix pylint refactor errors · pymc-devs/pymc4@ce1d895 · GitHubx
以下のファイルが変更されています。
- requirements.txt
- バージョン周りの問題の一時的な対応(後述)
- .travis.yml
- pylint実行時にcurrent directoryも
sys.path
に含める Usage and Invocations — pytest documentation
- pylint実行時にcurrent directoryも
- pymc4/sample.py
- pylintのエラー対応
- pymc4/model.py
- pylintのエラー対応
- pymc4/random_variable.py
- pylintのエラー対応
- pymc4/sample.py
- pylintのエラー対応
requirements.txt
依存ライブラリである tensorflow_probability
が原因で以下を requirements.txt
に記載してもlatestバージョンが取得できない問題があったみたいです。
tf-nightly tfp-nightly tb-nightly
一時的な対応として結局 tfp-nightly==0.0.1.dev20180515
のようにバージョンを明示的に指定する方法で対応しています。
tf-nightly==1.8.0.dev20180331 tfp-nightly==0.0.1.dev20180515 tb-nightly==1.8.0a20180424
参考資料
pymc4のソースコード読んでみた - “RandomVariable”, Add model tests, fix travis pytest problem [4357d39]
TL;DR
treedict
クラスの依存性を以前のコミットで削除した代わりに、RandomVariable
クラス内でModel
のcontexts.stack
全てに値を設定することで、ほぼ同等の動きをしている。
コミット
2018/06/03のコミットです。
RandomVariable - random_variable.py
どちらにしろ挙動が変わらないことがわかる
- 【before】treedictクラスに依存している場合、ModelがネストするとparentにもRandomVariableが伝搬する
with WithTreeModel(name="model1") as model1: rv1 = WithTreeRandomVariable(name="rv1") with WithTreeModel(name="model2") as model2: rv2 = WithTreeRandomVariable(name="rv2") rv3 = WithTreeRandomVariable(name="rv3") print("model1: {}".format([v for v in model1.named_vars])) print("model2: {}".format([v for v in model2.named_vars])) # 出力: # => model1: ['rv1', 'rv2', 'rv3'] # => model2: ['rv2', 'rv3']
- 【after】treedictクラスに依存せず、RandomVarible内でModel.get_contextsを辿っている場合、ModelがネストするとparentにもRandomVariableが伝搬する
with Model(name="model1") as model1: rv1 = RandomVariable(name="rv1") with Model(name="model2") as model2: rv2 = RandomVariable(name="rv2") rv3 = RandomVariable(name="rv3") print("model1: {}".format([v for v in model1.named_vars])) print("model2: {}".format([v for v in model2.named_vars])) # 出力: # => model1: ['rv1', 'rv2', 'rv3'] # => model2: ['rv2', 'rv3']
TODO
- [ ] ほぼ同じ挙動がに見えるが、設計を少し変更した理由がわからない
参考資料
pymc4のソースコード読んでみた - Add model tests, fix travis pytest problem [4357d39]
コミット
2018/06/03のコミットです。
以下ファイルが変更されています。
- .gitignore
.pytest_cache/
を追記
- .pylintrc
- naming convention のlintチェックをdisable: C0103 - PyLint Messages
- .travis.yml
- pytest実行時にcurrent directoryも
sys.path
に含める Usage and Invocations — pytest documentation
- pytest実行時にcurrent directoryも
- pymc4/init.py
*(ワイルドカード)
のimportをしてもpytlintエラーにならないようにしている(簡便さのため)
- pymc4/model.py
modelcontext
を__all__
に含めている
- pymc4/random_variable.py
- 後述
- pymc4/sample.py
- test/test_model.py
- 後述
- test/test_nothing.py
RandomVariable - random_variable.py
重要そうなので次回じっくり読みます。
test_model.py
重要そうなので次々回じっくり読みます。