GAN's Generator side technique to avoid decreasing output variation

# 概要

DCGAN(Deep Convolutional Adversarial Network)においてGeneratorの出力のバリエーションが失われていく現象に対してGenerator側で対処するテクニックについて記述する。

この現象について自分はGeneretorの学習ステップのバックプロパゲーションでミニバッチの各サンプルに共通する特徴に勾配が寄ってしまってGeneratorの高次特徴の少数の箇所から出力を生成するようになってしまうか、あるいはどのような高次特徴でも似たような出力をするようになってしまうと考えている。たぶんいろんな論文でもっと厳密な分析がされている。

この現象についての対処として有名なものとしてMinibatch Discriminationがある。

- [Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498)
- [Improved Techniques for Training GANs [arXiv:1606.03498]](http://musyoku.github.io/2016/12/23/Improved-Techniques-for-Training-GANs/)

これはDiscriminatorにおいてミニバッチ内の各サンプルに他のサンプルとの距離的なものを埋め込むというテクニックである。事情によりこのテクニックを導入するのが難しかったのと「レイヤー数やチャンネル数を増やすだけではないもっと賢いジェネレータを作れないか」というモチベーションによりこの記事の内容に至った。

# 注意

- まだまともに検証できていない。「なんか効いてるっぽい？」程度の状況。
- 数学的な背景は示せない。
- Generatorの出力層近くまでランダムサンプルzのランダムネスを波及させるというコンセプトだが原理的には「ミニバッチ内の共通の特徴に勾配が寄る」という現象は回避できない気がする。

# 背景にある目標

DCGANでヌード画像生成器を作ろうというのがそもそものモチベーション。

某手段により収集したヌード画像について以下の条件で学習画像を構成する。

- 縦長である
- 彩度の平均と分散が一定以上（モノクロやモノクロに近いものを除外）
- 頭部から腰回りが写っていて頭部が画像上部にある（倒立や過度に屈曲などしていない）

現在20000枚くらい集まっているが。

試行錯誤でいろいろなネットワークを作って試しているが生成系でヌード画像とみなせる出力を得ることには生成していない。原因はサンプルの多様性（ポーズが多様、背景領域の割合が大きく分布の重心が定まらないなど）だと考えている。

失敗の類型に「すべてのミニバッチが同じ画像を出力し始める」という今回のテーマがある。

# Channel Selector

出力層近くのコンボリューションに入力であるランダムサンプルの影響を与えようというのがコンセプト。コンボリューションのどのサンプルを重視するかの選択をランダムサンプルの全結合から行う。

![dcgan_image_generator_with_channel_selector.png](https://qiita-image-store.s3.amazonaws.com/0/25288/b7cba2d6-479e-6ef8-d6de-4b7e3e98aec5.png)


アップサンプルはなんでも良いと思うが自分はresize+conv2dを使っている。全結合の活性化関数は`[0.,1.]`が欲しかったのでsigmoid。各層にはBatch normalizeを入れている。これで出力層近くにもzのランダムネスが強く影響して出力の多様性が確保される。と思う、多分。

# 実装

Githubに置きました。 [gan_with_channel_selector](https://github.com/YusukeSuzuki/gan_with_channel_selector)

TensorFlowを使っている。

- Lossはleast square loss
- 出力層の活性化関数はG/Dともにsigmoid
- Upsampleはresize bilinear + conv2d
- OptimizerはD/GともにRMSProp、学習率1e-4
- 学習の戦略
    - if D's real sample correct rate < 0.5 or D's fake sample correct rate < 0.5 then train D and G
    - else train generator

# 実験

- CelebAデータセットを使用
  - 切り出しは178x218 -(crop center)-> 140x140 -(resize)-> 64x64
  - 各種論文ではもっと顔中央に限定して切り出している。自分の切り出しだと背景領域が学習しづらい分布になっているのでGANの成功がより難しい。
- GTX 1080ti x2
- Minibatch size per gpu is 32 
- 400k iteration
- データオーグメンテーションとしてランダムに左右反転をしている

# 実験結果

出力画像。変な顔がたくさん出てる・・・。ただし似たような顔はないように見える。

![Screenshot from 2017-10-13 15-01-07.png](https://qiita-image-store.s3.amazonaws.com/0/25288/79a65267-d7a9-7faf-e26d-a02da1efd393.png)

DのロスとGのロス。

![Screenshot from 2017-10-13 14-59-32.png](https://qiita-image-store.s3.amazonaws.com/0/25288/083f5e72-ac2e-27a6-84d8-3562ba4bc0cc.png)

![Screenshot from 2017-10-13 14-59-52.png](https://qiita-image-store.s3.amazonaws.com/0/25288/7c1d7d37-f914-bb13-6b93-92ccecab6dcc.png)

リアルサンプルについてDがリアルと判定した率

![Screenshot from 2017-10-13 15-00-16.png](https://qiita-image-store.s3.amazonaws.com/0/25288/2b09ba2b-64da-95d1-8e24-4b699e0f8ac0.png)

フェイク画像についてDがリアルと判定した率

![Screenshot from 2017-10-13 15-00-33.png](https://qiita-image-store.s3.amazonaws.com/0/25288/6d334f56-947b-2237-a453-c4230af20367.png)

Dの学習ステップが実行された回数の推移

![Screenshot from 2017-10-13 15-00-51.png](https://qiita-image-store.s3.amazonaws.com/0/25288/0e7a7e5f-1850-3dfd-9c92-bc9369905f6a.png)

# 考察

- channel selectorを入れた場合、ミニバッチのサンプル出力内に似たような画像が出る頻度が減っている。
- ここでは比較を示せていないがchannel selectorを入れていないGANではミニバッチのサンプル出力内に似たような画像が出ることが多かった。
- 背景の再現、背景に近い部分（髪型など）はなんか乱れてる。これはchannel selectorを入れない場合にも見られる。
- 出力画像の質が悪い（変な顔が多い）。conv2dの後に実際に使えるチャネルを絞っているのでそれが影響しているかもしれない。あるいは単に学習が足りていないからかも。
- 400k iterationの間にDの学習ステップが実行されたのが18k程度
    - つまり18kイテレーション程度しか学習していない分類器が見ている画像が出力になっていると考えられる
    - 画像分類器で18kイテレーションはいかにも少ない
    - なんでこんなに少ないのかというとGの学習が難しいから
    - Gのパラメータが増えてDに追いつくような学習に時間がかかっている
    - 学習率を調整すれば良いのかも知れないがドツボにハマりそう（というか確実にドツボにハマる）のでちょっと躊躇する

本格的には各出力画像が使用するチャネルがchannel selectorによってどれだけ制限されているかとかそういうのを検証すべき。

- channel selectorの全結合は2層にしないの？
    - まず計算が遅くなる
    - あとランダムサンプルと出力層の距離が開いて多様性の減少が再発する懸念がある
    - なので今回は1層にした
- conv2dのチャネルを増やしたら？
    - やってみたい
- conv2dのレイヤー数を増やしたら？
    - DCGANにおいては一般的にはレイヤー数を増やすと学習が難しくなる
    - consensus optimizationがこれを解消するとのことで実装できるなら試してみたい
- 正則化入れたほうが早くない？
    - ちょっと考えてる
- やってみたけどGPU使用率悪くない？
    - 各GPUでの勾配の平均をCPUで取ってる。TensorFlowのチュートリアルの手法だけれど全結合が多くなるとここが重くなる。GPUでやるべきなんだけど実験開始しちゃったので放置してた。

# 今後

真面目に検証するよりいろんな手法を試したいので（計算リソースには限りがある）どういう方向にいくかは未定。

ヌード画像のGANは結構難しいのでchannel selectorだけですべてが解決するわけではない。試行錯誤中に見つけたテクニックを整理選択しながら論文などにあるいろんな手法を試していく。GANにこだわるつもりもない。