EdwardによるDeep Beta Distribution(深層ベータ分布)モデル

こんにちは。お久しぶりの投稿です。

来週末に開催される db analytics show case Sapporo

www.db-tech-showcase.com


と言うイベントで講演する事になってまして、ベイズ統計やMCMCの基本的なところからEdwardのデモまでやっていく予定なのですが

ただ基本的なモデルを紹介するのもおもしろくないので、僕なりの新しい深層学習+確率モデリングなモデルを考えましたので紹介したいと思います。

EdwardやMCMCの基本的なところについては弊社のブログに寄稿した以下の記事が詳しいので合わせてそちらも御覧ください↓

data.gunosy.io

§1. お気持ち

例えば、ユーザーが付ける商品のレーティングを予測したいと考えます。

これは通常ターゲット { y } が閉区間{ [ 0, 1 ] } に値を取る回帰問題として定式化されます。*1

与えられた特徴量に対して、予測値 { y }を出力するわけですが、それは決定論的に決まるものなのか疑問があります。


ユーザーのレーティングであれば、気分・時間・各ユーザーが商品に対する持つ偏見(例えば◯◯製の製品は嫌い)等、ランダムネスがかなりあるように思われます。


つまり、ユーザーの特徴量と商品の特徴量が与えられた時に、閉区間{ [ 0, 1 ] }上の確率分布が定まりそこからレーティングがサンプリングされるようにモデリングしたほうが適切なのではないのかと考えるわけです。

§2. 定式化

深層学習の優秀さは言うまでもないので、それと上の確率モデリングの問題を上手く融合させます。

区間{ [ 0, 1 ] }上の連続値確率分布として、代表的なベータ分布があると思いますが

2つの正のパラメータを変化させる事によって様々な閉区間{ [ 0, 1 ] }上の確率分布を得る事が出来る事を思い出しましょう。


これを上手く利用して、特徴量 {x } に対して ベータ分布の2つのパラメータが出力されるようなニュールネットを作ります。

これにより、特徴量が与えられた際に様々な形状の閉区間{ [ 0, 1 ] }上の確率分布が出力されるモデルが得られます。

*2としてはこんな感じです。


f:id:mathetake:20170624134023p:plain

§3. 簡単な実験

今回のデータセットは 特徴量{x=(x_i)_i \in \mathbb{R}^{X_{DIM}} } に対してベータ分布のパラメタ {\alpha_x, \beta_x > 0}

{\alpha_x = \dfrac{1}{X_{DIM}}\sum \dfrac{1}{ 1 + \exp(- x_i) }}
{\beta_x = \dfrac{1}{X_{DIM}}\sum |x_i| }

により定まりそこからターゲット {y}がサンプリングされていると言う非線形なお遊びデータセットを使い、4層のニューラルネットを使ってモデリングしていきます。



まず必要なライブラリをimportして、定数とデータセットを準備します。

import numpy as np
import tensorflow as tf
import edward as ed
from edward.models import Normal, Empirical, 

# consts
N = 10000  # training sample size
N_test = 1000 # test sample size
X_DIM = 500  # input dim
MINI_BATCH_SIZE = 100  # batch_size
N_ITER = 10000 # number of iteration of MCMC
np.random.seed(seed=1)

# true_alpha =  softmax(x).mean()
# true_beta = |x|.mean()
def generate_samples(n):
    x = np.random.normal(size=[n, X_DIM])
    true_alpha = (1.0 / (1.0 + np.exp(-x))).mean()
    true_beta = np.absolute(x).mean()
    y = np.random.beta(a=true_alpha, b=true_beta, size=[n])
    return x, y

X_data, Y_data = generate_samples(N)
X_test, Y_test = generate_samples(N_test)

サンプルサイズと次元から、通常のMCMCでは歯が立たないのでSGMCMCを使っていくことにします。
そのためにunbiasedなミニバッチを生成する関数を定義しておきます。

# a function for generating unbiassed mini batches
def next_batch(mini_batch_size=MINI_BATCH_SIZE):
    indexes = np.random.randint(N, size=mini_batch_size)
    return X_data[indexes], Y_data[indexes]

ここからEdwardでモデリングを開始します。
まずニューラルネットのパラメタを準備して事前分布を定義し、またそれらを近似するための経験分布を定義します。

# define prior distributions over parameters
W_0 = Normal(loc=tf.zeros([X_DIM, 50]), scale=tf.ones([X_DIM, 50]))
W_1 = Normal(loc=tf.zeros([50, 10]), scale=tf.ones([50, 10]))
W_2 = Normal(loc=tf.zeros([10, 2]), scale=tf.ones([10, 2]))
b_0 = Normal(loc=tf.zeros(50), scale=tf.ones(50))
b_1 = Normal(loc=tf.zeros(10), scale=tf.ones(10))
b_2 = Normal(loc=tf.zeros(2), scale=tf.ones(2))

