動画紹介: 確率的ニューラルネットワークについて
昨年末VAEの勉強をしているときに、以下のようなことがわからなくて時間を喰っていた。
- なんで確率的に毎回出力の異なるモデルが必要になるのか
- reparameterization trickがなぜ必要になるのか
いろんな人に質問したり、関連サイトを読み漁ってなんとかなったのだが、 PFN得居さんのPyCON2016の講演動画ががとてもわかりやすいので、VAEみたいな確率的NNを 触る前に見ることをお勧め。
5_05 [招待講演 / Invited Talk] 確率的ニューラルネットの学習と Chainer による実装
以下、講演内容前半のメモ。
確率的NNとは何か
出力までの過程に確率分布からのサンプリングが含まれており、そのサンプリングの結果によって出力が変動するような ニューラルネットワークのこと
確率的NNをなぜ使うのか
分類問題などでは、出力として決定的な結果がほしいが、問題によっては出力として非決定的な結果がほしいことがある
例: * 実際に確率分布そのものがほしい場合 * 生成モデル(変分AEなど)
確率的NNを使う上で難しい点
途中に確率的なunitが入っている場合、何を最適化するのか? 確率的なunitが入っている場合、同じ入力でもNNを通すたびに出力が変わる。 出力をたくさん得ると、「出力の分布」が得られる。その「出力の分布の良さ」=確率分布の良さを測りたい
このようなことをする場合、各試行に対してloss値が定まるように設計することが多い。 そして出力をたくさん取って、各試行に対してlossを計算
=>その平均を取ることで「lossの期待値」が取れるため、これの最小化を最適化の目標とすることが多い。
確率分布自体は連続なものだが、 試行にかけられる時間が有限であるため、サンプル値は離散的にしか取ることができない。したがって近似が必要。
これに対処するための代表的な手法が下記2つ。
1. likelihood-ratio method (LR)
よく使われる手法。強化学習分野で「reinforce」と呼ばれていたものとほぼ一緒。 まず普通にforwardする(この過程でサンプリングが入る) => lossが高かったら今の選択が行われにくいように下げる / lossが低かったらあまり下げない。
出てきたlossが大きいか小さいか自体の判断が本来は難しいが、systematicにやるために、出てきたlossに比例する割合でその確率を下げる。
式の意味
: NNの出力。zは確率分布p(z|μ)に従う
:サンプルされたlossの値(fがloss関数)
: 平均値μの条件下でzを取る確率の勾配。この方向に進めば、zを取る確率が下がる。
これは1回だけだとzを取る確率が下がるだけだが、 f(z)が小さい方向には小さく f(z)が大きい方向には大きく 確率が下がるため、これを繰り返すことで相対的にf(z)が小さい確率が生き残って、高いままになる。
この式は真の期待値の勾配の不偏推定になっており、無限回サンプルすることで本物のgradientに一致する。
(ただし、これ自体は最小化したかったもの自体の勾配ではないのでもうひと工夫必要)
LRの問題点
勾配の大きさのブレが大きい。(サンプリングによりlossが何千倍、というオーダーでブレることもある) このため、varianceを下げるためのテクニックが必要になる。 よく使われるのが"baseline"という手法。 相対的にlossが小さい方に向けて移動させていくという動きは、定数を引いても挙動は変わらない(不偏推定性が保たれる)
うまい定数を引くことでvarianceが小さくなることが知られており、何らかの形で定数を決定し、 f(z)の代わりにf(z) - bを目標にする (bにはf(z)の平均などを用いる)
勾配の方向を一次元的に調節するだけなので、varianceの減らし方としては弱いが、手軽。
2. reparameterization trick
最近、もっと良いvarianceの少ない手法が開発された。それがreparameterization trick (ただし、正規分布のような連続な分布にしか使えない手法)
サンプリングを含むユニットを書き換えて、サンプリングを入力μとσ2と「平均0、分散1の、パラメータとは全く関係のないノイズ」の入力を掛け合わせるユニットとみなす。
このようにみなすことで偶然性(stochasticity)のない決定的なNNであるとみなせる。 => 普通にbackpropすれば良くなる。 しかも実装がかんたん。 ただし、ガウシアンでしか使えない。
離散のユニットについてはやっぱりLRを使わなければならない。
そうは言っても、予測したい問題が離散的な場合、離散的なモデルを使ったほうが良いことはあり、 学習しやすい方法を見つけ出すのは重要な問題として残っている。
(また、得居さんが離散的な値の場合について分散が大きくならない手法を研究しており、そのうち発表するとのこと。 (スライド上ではReLEGという名前がついていてた))
Chainer でのcoding方法
以下、Chainerでどうやって書くの?という話。 要点のみ。
VAEの場合の例 => ガウシアンは連続値なのでreparameterization trick
sigmoid belief network(SBN)の例 => 離散値なのでLRを使う (そのうちSBNの勉強をする機会があったら見返してみよう)
注意点: backpropのルートから外したい部分は
- chainerのChain宣言
__init__
内のsuper.__init__(..){ ... }
から外すこと - ChainerのVarianceではなく、Variance内のnumpy / cupyの値を直接操作すること