TensorFlow : Edward : 混合分布モデル (教師なし学習) (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 11/15 (v0.5.0), 11/02/2018
作成日時 : 10/21/2018
* 本ページは、Edward サイトの Tutorials : Unsupervised Learning を翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
混合分布モデル (教師なし学習)
教師なし学習では、タスクは訓練サンプル $\{x_n\}$ から成る、ラベル付けされていないデータから隠れ構造を推論します。
Edward の例で示します。Jupyter notebook による対話的バージョンは こちら で利用可能です。
データ
2-次元データポイント $\mathbf{x}_n\in\mathbb{R}^2$ のシミュレートされたデータセットを使用します。
def build_toy_dataset(N): pi = np.array([0.4, 0.6]) mus = [[1, 1], [-1, -1]] stds = [[0.1, 0.1], [0.1, 0.1]] x = np.zeros((N, 2), dtype=np.float32) for n in range(N): k = np.argmax(np.random.multinomial(1, pi)) x[n, :] = np.random.multivariate_normal(mus[k], np.diag(stds[k])) return x N = 500 # number of data points D = 2 # dimensionality of data x_train = build_toy_dataset(N)
生成されたデータポイントを可視化します。
plt.scatter(x_train[:, 0], x_train[:, 1]) plt.axis([-3, 3, -3, 3]) plt.show()
モデル
混合分布モデル (= mixture model) は典型的にはクラスタリングのために使用されるモデルです。それは各データポイントに混合成分 (= mixture component) を割り当てて、この混合成分はデータポイントが生成される分布を決定します。ガウシアン混合分布 はこのデータを生成するためにガウシアン分布を使用します (Bishop, 2006)。
$N$ データポイントのセットについて、各観測 $\mathbf{x}_n$ の尤度は :
\[
\begin{aligned}
p(\mathbf{x}_n \mid \pi, \mu, \sigma)
&=
\sum_{k=1}^K \pi_k \, \text{Normal}(\mathbf{x}_n \mid \mu_k, \sigma_k).\end{aligned}
\]
潜在変数 $\pi$ は $K$-次元確率ベクトルで個々のガウス分布を混合します、それぞれは平均 $\mu_k$ と標準偏差 $\sigma_k$ で特徴付けられます。
$\sum_{k=1}^K\pi_k=1$ であるような $\pi\in[0,1]$ 上の事前分布を (固定された $\alpha=1$ に対して)
\[
\begin{aligned}
p(\pi)
&=
\text{Dirichlet}(\pi \mid \alpha \mathbf{1}_{K})\end{aligned}
\]
と定義します。各成分 $\mathbf{\mu}_k\in\mathbb{R}^D$ 上の事前分布を
\[
\begin{aligned}
p(\mathbf{\mu}_k)
&=
\text{Normal}(\mathbf{\mu}_k \mid \mathbf{0}, \mathbf{I})\end{aligned}
\]
と定義します。各成分 $\mathbf{\sigma}_k^2\in\mathbb{R}^D$ 上の事前分布を
\[
\begin{aligned}
p(\mathbf{\sigma}_k^2)
&=
\text{InverseGamma}(\mathbf{\sigma}_k^2 \mid a, b)\end{aligned}
\]
と定義します。
私達は Edward でモデルの 2 つのバージョンを構築します: 一つは潜在変数として混合割り当て $c_n\in\{0,\ldots,K-1\}$ を結合的に (= jointly) 伴い、そしてもう一つはそれらを総計したものを伴います。
結合バージョンは混合割り当てに対して明示的な潜在変数を含みます。私達はこれを ParamMixture 確率変数で実装します; それは入力として混合確率、成分のパラメータ、そして成分の分布を取ります。(これを最初に z に対する ‘Categorical‘ 確率変数を構築してから x を構築することにより別々に書くこともまたできることに注意してください; ParamMixture は tf.gather の要求を回避します、それは少しだけより効率的です。)
from edward.models import Dirichlet, InverseGamma, MultivariateNormalDiag, \ Normal, ParamMixture K = 2 # number of components pi = Dirichlet(tf.ones(K)) mu = Normal(tf.zeros(D), tf.ones(D), sample_shape=K) sigmasq = InverseGamma(tf.ones(D), tf.ones(D), sample_shape=K) x = ParamMixture(pi, {'loc': mu, 'scale_diag': tf.sqrt(sigmasq)}, MultivariateNormalDiag, sample_shape=N) z = x.cat
崩壊型 (= collapsed) バージョンは混合割り当てを重視しません。これを Mixture 確率変数で実装します; それは入力として Categorical 分布と個々の分布成分のリストを取ります。それは混合割り当てを総計した (= sum out) 混合分布です。
from edward.models import Categorical, Dirichlet, InverseGamma, Mixture, \ MultivariateNormalDiag, Normal K = 2 # number of components pi = Dirichlet(tf.ones(K)) mu = Normal(tf.zeros(D), tf.ones(D), sample_shape=K) sigma = InverseGamma(tf.ones(D), tf.ones(D), sample_shape=K) cat = Categorical(probs=pi, sample_shape=N) components = [ MultivariateNormalDiag(mu[k], sigma[k], sample_shape=N) for k in range(K)] x = Mixture(cat=cat, components=components)
この解析では結合バージョンを使用します。
推論
モデルの各分布は共役事前分布で書かれますので、Gibbs サンプリングを使用できます。それは各分布の完全条件付き分布 (= complete conditionals) (i.e. 前にドローされた値上で条件付けられた各分布) からのドローに渡る反復によりマルコフ連鎖モンテカルロを遂行します。最初に Empirical 確率変数をセットアップします、これはサンプルのコレクションを使用して事後分布を近似します。
T = 500 # number of MCMC samples qpi = Empirical(tf.get_variable( "qpi/params", [T, K], initializer=tf.constant_initializer(1.0 / K))) qmu = Empirical(tf.get_variable( "qmu/params", [T, K, D], initializer=tf.zeros_initializer())) qsigmasq = Empirical(tf.get_variable( "qsigmasq/params", [T, K, D], initializer=tf.ones_initializer())) qz = Empirical(tf.get_variable( "qz/params", [T, N], initializer=tf.zeros_initializer(), dtype=tf.int32))
Gibbs サンプリングを実行します、クラスター mean をサンプラー進捗として追跡できるように訓練ループを明示的に書きます。
inference = ed.Gibbs({pi: qpi, mu: qmu, sigmasq: qsigmasq, z: qz}, data={x: x_train}) inference.initialize() sess = ed.get_session() tf.global_variables_initializer().run() t_ph = tf.placeholder(tf.int32, []) running_cluster_means = tf.reduce_mean(qmu.params[:t_ph], 0) for _ in range(inference.n_iter): info_dict = inference.update() inference.print_progress(info_dict) t = info_dict['t'] if t % inference.n_print == 0: print("\nInferred cluster means:") print(sess.run(running_cluster_means, {t_ph: t - 1}))
訓練の間に追跡された推論されたクラスタ mean については関連する Jupyter notebook を見てください。
批評
各データポイントの予測されたメンバーシップを可視化します。各データポイントに対する最高事後予測密度 (= the highest posterior predictive density) を生成するクラスタ割り当てを選択します。
これを行なうために、最初に事後分布からサンプルをドローして対数尤度の $N\times K$ 行列を計算します、各データポイント $\mathbf{x}_n$ とクラスタ割り当て $k$ のために一つです。100 事後分布サンプルに渡りこれを平均化して遂行します。
# Calculate likelihood for each data point and cluster assignment, # averaged over many posterior samples. ``x_post`` has shape (N, 100, K, D). mu_sample = qmu.sample(100) sigmasq_sample = qsigmasq.sample(100) x_post = Normal(loc=tf.ones([N, 1, 1, 1]) * mu_sample, scale=tf.ones([N, 1, 1, 1]) * tf.sqrt(sigmasq_sample)) x_broadcasted = tf.tile(tf.reshape(x_train, [N, 1, 1, D]), [1, 100, K, 1]) # Sum over latent dimension, then average over posterior samples. # ``log_liks`` ends up with shape (N, K). log_liks = x_post.log_prob(x_broadcasted) log_liks = tf.reduce_sum(log_liks, 3) log_liks = tf.reduce_mean(log_liks, 1)
それからカラムに沿って $\arg\max$ を取ります (クラスタ割り当て)。
clusters = tf.argmax(log_liks, 1).eval()
データポイントをそれらの予測されたメンバーシップにより彩色してプロットします。
plt.scatter(x_train[:, 0], x_train[:, 1], c=clusters, cmap=cm.bwr) plt.axis([-3, 3, -3, 3]) plt.title("Predicted cluster assignments") plt.show()
モデルはデータを正しくクラスタリングしました。
References
- Bishop, C. M. (2006). Pattern recognition and machine learning. Springer New York.
以上