Edwardを使ってベイズ推定のイメージをつかむ実験

# 概要
ベイズ推定を学習中です。
そのために確率的プログラミングライブラリ**Edward**を利用して、ベイズ推定を用いた機械学習を行いました。
私自身、未熟であり学習中の事柄ですので、つっこみどころありましたらコメントいただけると幸いです。

# ベイズ推定
よく使われる機械学習は、データを学習後にパラメータが **ひとつ** に定まる。
一方、ベイズ推定を用いた機械学習は、データを学習後にパラメータが **確率的** になる。

例えば回帰分析を行った場合、式のパラメータAおよびBが確率的に表現できる。
よって予測したyも確率で表現できる。

``` math
y = AX + B
```

## 利点
パラメータを確率的に表現することができるので、初期パラメータを任意の確率分布で設定できる。
**学習前にも事前の経験則をモデルに適用することができる**
つまり極論、データがなくても予測ができる

個人的には下記のような利用イメージ

1. フランチャイズチェーンの会社があり、購買の需要予測をしている。
1. 既存店舗はすでに長いこと運営しており、多くのデータが存在している
1. 新しく店舗をたてることになった。新規店舗でも需要予測をしたい
1. 新規店舗でデータが増えるまでは、既存店舗で得た分布を初期パラメータとして利用する

また、その仕組みから学習後の予測モデルから、さらに再学習が容易に可能。

# Edwardについて
[Edward](http://edwardlib.org/)
Pythonのライブラリで、TensorFlowを利用した**確率的プログラミング**ができる。
確率的プログラミングとは何かは理解できていないので割愛する。上記で記載したベイズ推定による回帰分析ができると話題。

pipでインストールできる。

``` cmd
$pip install edward
```
※この記事では他にpandas、numpy、tensorflowをつかう

# Edwardを用いた回帰分析

定番の[irisデータセット](https://archive.ics.uci.edu/ml/datasets/iris)を利用して回帰分析を行う

花の"がくの大きさ"から"花弁の大きさ"を予測する。

* SepalLength(がくの大きさ)
* PetalLength(花弁の大きさ)

公式サイトの下記ページを参考にする。
[Edward-Supervised Learning (Regression)](http://edwardlib.org/tutorials/supervised-regression)
[Edward-Iterative estimators (“bayes filters”) in Edward?](https://discourse.edwardlib.org/t/iterative-estimators-bayes-filters-in-edward/104)

epoch毎にデータセットからランダムにデータを10個取り出して、学習を行う。
epochが進むごとに、パラメータが確率的に求まる->複数の予測モデルができあがるので、そのモデルをグラフに描画する。

* 初期段階
![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/53351/0c4807f7-e3d9-b2c8-c817-2b94e62c08fd.png)

* 中期段階
![ダウンロード (1).png](https://qiita-image-store.s3.amazonaws.com/0/53351/416d5cb4-d5e6-8c2d-0c13-518c096361a9.png)

* 後期段階
![ダウンロード (2).png](https://qiita-image-store.s3.amazonaws.com/0/53351/3bee178a-5d05-f254-013b-c015ebde9aef.png)

* 学習完了時
![ダウンロード (7).png](https://qiita-image-store.s3.amazonaws.com/0/53351/04e67649-d45b-e5e4-cfd8-6a03625a404e.png)


1. 予測モデルが徐々にデータにフィットしていくことが確認できる
2. 学習完了時にも複数の予測モデルがある
3. 上のデータの固まりだけではなく、下のデータの固まりにひっぱられる予測モデルがある（こういうところが多分うれしい）


上記の実験のコードを下記に記載する。

``` edward_iris.ipynb
import tensorflow as tf
import edward as ed
import numpy as np
import pandas as pd
from edward.models import Normal
import matplotlib.pyplot  as plt

def visualise(X_data, y_data, w, b, n_samples=10):
    w_samples = w.sample(n_samples)[:, 0].eval()
    b_samples = b.sample(n_samples).eval()
    plt.xlim(0, 10)
    plt.ylim(0, 10)
    plt.scatter(X_data[:, 0], y_data)
    inputs = np.linspace(-8, 8, num=400)
    for ns in range(n_samples):
        output = inputs * w_samples[ns] + b_samples[ns]
        plt.plot(inputs, output)
    plt.show()

# irisデータを利用する
df_iris = pd.read_csv("iris.csv")
# 説明変数
feature = ["SepalLength"]
# 目的変数
target = "PetalLength"

# バッチの数
N = 10
# 説明変数の次元数
D =  len(feature)

# バッチデータを生成する
def next_batch(df, M):
    # バッチサイズでサンプリングする
    sample = df.sample(n=M)
    x = sample[feature].values
    y = sample[target].values
    return x, y

# モデルを作成する
# Xの箱を作成
X = tf.placeholder(tf.float32, [None, D])
# loc: mu 平均, scale: sigma　分散
# mu=0, sigma:1
# 係数 weight
w = Normal(loc=tf.zeros(D), scale=tf.ones(D))
# 切片 bias
b = Normal(loc=tf.zeros(1), scale=tf.ones(1))
#  一次関数を作成する
y = Normal(loc=ed.dot(X, w) + b, scale=1.0)


#　初期データ
X_batch, y_batch = next_batch(df_iris, N)

# 事前分布/事後分布
# **パラメータが確率的=>予測するための式がいっぱいあると考える**
# 学習することで、複数の式が一つの式に収束していく
# mu = 正規分布, sigma = 0以上(正規分布からsoftplus関数を利用して0以上に変換)
# 係数 weight
qw = Normal(
    loc=tf.Variable(tf.random_normal([D])),
    scale=tf.nn.softplus(tf.Variable(tf.random_normal([D])))
)
# 切片 bias
qb = Normal(
    loc=tf.Variable(tf.random_normal([1])),
    scale=tf.nn.softplus(tf.Variable(tf.random_normal([1])))
)

# 推測を初期化
inference = ed.KLqp({w: qw, b: qb}, data={X: X_batch, y: y_batch})
inference.initialize()
tf.global_variables_initializer().run()

for _ in range(inference.n_iter):
    if(_ % (int)((_ / 10) + 1) == 0):
        # グラフに描く
        visualise(df_iris[feature].values, df_iris[target].values, qw, qb)
    # 新しいデータを取り出す
    X_batch, y_batch = next_batch(df_iris, N)
    info_dict = inference.update({X: X_batch, y: y_batch})
    inference.print_progress(info_dict)
```

以上。