ベイズ推定を用いた変化点検出と変化点数の考察

# はじめに
[「Pythonで体験するベイズ推論」](http://www.morikita.co.jp/books/book/3155)を読んで行くうちに疑問があり、コーディングしたものの備忘録です

良い書籍なので興味のある方は是非ご購読を

# 変化点検出とは

「ある時点でCMを打った結果、パーセプションがあがり売り上げが上がった」
「ある時点で部品の故障があった結果、不良品率が上がった」

など時系列データでは外的・内的要因によってトレンドが変化することがあります。
変化点検出ではそういった変化が生じた「ある時点」を検出する仕組みとなります

まずは変化時点を知り、そこから原因を追求していったりしますね

# ベイズを用いた変化点検出（実際＝予想）

それではPyMCを使って、ベイズを用いた変化点検出をやってみましょう

まず疑似データを用意します
ここではポワソン分布に従って、2回変化するダミーデータを作ります

```py3
a = np.random.poisson(lam=10, size=100) 
b = np.random.poisson(lam=5, size=50) 
c = np.random.poisson(lam=15, size=120) 

data = np.hstack((a,b,c))

n = len(data)
plt.bar(np.arange(n), data, color='r')
plt.xlim(0,n)

plt.show()
```

<img width="517" alt="スクリーンショット 2018-01-23 14.48.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/170cb0a8-6c9e-c1c8-4c5c-a3fd8bf1fc01.png">

さあデータはできたので推定して見ましょう
まずは変化点が実データと同じ２つだと仮定したモデルを組みます

データの事前分布確率もポワソン分布（パラメータλは指数分布）
変化点τは離散一様分布としましょう

```py3
alpha = 1.0 / data.mean()

lambda_1 = pm.Exponential("lambda_1", alpha)
lambda_2 = pm.Exponential("lambda_2", alpha)
lambda_3 = pm.Exponential("lambda_3", alpha)

tau = pm.DiscreteUniform("tau", lower=0, upper=n-1)
tau_2 = pm.DiscreteUniform("tau_2", lower=0, upper=n)

@pm.deterministic
def lambda_(tau=tau, tau_2=tau_2, lambda_1=lambda_1, lambda_2=lambda_2, lambda_3=lambda_3):
    out = np.zeros(n)
    out[:tau] = lambda_1
    out[tau:tau_2] = lambda_2
    out[tau_2:] = lambda_3
    return out

observation = pm.Poisson("obs", lambda_, value=data, observed=True)
model = pm.Model([observation, lambda_1, lambda_2, lambda_3, tau, tau_2])

mcmc = pm.MCMC(model)
mcmc.sample(40000, 10000)
```

さあこれでベイズ推定は終わりました
結果はどうなったでしょう？

まずはポワソン分布のパラメータλの事後確率分布を見て見ます

```py3
lambda_1_samples = mcmc.trace('lambda_1')[:]
lambda_2_samples = mcmc.trace('lambda_2')[:]
lambda_3_samples = mcmc.trace('lambda_3')[:]
tau_samples = mcmc.trace('tau')[:]
tau_2_samples = mcmc.trace('tau_2')[:]

plt.hist(lambda_1_samples, histtype='stepfilled', normed=True, label="posteriro of $\lambda_1$")
plt.hist(lambda_2_samples, histtype='stepfilled', normed=True, label="posteriro of $\lambda_2$")
plt.hist(lambda_3_samples, histtype='stepfilled', normed=True, label="posteriro of $\lambda_3$")
plt.xlim([0,18])
plt.ylim([0,1.6])
plt.show()
```

<img width="534" alt="スクリーンショット 2018-01-23 14.53.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/cb9d0f77-ba51-fc41-3985-ccae0d10c076.png">

綺麗ですね！
実データともあっています

ではτはというと、、、

```py3
plt.hist(tau_samples, label="posteriro of $\tau$")
plt.hist(tau_2_samples, label="posteriro of $\tau_2$")
plt.show()
```

<img width="577" alt="スクリーンショット 2018-01-23 14.53.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/da83338d-c6b9-5e3c-3119-74d8b29066fc.png">

うん、こちらも想定とほぼ同じですで推測はうまく言っているようです

# ベイズを用いた変化点検出（実際＜＞予想）

先ほどの結果は潜在因子を見事に推定していて素晴らしい結果でした
これを使えばもう完璧です！！！

、、、とはまあ現実行かないものです
実際はこんなにはっきり変化点があることは多くなく、もっと混沌としています
また変換点もいくつかあるかなんてぱっと見ではわからないことも多いです

では変化点が想定と異なった場合はどうなるのでしょうか？

まずは変化点が本来より少ない場合（１件）を見てましょう

```py3
alpha = 1.0 / data.mean()

lambda_1 = pm.Exponential("lambda_1", alpha)
lambda_2 = pm.Exponential("lambda_2", alpha)

tau = pm.DiscreteUniform("tau", lower=0, upper=n)

@pm.deterministic
def lambda_(tau=tau, lambda_1=lambda_1, lambda_2=lambda_2):
    out = np.zeros(n)
    out[:tau] = lambda_1
    out[tau:] = lambda_2
    return out

observation = pm.Poisson("obs", lambda_, value=data, observed=True)
model = pm.Model([observation, lambda_1, lambda_2, tau])
```

こんな感じですね
結果はというと、、、

<img width="551" alt="スクリーンショット 2018-01-23 14.57.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/c9ef715b-2c35-6901-5435-c47945769d9a.png">


<img width="552" alt="スクリーンショット 2018-01-23 14.58.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/79fb4d03-95f0-12ca-9e54-a544e39dba80.png">

確かに変化点が少ないので正しくは推定できていないものの
でも与えられた範囲内では問題なさそうです

シュミレーション値と推測値を合わせても、一つの変換点は間違っていないことがわかります

<img width="524" alt="スクリーンショット 2018-01-23 14.58.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/c37c6957-750f-1991-5647-4b9ba0f0e305.png">

では変化点が多い場合はどうでしょう？

```py3
alpha = 1.0 / data.mean()

lambda_1 = pm.Exponential("lambda_1", alpha)
lambda_2 = pm.Exponential("lambda_2", alpha)
lambda_3 = pm.Exponential("lambda_3", alpha)
lambda_4 = pm.Exponential("lambda_4", alpha)

tau = pm.DiscreteUniform("tau", lower=0, upper=n-1)
tau_2 = pm.DiscreteUniform("tau_2", lower=0, upper=n)
tau_3 = pm.DiscreteUniform("tau_3", lower=0, upper=n)

@pm.deterministic
def lambda_(tau=tau, tau_2=tau_2, tau_3=tau_3,  lambda_1=lambda_1, lambda_2=lambda_2, lambda_3=lambda_3, lambda_4=lambda_4):
    out = np.zeros(n)
    out[:tau] = lambda_1
    out[tau:tau_2] = lambda_2
    out[tau_2:tau_3] = lambda_3
    out[tau_3:] = lambda_4
    return out

observation = pm.Poisson("obs", lambda_, value=data, observed=True)
model = pm.Model([observation, lambda_1, lambda_2, lambda_3, lambda_4, tau, tau_2, tau_3])
```

さてさて結果です

<img width="536" alt="スクリーンショット 2018-01-23 15.01.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/c41b635b-b904-12d1-2928-918ad808f7ec.png">

<img width="550" alt="スクリーンショット 2018-01-23 15.01.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/62468/2025c29c-172a-90ef-1134-40b2142ea3b4.png">

いきなりのカオスですね、、、
変化点が多い場合はこれだけはっきりしたものでも推測がおかしくなることがわかります

# まとめ

変化点の仮説が異なった場合の動きを簡単に検証して見ました
「じゃあ変化点はいくつがいいの？」という話がありますが、値をレトロスペクティブに評価するなら明らかに重なったような場合は点を減らした方がいいのかもしれません（まあモデルが間違っている場合の方が多いでしょうが、、、）

もしくは変化点の数も推定するモデルを描いてもいいかもですね