PRML 10章 分解による近似

# やりたいこと
　変分推論は真の事後分布を分解によって近似します。今回はPRML下巻10.1.2を参考に実際にパラメータの事後分布のパラメータを計算、プロットをJuliaで書いてみます。
 　ソースコードは[須山さんのブログ](https://machine-learning.hatenablog.com/entry/2016/01/31/172500)を参考にしました。

# パラメータの更新式
詳しい計算は、須山さんのブログがわかりやすいのでそちらを参考にしてください。ここではパラメータの更新の式だけ書いておきます。
今回は二変数ガウス分布を以下の１変数ガウス分布で表せると仮定します。

```math
q(\boldsymbol{z}) = q_{1}(z_{1})q_{2}(z_{2})
```
$q(\boldsymbol{z})$に対して、求めたい分布の因子以外の因子について期待値を取り、

```math
\log q_{1}(z_{1}) = E_{z_{2}}[ \log q(\boldsymbol{z}) ] + const \\
\log q_{2}(z_{2}) = E_{z_{1}}[ \log q(\boldsymbol{z}) ] + const
```
計算を進めていくとそれぞれ$z_{1}, z_{2}$についての二次の式となるので、分布$q_{1}(z_{1}), q_{2}(z_{2})$はガウス分布となります。

```math
q_{1}(z_{1}) = N( z_{1} | m_{1}, \lambda_{11}^{-1} ) \\
q_{2}(z_{2}) = N( z_{2} | m_{2}, \lambda_{22}^{-1} ) \\
```

```math
m_{1} = \mu_{1} - \lambda_{11}^{-1}\lambda_{12}( E_{z_{2}}[z_{2}] - \mu_{2} ) \\
m_{2} = \mu_{2} - \lambda_{22}^{-1}\lambda_{21}( E_{z_{1}}[z_{1}] - \mu_{1} ) \\
```
ここで、$E_{z_{1}}[z_{1}] = m_{1}, E_{z_{2}}[z_{2}] = m_{2}$であるので、パラメータの更新式は相互に依存し合います。これは連立方程式が解け、直ちに$m_{1} = \mu_{1}, m_{2} = \mu_{2}$となりますが、今回はパラメータの収束の様子を見るので、パラメータは交互に更新させます。うまく更新ができると、平均はきちんと近似できることになります。
各分布は独立であると仮定しているので、共分散行列の対角成分は0になります。また、非対角成分は分解した分布の分散に等しくなります。つまり、分散はうまく表せないことが予想されます。
では実際に見てみましょう。

# ソースコード

```julia:gauss_decom.jl
using Distributions
using Plots
using StatsPlots
using LinearAlgebra

function update_param!( m, μ, λ )
    m[1] = μ[1] - inv(λ[1,1])*λ[1,2]*(m[2] - μ[2])
    m[2] = μ[2] - inv(λ[2,2])*λ[2,1]*(m[1] - μ[1])
end

function main()
    # truth dist
    true_μ = [0.0, 0.0]
    
    # 回転行列は直行行列でもある
    theta = 2.0*pi/12 
    A = reshape([cos(theta), -sin(theta),
                 sin(theta), cos(theta)], 2, 2)
    true_Λ = inv(A * inv(reshape([1,0,0,10], 2, 2)) * A')
    
    # init
    # プロットがきれいになりそうな値で初期化した
    m = [3.0,7.0]
    Λ = [true_Λ[1,1] 0;0 true_Λ[2,2]]

   anim = @animate for i in 1:20
        update_param!( m, true_μ, true_Λ )
        covellipse( true_μ, inv(true_Λ), xlim=[-2, 2], ylim=[-2, 2], title= "n=$i", label="truth_dist" )
        plot!( [true_μ[1]], [true_μ[2]], markershape=:x, markersize=5, label=false )
        covellipse!( m, inv(Λ), color="blue", coloralpha=0.3,label="approximate_dist" )
        
    end
    gif( anim, fps=5 )
end
main()
```
![tmp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1272355/6a8cfde5-4159-de8f-a034-78a779ec947b.gif)

まずは平均についてです。パラメータを更新すると徐々に真の分布の平均に近づくことがわかります。分散は非対角成分が0なので変数間の関係をうまく表せていないことがわかります。

# 勉強するにあたってハマったとことか
- 近似した事後分布の共分散行列の非対角成分は、分布同士が独立という仮定を置いているので$\lambda_{12}=\lambda_{21}=0$です。でも、パラメータの更新に使う共分散行列の非対角成分は、0ではないので注意しましょう。(私はプログラムを書くときにごっちゃになったので覚書として）
- 正定値行列を作るのに、今までは固有値が全て正になるように行列の成分をいちいち調整していたのですが、回転行列という直行行列を使うことで、簡単に正定値行列を作れることを須山さんのソースコードで知りました。
- juliaでgifを作るのやり方は[ここ](https://qiita.com/Lirimy/items/8976bb4dcf5febad178e)を参考にしました。

# PCAでも同じことができる？
データにPCA(主成分分析)を適用し個々のデータの相関をなくしたうえで、パラメータを推定すれば同じ結果が出るのではないか、と上司にアドバイスを頂いたので次はそれもやってみたいと思います。

# 参考文献
- [パターン認識と機械学習 下](https://www.amazon.co.jp/%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E8%AA%8D%E8%AD%98%E3%81%A8%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92-%E4%B8%8B-%E3%83%99%E3%82%A4%E3%82%BA%E7%90%86%E8%AB%96%E3%81%AB%E3%82%88%E3%82%8B%E7%B5%B1%E8%A8%88%E7%9A%84%E4%BA%88%E6%B8%AC-C-M-%E3%83%93%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97/dp/4621061240)
- [作って遊ぶ機械学習。](https://machine-learning.hatenablog.com/entry/2016/01/31/172500)
- [意味がわかる主成分分析 Qiita記事](https://qiita.com/NoriakiOshita/items/460247bb57c22973a5f0)