TensorFlowによるDCGANでロゴ画像を生成してみた

最近TensorFlowのv1.0がリリースされたようなのですが、まだ確認しておらず少し古いバージョンでの実行となっています。なるべく早めにv1.0に更新できればという状態です。


今回はDCGANのモデルをTensorFlowで実装し、ロゴ画像を生成してみました。
元論文は[Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks](http://arxiv.org/abs/1511.06434)のようです。

DCGANについては既にやっている人も多く、学習のロジックや詳細については
[Chainerで顔イラストの自動生成](http://qiita.com/mattya/items/e5bfe5e04b9d2f0bbd47)
[Chainerを使ってコンピュータにイラストを描かせる](http://qiita.com/rezoolab/items/5cc96b6d31153e0c86bc)
[TensorFlowによるDCGANでアイドルの顔画像生成](http://memo.sugyan.com/entry/20160516/1463359395)
あたりがわかりやすく説明されており参考になります。


# 学習環境
TensorFlow 0.11.0rc2
Ubuntu 16.04
GTX1080


# 画像データ
webサービスや企業などのロゴ画像を643枚収集

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/e64c07af-950c-9655-8041-244ca373f400.png)

# 実装・学習
基本的にコードは[https://github.com/carpedm20/DCGAN-tensorflow](https://github.com/carpedm20/DCGAN-tensorflow)をベースにしてます。
今回の[コード(github)](https://github.com/sanshirookazaki/logogan2)
GeneratorとDiscriminatorのスケールなどパラメータを少しいじってあります。

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/f2af45cc-3c58-ec24-f187-8c30428b5403.png)![image](https://qiita-image-store.s3.amazonaws.com/0/115249/a5f708ee-55f0-2b3f-ce08-bcf4976dd19c.png)

tf.nn.batch_normalizationをGeneratorとDiscriminatorに入れてあります。

```python:model.py
...
def generator(self, z, y=None):
        if not self.y_dim:
            # project `z` and reshape
            self.z_, self.h0_w, self.h0_b = linear(z, self.gf_dim*8*4*4, 'g_h0_lin', with_w=True)     #linear => ops.pyメソッド
                                                                                                    # z_= in*random + b , h0w = random , h0b = bias(random) 
            self.h0 = tf.reshape(self.z_, [-1, 4, 4, self.gf_dim * 8]) 
            mean0, var0 = tf.nn.moments(self.h0, [0,1,2])                             #[-1,4,4,64*8]
            h0 = tf.nn.relu(tf.nn.batch_normalization(self.h0, mean0, var0, None , None,1e-5,name='bn0'))     # relu(batch_norm(h0)　name='g_bn0')

            self.h1, self.h1_w, self.h1_b = deconv2d(h0,                                #deconv2d => ops.pyメソッド                        #                        
                [self.batch_size, 8, 8, self.gf_dim*4], name='g_h1', with_w=True)       # h1 = deconv[64,8,8,64*8] ,h1w = w(rand) , hb1 = b(rand)
            mean1, var1 = tf.nn.moments(self.h1, [0,1,2])
            h1 = tf.nn.relu(tf.nn.batch_normalization(self.h1, mean1, var1, None , None,1e-5,name='bn1'))       # relu(batch_norm(h1)　name='g_bn1') 

            h2, self.h2_w, self.h2_b = deconv2d(h1,                                     # 以下deconvルーチン
                [self.batch_size, 16, 16, self.gf_dim*2], name='g_h2', with_w=True)     #
            mean2, var2 = tf.nn.moments(h2, [0,1,2])
            h2 = tf.nn.relu(tf.nn.batch_normalization(h2, mean2, var2, None , None,1e-5,name='bn2'))                         

            h3, self.h3_w, self.h3_b = deconv2d(h2,
                [self.batch_size, 32, 32, self.gf_dim*1], name='g_h3', with_w=True)
            mean3, var3 = tf.nn.moments(h3, [0,1,2])
            h3 = tf.nn.relu(tf.nn.batch_normalization(h3, mean3, var3, None , None,1e-5,name='bn3'))

            h4, self.h4_w, self.h4_b = deconv2d(h3,
                [self.batch_size, 64, 64, 3], name='g_h4', with_w=True)                 # out_shape = [64,64,64,3]

            return tf.nn.tanh(h4)   
...                                                    # tanh(h4)
```


# 結果
生成された画像の一部(gifはサイズオーバーでした)
ロゴっぽい感じではありますかね
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/3b489792-a0c5-e76d-e4c5-434a9e7373ad.png)



![test_2016-08-15 03_59_21.png](https://qiita-image-store.s3.amazonaws.com/0/115249/34fe8c99-176b-3be3-9afd-34b97b6ebbdc.png)


# 改良を試みる--Feature matching
GANの学習テクニックなどが書かれている[Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498)を参考に改良したい。英語なので理解はアバウトですが。。。

既に先行研究[TensorFlowによるDCGANでアイドルの顔画像生成 その後の実験など](http://memo.sugyan.com/entry/2016/10/12/084751)でFeature matchingをされていたので参考にさせていただき、やってみました。[コード(github)](https://github.com/sanshirookazaki/logogan2-feature_matching)


```python:model.py
        #生成された画像の平均値と学習データ画像の平均値
        self.feature_matching = 0.1
        self.mean_from_g = tf.reduce_mean(self.G )#, reduction_indices=(0))      #生成画像平均値
        self.mean_from_i = tf.reduce_mean(self.images)# , reduction_indices=(0)) #学習画像平均値
        #平均値の差分に適度な値をかける
        self.g_mean_loss = tf.mul(tf.nn.l2_loss(self.mean_from_g - self.mean_from_i), self.feature_matching)

        #判別器の特徴(生成画像/学習画像)の平均値
        self.feature_form_i = tf.reduce_mean( self.D_feature )#, reduction_indices=(0))
        self.feature_from_g = tf.reduce_mean( self.D_feature_ )#, reduction_indices=(0))
        #平均値の差分に適度な値をかける
        self.g_feature_loss =  tf.mul(tf.nn.l2_loss(self.feature_from_g - self.feature_form_i), self.feature_matching)
        #self.losses = self.mean_loss + self.feature_loss 
        #tf.add_to_collection('ge_loss',self.losses)

        self.d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits, tf.ones_like(self.D)))
        self.d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits_, tf.zeros_like(self.D_)))
        self.g_loss_ = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits_, tf.ones_like(self.D_)))
        #tf.add_to_collection('ge_loss',self.ge_loss)
                                                    
        self.d_loss = self.d_loss_real + self.d_loss_fake    #disc _loss合計
        self.g_loss = self.g_loss_  + self.g_mean_loss + self.g_feature_loss  # generator_lossの計算
```


# 結果２
Feature matchingして生成された一部の画像
んー、大きな変化は見られないようですねー
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/bc3dcb4b-ef9e-0c9b-5272-eea099f873b1.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/4a6f40fe-fc0e-4b60-1f03-e54c696b3706.png)


# さらに改良を試み--データ変更・増量
とりあえずデータを変えて、ロゴ画像4350枚に増やし(前の643枚を含まない)
もう少し大きい画像で生成できるようにネットワークのスケールを変更して、
とりあえず生成してみる(Feature matchingなし)
[コードgithub](https://github.com/sanshirookazaki/logogan3)


![image](https://qiita-image-store.s3.amazonaws.com/0/115249/7db2bc68-5494-8867-3fda-6a749ddf2bcb.png)
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/ce4f90ee-5ab7-3600-7aa5-9c5f1b1b73b0.png) ![image](https://qiita-image-store.s3.amazonaws.com/0/115249/faa96919-c796-c1e6-dc41-9607e604d858.png)

# 結果3
大きい画像で見やすくなったかと。
同じロゴ画像でもデータが変わると、生成される画像も随分変わりますね

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/f2203e01-91bd-e42e-0188-290d4008f2ab.png)


![image](https://qiita-image-store.s3.amazonaws.com/0/115249/b4b8c645-4ad7-c34f-8d4e-59267b28674e.png)


# まだ改良を--Semi supervised Learning
論文[Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498)での、"Semi-supervised Learning"についてです。
今までは集めたロゴ画像に対しDiscriminatorが（生成されたもの/学習データ）の２値で分類をしていました。
これをもっと高度なラベルをつけて、つまり他クラス分類のようにKクラスの分類に生成された画像を加えてK+1クラスでの学習する。さらにGeneratorで生成された画像をデータセットに加えることで半教師あり学習となる？らしい。。。
そして誤差関数は
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/6ed3b7d4-8f84-65fb-8fa6-d737037886a1.png)

と定義するようです。これは赤い部分が通常のKクラス分類の誤差的なもので、青い部分が生成も含めた誤差を計算する関数で、今まで通りGANの誤差(下の青いとこ)で計算して、赤と青を足して誤差Eにするみたいな感じですかね。
。。。

んー難しいし、ロゴ画像を他クラスにはできない。ので、スマホアプリのアイコン画像を3000枚ほど収集し、
ロゴ　・　アプリ　・　生成　　の3クラスにすることにしてみました。
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/6cae7119-ab68-2881-91b3-cb0ad0e10388.png)収集したアプリ画像

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/ab08a47e-0f61-d795-f30f-635f6146dab3.png)





ロゴの画像も適当に3000枚に絞り、学習させてみる。[コードgithub](https://github.com/sanshirookazaki/logogan3-semi)

#結果４
生成された画像の一部。
うーん。微妙です。改善は確認できない。

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/f412357d-f709-a811-18dc-ad2902b6df5c.png)


![image](https://qiita-image-store.s3.amazonaws.com/0/115249/92e8cf95-0d9a-34a5-366a-d16238efa53b.png)

#まだまだ改良を--Minibatch discrimination
またまた論文[Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498)よりMinibatch discriminationというものをやってみる。
生成された画像には似たようなものがいくつも出ており、これは勾配が集中してしまうことが原因であり、Discriminatorの特徴量に以下ようなの計算した結果をサイド情報として結合し、次の層に渡すことで同じものが出ることを防ぎたいということらしいです。

Discriminatorの特徴量f(Xi)にテンソルT(Tの中身については論文では分かりませんでした)を
かけて、その結果マトリクスMとする。n個のMの行についてL1-distanceをi,j全て求め、Cbとしてe^CbのΣをB個並べたベクトルと元の特徴量f(Xi)と結合して次の層に渡す。
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/b27c9474-a3c1-8fe5-8f21-a238bd02a959.png)![image](https://qiita-image-store.s3.amazonaws.com/0/115249/26b53002-3592-309d-88c4-6d30501356ae.png)

今回はこれをDiscriminatorの３層目に入れ、４層目の入力に適用しました。
[https://github.com/jakekim1009/temporalprediction/blob/master/tensorflow_code/anthony_lstm/static_ops.py](https://github.com/jakekim1009/temporalprediction/blob/master/tensorflow_code/anthony_lstm/static_ops.py)を参考にしました。
＊コードは後ほど更新します。


#結果
Minibatch discriminationは計算に多くメモリを消費するため、生成の画像は小さくなってます。
多いパターンが生成されているような、そうでもないような感じです。あまり効果があったとは言えないですかね。。。
![image](https://qiita-image-store.s3.amazonaws.com/0/115249/8972d6a3-763b-66e1-d629-bc961622c7d4.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/115249/e0f0a2b4-7b1a-082a-f35f-d7e1eaae9d0f.png)



#まとめ
TensorFlowを使ってDCGANでロゴ画像を生成しました。その後の改良も試みてみたものの微妙な感じでした(やり方が間違いなどあるかも)。もうちょっと色々試してみたいと思います。

また更新します。