GAN と親しもう！（機械学習名古屋第１7回勉強会）

# GANとは

---
# GAN
GANは「generative adversarial networks」の略

日本語だと敵対的生成ネットワークという意味（adversarial=敵対的）

その一種のDCGANでブレーク（以下DCGANベースで説明）

---
# 生成モデル
出力が画像等になるモデル
DCGANでは、乱数を入力すると画像が生成される

![generative_model.png](https://qiita-image-store.s3.amazonaws.com/0/51820/d4bbcf40-ceb4-710c-99af-006c4a91b85e.png)


---
# 敵対的とは
生成するネットワークと品評するネットワークを切磋琢磨させながら訓練

* 生成するネットワーク・・・Generator
* 品評するネットワーク・・・Discriminator

と呼ばれる（モデルによっては別の名も）

Discriminatorは、画像がデータセットに含まれる画像なのか生成した画像かを分類

---
# DCGANの訓練の登場人物

* 訓練データセット・・・画像データセット  
* 生成される画像・・・訓練データセットにありそうな画像
* Generatorの入力・・・ランダムなベクトル
* Generator・・・ランダムなベクトルを入力して画像と同じ形のテンソルを出力するネットワーク
* Discriminator・・・画像を入力して0から1の値を出力するネットワーク  
（例えば、1に近いと本物、0に近いと生成したものの意味）

---
# DCGANの訓練の雰囲気
GeneratorとDiscriminatorを交互に訓練

* Discriminatorは、生成された画像かデータセットの画像かを識別するように訓練
* Generatorは、Discriminatorがデータセットの画像と誤認するように訓練

---
# DCAGNの概要図
![DCGAN_abstract.png](https://qiita-image-store.s3.amazonaws.com/0/51820/c9541674-594d-7a31-712a-f763403287eb.png)

---
# 具体的な実装方法
$x$: データセットの画像
$z$: ランダムなベクトル

```math
\begin{align}
L_G(z) &= -\log D(G(z)) \\
L_D(x,z) &= -\log D(x)-\log(1-D(G(x)))
\end{align}
```
$L_G$はGeneratorの損失、$L_D$はDiscriminatorの損失

ごちゃごちゃしているけど、通常の分類問題であるようなcross entropyを使った損失関数
$L_G$はGenenratorが生成した画像を、Dが本物と分類するための損失関数
$L_D$はGenenratorが生成した画像を、Dが偽物と分類し、データセットの画像を本物と分類するための損失関数

---
# 実例

---
# 生成例
![result_gan.png](https://qiita-image-store.s3.amazonaws.com/0/51820/5d58469f-4e7d-6a22-4c20-6c143f6528b2.png)

入力の値を少しずつかえると連続的に顔が変わったり

---
# デモ
tensorflow-hubに訓練済みのモデルがあるのでそれを試しましょう
https://www.tensorflow.org/hub/modules/google/progan-128/1

* モデル・・・progressiveGANというタイプのモデル
* データ・・・人の顔のデータセットCelebA
* 入力・・・512次元のベクトル
* 生成・・・128x128の画像（0〜1の値に正規化されている）

---
# 必要なもののインストール
```
pip install tensorflow tensorflow-hub pillow
```
(pillowは生成した画像を保存する用)

今回の環境は、
Python 3.6.6
tensorflow 1.10.1
tensorflow-hub 0.1.1
pillow 5.2.0

---
# サンプルコード
```python
import tensorflow as tf
import tensorflow_hub as hub
from PIL import Image
import numpy as np


# モデルのダウンロード(時間がかかる)
gan = hub.Module("https://tfhub.dev/google/progan-128/1")


# 入力の乱数(placeholderなどを使ってカスタマイズも可)
z_values = tf.random_normal(shape=[10, 512])
images = gan(z_values)


with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    # 画像生成
    out = sess.run(images)

    # 保存
    for i, image in enumerate(out):
        Image.fromarray(np.uint8(image * 255)).save(f"result_{i}.png")  # 値を0〜255にして保存
```

---
# GANの訓練の例
訓練には例えば

* [tensorflow/modelsのGANの項目](https://github.com/tensorflow/models/tree/master/research/gan)
* [pfnet-research/chainer-gan-lib](https://github.com/pfnet-research/chainer-gan-lib)

---
# GANの発展

---
# GANの訓練は難しい
理屈は、ゆっくり考えると難しくない
けど、実際に訓練させると簡単じゃない

* Discriminatorが厳しくなりすぎて、Generatorがどんな画像を生成したらいいかわからなくなって、ノイズのような画像を生成
* 同じような関数を片方では増やそうとして、もう一方では減らそうとして、これが0に近づけばいいって指標がない
* いつ訓練を終わらせたらいいか分からない
* 分類問題みたいに正解がきっちりしているわけではない

2017年を中心に色んなGANの亜種が盛んに研究された

---
# とりあえずGANすることになったら
とりあえずGANするならこれにしとけってものが

* WGAN
* WGAN-gp（WGANを少し変更したもの）

---
# WGAN
* データセットは画像の確率と解釈できる
* 乱数から生成される画像の確率分布も考えている対象

この2つの確率が近いようにするのが生成モデルのテーマ

近いかどうかを表現するためにWasserstein距離というものを使う

---
# WGANのありがたみ
* Wasserstein距離が近似的に計算可能
* 訓練の進捗をWasserstein距離（の近似値）だけで判断可能  
これが小さくなっていくとOK

---
# 応用

---
# 応用パターン
単純に画像などを生成するだけではない

* cycleGAN  
画像の傾向を交換するようなことを行う
* [Everybody Dance Now](https://carolineec.github.io/everybody_dance_now/)  
姿勢（関節などの位置）から、その姿勢をした姿をGANで生成。
* 異常検知
* 超解像

どんな入力でどんな意味合いのものを生成するかがポイント

---
# 他にも
画像以外にも

* 文書  
SeqGAN
* 音楽  
WaveGAN

など

機械学習名古屋研究会でもGANを扱った論文を紹介する人が結構いたり

---
# 以上、ご静聴ありがとうございました