xiangze's sparse blog

機械学習、ベイズ統計、コンピュータビジョンと関連する数学について

Edwardを試してみた(3)敵対的生成ネットワーク(GAN)

Edward2.0が発表され、criticismが廃止されたり、インターフェースが変わったりと大きな変化があるようですが、ここでは引き続きEdwardの例を実行してみます。

GAN

Edward(ver. 1.3)ではGANは独自のクラスを作成し、それを実行することで推定を行います。WGAN(Wasserstein GAN), BIGAN(Bidirectional GAN)はそれを継承したクラスになっています。

識別器(descriminator)、生成器(generator)として自由な形のニューラルネットを入れることができ、GANのその推定に使うOptimaizerは別々に代入する形になっています*1
GANの最適化アルゴリズムの詳細や様々な変種、応用についてはEdward TurtorialやReferenceにあげた他の人による説明を参照していただくとしてここではアルゴリズムの非常に基本的なところを説明します。

コードと計算結果

mnistを例として使ったコードは以下のようになります。
gist.github.com

現状の実装ではコンストラクタでデータx_phとxの組と識別ネットワーク、初期化(initialize)関数に生成用、識別用の最適化アルゴリズムイテレーション回数を入れるようになっています。
sess.run()から生成されたデータ(画像)が出てくるようになっていて、それを表示しています。
f:id:xiangze:20180206095807p:plain
(An Overview of GAN https://arxiv.org/abs/1711.05914 より)

WGANも同様です。
gist.github.com

特にWGANの場合はそのままではlossの収束が保障されていないらしく、学習が進むごとに出力(画像)とlossを表示していった方がいいらしく、Tensorboardを使った方が良さそうですと思ったら学習途中の結果は見れないそうです。。。
こちらではlossが突然跳ねあがってしまっています。

内部実装

https://github.com/blei-lab/edward/blob/master/edward/inferences/gan_inference.py#L134
コードを見るとlossの部分は

reg_terms_d = tf.losses.get_regularization_losses(scope="Disc")
reg_terms_all = tf.losses.get_regularization_losses() 
reg_terms = [r for r in reg_terms_all if r not in reg_terms_d] 

loss_d = tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.ones_like(d_true), logits=d_true) 
+ tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.zeros_like(d_fake), logits=d_fake)
loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.ones_like(d_fake), logits=d_fake)

loss_d = tf.reduce_mean(loss_d) + tf.reduce_sum(reg_terms_d) 
loss = tf.reduce_mean(loss) + tf.reduce_sum(reg_terms) 

d_true,d_fakeはそれぞれdiscriminatorの正解データ(x_true)を入力した時の出力、不正解データ(x_fake)を入力した時の出力です。
loss_d, lossはそれぞれ識別器、生成器の誤差に相当する値になります。loss_dの式は
f:id:xiangze:20180205000006j:plain
となっていてtf.nn.sigmoid_cross_entropy_with_logits
を使って最適化対象の関数内の E_x~p(x)[log(D(x)) ] =∫dx p(x) log(D(x))を表現しています。
 *2
loss, loss_dの勾配を計算し(https://github.com/blei-lab/edward/blob/master/edward/inferences/gan_inference.py#L148)、
初期化の最後のステップで最適化アルゴリズムにloss loss_dとその勾配を代入してtrain, train_dを作ります。
https://github.com/blei-lab/edward/blob/master/edward/inferences/gan_inference.py#L101
これをupdate関数で更新していきます。サンプルコードではloss,loss_dを同時にupdateしていますが、updateの頻度を変える方法もあるようで、GANのクラスは引数variablesに”Gen”,”Disc”を入れられることでこれに対応しています。
https://github.com/blei-lab/edward/blob/master/edward/inferences/gan_inference.py#L184

WGAN,BIGANも同様に最適化対象の関数とその勾配を定義しています。
https://github.com/blei-lab/edward/blob/master/edward/inferences/wgan_inference.py#L95

*1:この辺りもEdward2.0で変更される可能性が高いです。

*2:NIPS 2016 Tutorial: Generative Adversarial Networksの式(8)にも同様の記述があります。