# define empirical distributions for prameter inference
q_W0 = Empirical(params=tf.Variable(tf.random_normal([N_ITER, X_DIM, 50])))
q_W1 = Empirical(params=tf.Variable(tf.random_normal([N_ITER, 50, 10])))
q_W2 = Empirical(params=tf.Variable(tf.random_normal([N_ITER, 10, 2])))
q_b0 = Empirical(params=tf.Variable(tf.random_normal([N_ITER, 50])))
q_b1 = Empirical(params=tf.Variable(tf.random_normal([N_ITER, 10])))
q_b2 = Empirical(params=tf.Variable(tf.random_normal([N_ITER, 2])))


次にモデルを構築します

def build_deep_Beta_distribution(x):
    h_1 = tf.tanh(tf.matmul(x, W_0) + b_0)
    h_2 = tf.tanh(tf.matmul(h_1, W_1) + b_1)
    h_3 = tf.nn.softplus(tf.matmul(h_2, W_2) + b_2)
    alpha = h_3[:, 0]
    beta = h_3[:, 1]
    return Beta(concentration1=alpha, concentration0=beta)

# prepare placeholders for data points
x_ph = tf.placeholder(tf.float32, [MINI_BATCH_SIZE, X_DIM])
y_ph = tf.placeholder(tf.float32, [MINI_BATCH_SIZE])

# build our model
y = build_deep_Beta_distribution(x_ph)


今回は、推定アルゴリズムとしてSGHMCを使います。SMHMCはHMCを単純に確率勾配にするだけではなく(この場合収束が保証されない)、補助パラメータを導入してサンプリングしていく手法です。
[1402.4102] Stochastic Gradient Hamiltonian Monte Carlo

# initialization of parameter inference
latent_vars = {W_0: q_W0, W_1: q_W1, W_2: q_W2,
               b_0: q_b0, b_1: q_b1, b_2: q_b2}
SGHMC = ed.SGHMC(latent_vars=latent_vars, data={y: y_ph})
SGHMC.initialize(scale={y: float(N) / MINI_BATCH_SIZE}, step_size=0.0001, n_iter=N_ITER)

init = tf.global_variables_initializer()
init.run()
for _ in range(N_ITER):
    X_batch, Y_batch = next_batch(MINI_BATCH_SIZE)
    info_dict = SGHMC.update(feed_dict={x_ph: X_batch, y_ph: Y_batch})
    SGHMC.print_progress(info_dict)

§4. 精度評価

Edwardでの精度評価は簡単で、テストデータ用のplaceholderを用いて edward.copy メソッドを使って推定されたパラメータの平均値を使ったグラフを新たに構築した後 edward.evalutate メソッドにデータを食わせるだけです。

# criticize our model
x_test_ph = tf.placeholder(tf.float32, [N_test, X_DIM])
y_test = build_deep_Beta_distribution(x_test_ph)
dict_swap = {W_0: q_W0.mean(), W_1: q_W1.mean(), W_2: q_W2.mean(),
             b_0: q_b0.mean(), b_1: q_b1.mean(), b_2: q_b2.mean()}
y_post = ed.copy(y_test, dict_swap=dict_swap)
log_loss = ed.evaluate(metrics='mean_squared_error', data={x_test_ph: X_test, y_post: Y_test})
print('[deep Beta distribution] mean_squared_error on test data: ', log_loss)


簡単に他のモデルと比較するために、線形回帰でもモデリングしておきます:

from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error
linear = SGDRegressor(loss='squared_loss')
linear.fit(X_data, Y_data)
linear_loss = mean_squared_error(Y_test, linear.predict(X_test))
print('[linear regression] mean_squared_error on test data: ', linear_loss)


結果はこんな感じでした。

[deep Beta distribution] mean_squared_error on test data:  0.108737
[linear regression] mean_squared_error on test data:  0.132299

§5. おわりに

ものすごく雑に書いてしまいましたが、このようにEdwardを使えば自由自在に深層学習と確率モデリングを組み合わせる事ができます。

日本人のコミュニティはほとんどない状態ですので、この記事を読んで少しでもおもしろいなあと思ったら Edwardのチュートリアルをやってみてください↓

Edward – Getting Started


それでは。

*1:正直なところ普通はマルチクラス分類にするんでしょうが、分かりやすい例を出したかったのでこうしてます

*2:http://www.st.keio.ac.jp/learning/1403.html, https://ja.wikipedia.org/wiki/%E3%83%99%E3%83%BC%E3%82%BF%E5%88%86%E5%B8%83