TensorFlow Probability : Edward2 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 11/18/2018
作成日時 : 11/01/2018
* 本ページは、tensorflow/probability の Edward2 の README.md を翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
TensorFlow Probability : Edward2
Edward2 は TensorFlow と Python の確率的プログラミング言語です。それは TensorFlow エコシステムを拡張し、その結果、モデルを確率的プログラムとして宣言して柔軟な訓練、潜在変数推論、そして予測のためにモデルの計算を操作できます。
貴方は Edward からアップグレードしていますか?ガイド Upgrading_from_Edward_to_Edward2.md をチェックしてください。
1. 確率的プログラムとしてのモデル
確率変数
Edward2 では、確率的モデルの構造を指定するために RandomVariables を使用します。確率変数 rv は確率分布 (rv.distribution) を運搬 (= carry) し、これは log_prob と sample のような確率変数のメソッドを管理する TensorFlow Distribution インスタンスです。
確率変数は TensorFlow Distributions のように形成されます。
from tensorflow_probability import edward2 as ed normal_rv = ed.Normal(loc=0., scale=1.) ## <ed.RandomVariable 'Normal/' shape=() dtype=float32> normal_rv.distribution.log_prob(1.231) ## <tf.Tensor 'Normal/log_prob/sub:0' shape=() dtype=float32> dirichlet_rv = ed.Dirichlet(concentration=tf.ones([2, 10])) ## <ed.RandomVariable 'Dirichlet/' shape=(2, 10) dtype=float32>
デフォルトでは、確率変数 rv のインスタンス化は tensor rv.value ~ rv.distribution.sample() を形成するためにサンプリング op を作成します。サンプルのデフォルト数 (rv への sample_shape 引数を通して制御可能です) は一つで、そしてオプションの value 引数が提供されれば、サンプリング op は作成されません。確率変数は TensorFlow op と相互作用できます: TF ops はサンプル上で動作します。
x = ed.Normal(loc=tf.zeros(10), scale=tf.ones(10)) y = 5. x + y, x / y ## (<tf.Tensor 'add:0' shape=(10,) dtype=float32>, ## <tf.Tensor 'div:0' shape=(10,) dtype=float32>) tf.tanh(x * y) ## <tf.Tensor 'Tanh:0' shape=(10,) dtype=float32> x[2] # 3rd normal rv ## <tf.Tensor 'strided_slice:0' shape=() dtype=float32>
確率モデル
Edward2 の確率モデルは一つまたはそれ以上の RandomVariables をインスタンス化する Python 関数として表現されます。典型的には、関数 (「プログラム」) は生成過程を実行してサンプルを返します。関数への入力はモデルがその上で条件付けられる値として考えることができます。
下でベイズロジスティック回帰を書きますが、そこでは特徴、係数、そして切片が与えられたとき二値出力結果が生成されます。係数と切片に渡る事前分布があります。関数の実行は TensorFlow グラフに演算を追加します、そして TensorFlow session で結果ノードを求めると事前分布から係数と切片をサンプリングし、そしてこれらのサンプルを出力結果を計算するために使用します。
def logistic_regression(features): """Bayesian logistic regression p(y | x) = int p(y | x, w, b) p(w, b) dwdb.""" coeffs = ed.Normal(loc=tf.zeros(features.shape[1]), scale=1., name="coeffs") intercept = ed.Normal(loc=0., scale=1., name="intercept") outcomes = ed.Bernoulli( logits=tf.tensordot(features, coeffs, [[1], [0]]) + intercept, name="outcomes") return outcomes num_features = 10 features = tf.random_normal([100, num_features]) outcomes = logistic_regression(features) # Execute the model program, returning a sample np.ndarray of shape (100,). with tf.Session() as sess: outcomes_ = sess.run(outcomes)
Edward2 プログラムはまたデータを直接的にモデル化したものを越えて分布を表わすこともできます。例えば、下では学習可能な分布をそれをロジスティック回帰事後分布に近似する意図で書きます。
import tensorflow_probability as tfp def logistic_regression_posterior(num_features): """Posterior of Bayesian logistic regression p(w, b | {x, y}).""" posterior_coeffs = ed.MultivariateNormalTriL( loc=tf.get_variable("coeffs_loc", [num_features]), scale_tril=tfp.trainable_distributions.tril_with_diag_softplus_and_shift( tf.get_variable("coeffs_scale", [num_features*(num_features+1) / 2])), name="coeffs_posterior") posterior_intercept = ed.Normal( loc=tf.get_variable("intercept_loc", []), scale=tfp.trainable_distributions.softplus_and_shift( tf.get_variable("intercept_scale", [])), name="intercept_posterior") return coeffs, intercept coeffs, intercept = logistic_regression_posterior(num_features) # Execute the program, returning a sample # (np.ndarray of shape (55,), np.ndarray of shape ()). with tf.Session() as sess: sess.run(tf.global_variables_initializer()) posterior_coeffs_, posterior_intercept_ = sess.run( [posterior_coeffs, posterior_intercept])
上の例で tfp.trainable_distributions の使用方法に注意してください。それは (正規分布のスケールのように) 制約付き空間にあるパラメータを持つ分布のためのユティリティを提供します。
変分プログラムとしてのテクニックの完全な例については、確率的 PCA チュートリアル を見てください。
モデル計算を操作する
Interceptor
確率モデルの訓練とテストは典型的には生成過程からの単なるサンプル以上を必要とします。柔軟な訓練とテストを有効にするために、interceptor を使用してモデルの計算を操作します。
interceptor はもう一つの関数 f とその引数 *args, **kwargs 上で動作する関数です。それは出力を返す前に様々な計算を遂行します (典型的には f(*args, **kwargs) : その関数自身を適用する結果です)。ed.interception コンテキスト・マネージャは interceptor をスタック上にプッシュし、任意のインターセプト可能な関数はスタックによりインターセプトされます。総ての確率変数コンストラクタはインターセプト可能です。
下ではロジスティック回帰モデルの生成過程をインターセプトします。特に、その事前分布ではなく学習された事後分布平均で予測を行ないます。
def set_prior_to_posterior_mean(f, *args, **kwargs): """Forms posterior predictions, setting each prior to its posterior mean.""" name = kwargs.get("name") if name == "coeffs": return posterior_coeffs.distribution.mean() elif name == "intercept": return posterior_intercept.distribution.mean() return f(*args, **kwargs) with ed.interception(set_prior_to_posterior_mean): predictions = logistic_regression(features) training_accuracy = ( tf.reduce_sum(tf.cast(tf.equal(predictions, outcomes), tf.float32)) / tf.cast(tf.shape(outcomes), tf.float32))
プログラム変換
interceptor を使用して、プログラム変換を適用することもできます、これはモデルの一つの表現から他のものへマップします。これは (ダウンストリームなユースケースに依拠する) 異なるモデル特性への便利なアクセスを提供します。
例えば、マルコフ連鎖モンテカルロ・アルゴリズムはしばしば入力としてモデルの対数同時確率関数を必要とします。下では生成過程を指定するベイズロジスティック回帰問題を取りそしてその対数同時確率関数を得るために組み込みの ed.make_log_joint 変換を適用します。対数同時関数は入力として生成プログラムの元の入力とプログラムの確率変数を取ります。それは確率変数対数確率に渡り総計したスカラー Tensor を返します。
私達の例では、特徴と出力結果は固定され、coeffs と intercept の事後分布からサンプルをドローするためにハミルトニアン・モンテカルロを使用することを望みます。この使用のために、target_log_prob_fn を作成します、これは単に coeffs と intercept を引数として取り入力特徴をピンで止めてその既知の値として rv 結果を出力します。
import tensorflow_probability as tfp # Set up training data. features = tf.random_normal([100, 55]) outcomes = tf.random_uniform([100], minval=0, maxval=2, dtype=tf.int32) # Pass target log-probability function to MCMC transition kernel. log_joint = ed.make_log_joint_fn(logistic_regression) def target_log_prob_fn(coeffs, intercept): """Target log-probability as a function of states.""" return log_joint(features, coeffs=coeffs, intercept=intercept, outcomes=outcomes) hmc_kernel = tfp.mcmc.HamiltonianMonteCarlo( target_log_prob_fn=target_log_prob_fn, step_size=0.1, num_leapfrog_steps=5) states, kernel_results = tfp.mcmc.sample_chain( num_results=1000, current_state=[tf.random_normal([55]), tf.random_normal([])], kernel=hmc_kernel, num_burnin_steps=500) with tf.Session() as sess: states_, results_ = sess.run([states, kernels_results])
返された states_[0] と states_[1] はそれぞれ coeffs と intercept のための 1,000 事後サンプルを含みます。これは例えば、新しいデータ上でモデルの事後予測を評価するために使用されるかもしれません。
Examples
end-to-end サンプルとチュートリアルに興味がありますか?tensorflow_probability/examples/ を見てください。
以上