[Survey]Adversarial Autoencoders

[Adversarial Autoencoders](http://arxiv.org/pdf/1511.05644v1.pdf)


この論文の目的は、**autoencoder**の**latent code vector**が任意の分布になるような学習方法を提案することです。
**adversarial autoencoders**に関する論文としては以下３つが有名で各サイトで説明されているので、今回は細かい説明は省略したいと思います。

1. [Generative Adversarial Nets](http://arxiv.org/pdf/1406.2661v1.pdf)
2. [Adversarial Autoencoders](http://arxiv.org/pdf/1511.05644v1.pdf)
3. [Unsupervised Representation learning With Deep Convolutional Generative Adversarial Networks](http://arxiv.org/pdf/1511.06434v2.pdf)

1番の論文に関しては下記サイトがとても詳しく、**Tensorflow**による実装も掲載されています。
http://evjang.com/articles/genadv1


# Adversarial autoencoders
**Adversarial autoencoders**のブロック図と学習の流れを示したのが下記の図になります。
上段が**autoencoder**になります。**autoencoder**は入力されたデータの次元を圧縮して（**encode**)、元の次元戻す(**decode**)処理をするもので、なるべく元の入力と同じになるように重みを学習します。**autoencoder**の**latent code vector** $z$の分布$q(z)$と任意の分布$p(z)$が同じになるように**autoencoder**を学習させます。$q(z)$と$p(z)$が似ているのかどうか判断するのが**Discriminator**の役割です。もし$q(z)$と$p(z)$が同じになるように**autoencoder**を学習できれば、任意の分布$p(z)$から生成した$z$を**Decoder**に入力してあげると、$x$を入力しなくても$x$に似たデータが$z$から生成できます。

![adversarialautoencoder.png](https://qiita-image-store.s3.amazonaws.com/0/100523/b1a8bfe8-7b59-23ed-9031-dc3f0df5a9e2.png)

最適化の式は下記のようになります。
ここで$D$はDiscriminator modelで$G$はGenerator Model、$p_{data}$任意の分布、$p(z)$はGeneratorにより生成された$z$の分布です。
学習は下記のようにおこないます。
1. **true sample**(任意の分布のデータ）と**fake sample**(Generatorにより生成されたデータ）をうまく分離できるように**Discriminator**を学習します。下記の式の第一項は**true sample**のときに1と言うようになれば値が大きくなります。第二項は**fake sample**($G(z)$)のときに0と言うようになれば値が大きくなります。足したものを$D$に関して最大化すれば**true sample**と**fake sample**がうまく分離できるようになります。
2. **Generator**は**Discriminator**を騙せるように学習します。**Discriminator**をうまく騙せると$D(G(z))$が１になります。なので下記の式を$G$に関して最小化すれば**Discriminator**をうまく騙せるようになります。
![image](https://qiita-image-store.s3.amazonaws.com/0/100523/53509076-828f-0081-ad78-55e446a4991b.png)


実際に**MNIST**のデータを学習させる時は下記のような構造にします。基本的には変わりありませんが、**Discriminator**にデータが0なのか1なのか...9なのかを示した**one hot vector**を渡します。

![image](https://qiita-image-store.s3.amazonaws.com/0/100523/a9a8985b-ca75-9dcb-4779-fccf99109cae.png)

# Result
もうすでに**Adversarial autoencoders**のコードはいろいろ公開されていますが、自分でも**tensorflow**で実装して学習させてみました。(もはや何番煎じかわかりませんが...)
実装の際には下記のコードやサイトを参考にしました。
http://musyoku.github.io/2016/02/22/adversarial-autoencoder/
https://github.com/takerum/adversarial_autoencoder
https://www.reddit.com/r/MachineLearning/comments/3ybj4d/151105644_adversarial_autoencoders/?

## implementation
ネットワーク構造及び学習パラメータは下記の通りです。

・Encoder, Decoder, Discriminatorともに3 layer network
・すべてのネットワークのhidden layerのunit数は1000
・Encoderはfirst, second layerでReLUを使用、last layerは何もなし
・Decoderはfirst, second layerでReLUを使用、last layerはsigmoidを使用。
・Discriminatorは、first, second、last layerでReLU使用
・Latent code vectorは2次元
・100 epoch
・batch size 100
・Batch Normalization,weight decayは無し
・OptimizerはAdam
・Learning rateは、autoencoderとgeneratorは0.001でdiscriminatorは0.0002
・generatorだけ学習回数を2倍にしました。（autoencoder, discriminator, generatorの順で学習させるとうまく学習が収束しなかったので、autoencoder, discriminator, generator, generatorという感じにしました。ほんとうにこれでいいのか不明ですが・・・。）
・autoencoderは最初にpre-trainingしました。

実際に学習させた結果下記のようになりました。

## 10 2D Gaussian
MNISTの0〜9のデータを10個の**2D Gaussian分布**に押し込みます。
![samples.png](https://qiita-image-store.s3.amazonaws.com/0/100523/b42fceb3-6c59-0ca8-be55-c6151e75fbb7.png)

epoch毎の$q(z)$の分布をgif animationにしてみました。qittaは画像サイズが1M以上だと圧縮されてgif animationが動かなくなるので、160x160にResizeしました。小さすぎてなんだかよくわからなくなってしまいましたが・・・。
![s_q_z_f10.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/40175286-5c98-e21f-f137-1fc036196f74.gif)

decoderに$p(z)$を入力した時のepoch毎の出力結果をgif animationにしたものです。

![s_img_d00.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/91bf920f-339e-817a-6929-6d20a323dc80.gif)![s_img_d01.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/e9b3d732-67d3-c07e-21f4-c502c3271468.gif)![s_img_d02.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/075e1c91-8086-5fff-11b7-83be2161d36d.gif)
![s_img_d03.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/d898360d-761f-0278-077a-34d406c187ec.gif)![s_img_d04.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/0dbbfdd0-cb43-6c22-1651-3e80d10629e4.gif)![s_img_d05.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/29327a11-ab8d-cd16-18b2-7bbdfe14f263.gif)
![s_img_d06.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/ce456e18-f6ab-99ec-fbcf-625a584dc759.gif)![s_img_d07.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/5f84733d-303d-ff34-c844-a0b643358cfd.gif)![s_img_d08.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/9557c297-97d1-fe6e-b1ba-00d46b667836.gif)
![s_img_d09.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/7c909aef-c093-afcd-1234-c43ba703f3c5.gif)


## swiss roll
MNISTの0〜9のデータを**2D swiss roll分布**に押し込みます。
![samples.png](https://qiita-image-store.s3.amazonaws.com/0/100523/41113491-d410-0e70-dd91-6970a8fb8892.png)

epoch毎の$q(z)$の分布をgif animationにしてみました。
![s_q_z_f10.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/1745cd40-e922-41c6-5706-9f3f67f7a3c5.gif)


decoderに$p(z)$を入力した時のepoch毎の出力結果をgif animationにしたものです。
![s_img_d00.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/145fe048-2a58-830a-1723-1fa0975ad65f.gif)![s_img_d01.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/da92b498-9ac6-e816-f769-f0cf4edf18c8.gif)![s_img_d02.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/a94a41db-00bc-2a36-3f36-4f414e1b4fc9.gif)
![s_img_d03.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/1656c43a-6b09-fa10-e22a-cf979171b1f7.gif)![s_img_d04.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/4dcdd3f9-f92e-1283-1240-b897cae1ead8.gif)![s_img_d05.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/15bba21f-8c79-9698-97be-19ba64e3197d.gif)
![s_img_d06.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/441165a0-8026-597a-070a-2d760fe70f51.gif)![s_img_d07.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/0721ad16-cf60-91ba-d012-7d5a54bb92c2.gif)![s_img_d08.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/7db64d08-76e0-b9bc-e489-f56e439a1c1d.gif)
![s_img_d09.gif](https://qiita-image-store.s3.amazonaws.com/0/100523/f916be07-3d7a-ead8-e8d7-f904a0326d60.gif)

# 最後に
試行錯誤のすえ、ようやく学習が収束するようになりました。自分は色々な人の実装を参考にしながらやったので何とかなりましたが、これを最初に実装した人はすごいなと思います。実装してみた感想としては、うまく収束させるのが難しいです。ただ3つを順番に学習させればいいというわけではなさそうで、３つのバランスが重要な気がします。~~できたばかりでコードがぐちゃぐちゃのため整理したらGithubにあげようかなと思っています。~~

[code](https://github.com/supersaiakujin/adversarial_autoencoders)