高精度で画像が綺麗なclassifier_AutoencoderができたII♬～Wide_resnet＿Autoencoder

前回は、ある意味最も簡単で[高精度なVGG16ベースにAutoencoder](https://qiita.com/MuAuan/items/02f4c8d2b900f1019489)を作成し、十分な精度と非常に綺麗な画像生成ができることを報告した。今回はさらに高精度をたたき出していて比較的簡単な構造を持つWide_resnetを利用してAutoencoderを作成・評価したので報告する。

コードは以下に置いてある（直接リンク）
[AutoEncoder/wide_resnet_AE.py](https://github.com/MuAuan/AutoEncoder/blob/master/wide_resnet_AE.py)

Wide_resnetの元コードは以下を参考にさせていただきました。
因みに、以下の論文によればCifar10で条件次第で９６％程度の精度が出るようである。
【参考】
[transcranial/wide-resnet/wide-resnet.ipynb](https://github.com/transcranial/wide-resnet/blob/master/wide-resnet.ipynb)
オリジナル論文は以下である。
[Wide Residual Networks：Sergey Zagoruyko, Nikos Komodakis
(Submitted on 23 May 2016 (v1), last revised 14 Jun 2017 (this version, v4))](https://arxiv.org/abs/1605.07146)

ということで、今回も前回同様にこれをAutoencoderにカスタマイズして、高精度・高美麗な画像生成を目指す。

###プログラムについて
このモデルの構造は、以下のような構成になっている。
まず、導入部。。。ここは通常の構成である。```Activation```も ```y = Activation('relu')(y)```である。```Dropout(0.5)```である。ただし、```conv2d_2 (Conv2D)  (None, 32, 32, 40)   5800        activation_1[0][0]```の```(None, 32, 32, 40)```の次元40がモデルワイドパラメータとなっており、通常より広く、80になったりする。

```
Layer (type)                    Output Shape         Param #     Connected to
==================================================================================================
input_1 (InputLayer)            (None, 32, 32, 3)    0
__________________________________________________________________________________________________
conv2d_1 (Conv2D)               (None, 32, 32, 16)   448         input_1[0][0]
__________________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, 32, 32, 16)   64          conv2d_1[0][0]
__________________________________________________________________________________________________
activation_1 (Activation)       (None, 32, 32, 16)   0           batch_normalization_1[0][0]
__________________________________________________________________________________________________
conv2d_2 (Conv2D)               (None, 32, 32, 40)   5800        activation_1[0][0]
__________________________________________________________________________________________________
batch_normalization_2 (BatchNor (None, 32, 32, 40)   160         conv2d_2[0][0]
__________________________________________________________________________________________________
activation_2 (Activation)       (None, 32, 32, 40)   0           batch_normalization_2[0][0]
__________________________________________________________________________________________________
dropout_1 (Dropout)             (None, 32, 32, 40)   0           activation_2[0][0]
__________________________________________________________________________________________________
conv2d_3 (Conv2D)               (None, 32, 32, 40)   14440       dropout_1[0][0]
__________________________________________________________________________________________________
```
そして、このモデルのResnet的な特徴が次に現れる。すなわち、```lambda_1(Lambda)```の引数はshortcutである```conv2d_1```入力直後のテンソルと、直前の```conv2d_3```のmergeである。

```
_________________________________________________________________________________________________
lambda_1 (Lambda)               (None, 32, 32, 40)   0           conv2d_1[0][0]
__________________________________________________________________________________________________
merge_1 (Merge)                 (None, 32, 32, 40)   0           conv2d_3[0][0]
                                                                 lambda_1[0][0]
__________________________________________________________________________________________________
```
そしてここからは、繰り返しの以下の構造が続く。ここでもmearge_1からmerge_2へshortcutしている。

```
__________________________________________________________________________________________________
batch_normalization_3 (BatchNor (None, 32, 32, 40)   160         merge_1[0][0]
__________________________________________________________________________________________________
activation_3 (Activation)       (None, 32, 32, 40)   0           batch_normalization_3[0][0]
__________________________________________________________________________________________________
conv2d_4 (Conv2D)               (None, 32, 32, 40)   14440       activation_3[0][0]
__________________________________________________________________________________________________
batch_normalization_4 (BatchNor (None, 32, 32, 40)   160         conv2d_4[0][0]
__________________________________________________________________________________________________
activation_4 (Activation)       (None, 32, 32, 40)   0           batch_normalization_4[0][0]
__________________________________________________________________________________________________
dropout_2 (Dropout)             (None, 32, 32, 40)   0           activation_4[0][0]
__________________________________________________________________________________________________
conv2d_5 (Conv2D)               (None, 32, 32, 40)   14440       dropout_2[0][0]
__________________________________________________________________________________________________
merge_2 (Merge)                 (None, 32, 32, 40)   0           conv2d_5[0][0]
                                                                 merge_1[0][0]
__________________________________________________________________________________________________
```
そして、merge_3, merge_4と同様な構造が続き、以下の通り```AveragePooling2D(pool_size=(2, 2)…```によりテンソル(None, 32, 32, 40)を(None, 16, 16, 80)に圧縮する。

```
batch_normalization_9 (BatchNor (None, 32, 32, 40)   160         merge_4[0][0]
__________________________________________________________________________________________________
activation_9 (Activation)       (None, 32, 32, 40)   0           batch_normalization_9[0][0]
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 16, 16, 80)   28880       activation_9[0][0]
__________________________________________________________________________________________________
```
そして、merge_5～merge_8と同様な構造が続き、以下の通りテンソルを(None, 16, 16, 80)から(None, 8, 8, 160)へ圧縮する。

```
batch_normalization_17 (BatchNo (None, 16, 16, 80)   320         merge_8[0][0]
__________________________________________________________________________________________________
activation_17 (Activation)      (None, 16, 16, 80)   0           batch_normalization_17[0][0]
__________________________________________________________________________________________________
conv2d_18 (Conv2D)              (None, 8, 8, 160)    115360      activation_17[0][0]
__________________________________________________________________________________________________
```
最後は、merge_12の後、BNとReluを通して、```AveragePooling2D(pool_size=(8, 8)…```
により、(None, 1, 1, 160) に圧縮して、通常通りflattenしてsoftmaxしてカテゴライズする。

```
batch_normalization_25 (BatchNo (None, 8, 8, 160)    640         merge_12[0][0]
__________________________________________________________________________________________________
activation_25 (Activation)      (None, 8, 8, 160)    0           batch_normalization_25[0][0]
__________________________________________________________________________________________________
average_pooling2d_3 (AveragePoo (None, 1, 1, 160)    0           activation_25[0][0]
__________________________________________________________________________________________________

flatten_1 (Flatten)             (None, 160)          0           average_pooling2d_3[0][0]
__________________________________________________________________________________________________
softmax (Dense)                 (None, 10)           1610        flatten_1[0][0]
__________________________________________________________________________________________________
```
一方、画像生成は、どこから生成してもよいが、経験的に中間テンソルの次元が大きい方が良い画像が得られることから、一度圧縮しただけのmerge_5を入力とした。

```
conv2d_26 (Conv2D)              (None, 16, 16, 160)  115360      merge_5[0][0]
__________________________________________________________________________________________________
up_sampling2d_1 (UpSampling2D)  (None, 32, 32, 160)  0           conv2d_26[0][0]
__________________________________________________________________________________________________
conv_out (Conv2D)               (None, 32, 32, 3)    4323        up_sampling2d_1[0][0]
==================================================================================================
```

論文の主張は、この次元を広くとることにより、浅く計算量も少ないが、精度を上げられるということである。

最後にパラメータは以下の通りで、主張のとおり得られた精度の割に非常に少なくなっています。

```
==================================================================================================
Total params: 2,399,565
Trainable params: 2,395,053
Non-trainable params: 4,512
__________________________________________________________________________________________________
```
###結果
まず、精度は以下の通りである。
flatten_1 (Flatten)  (None, 160)のときDataAugmentationして、400回回した時の最後の20epochは以下のとおりである。だいたい、精度は画像で９０．７％、カテゴライズの精度は９０．３％程度である。

```
i	loss       conv_acc     cat_acc     val_loss   val_conv_acc val_cat_acc
9	0.041986	0.887139	0.994700	0.532085	0.902588	0.901500
9	0.040450	0.889115	0.994980	0.537650	0.904794	0.900800
9	0.038427	0.889174	0.995360	0.535107	0.902376	0.900600
9	0.039437	0.890011	0.995440	0.536625	0.905926	0.903300
9	0.037745	0.890337	0.996140	0.533567	0.906014	0.903200
9	0.037827	0.890247	0.996040	0.533253	0.907465	0.902800
9	0.037454	0.890561	0.995900	0.534036	0.905807	0.902000
9	0.036824	0.890759	0.995940	0.531661	0.903773	0.903700
9	0.037021	0.890887	0.996000	0.532117	0.907008	0.903500
9	0.036617	0.890728	0.995800	0.533722	0.906485	0.903700
9	0.037916	0.891020	0.995760	0.533727	0.904399	0.903000
9	0.036099	0.890885	0.996480	0.533688	0.906160	0.903000
9	0.036649	0.890819	0.996400	0.531420	0.906098	0.903100
9	0.037322	0.890810	0.995940	0.531118	0.905196	0.903200
9	0.038246	0.890967	0.995080	0.531589	0.905818	0.903000
9	0.037161	0.890893	0.995660	0.531726	0.906644	0.903400
9	0.036335	0.891010	0.996340	0.531812	0.906841	0.903500
9	0.036954	0.890790	0.995640	0.533455	0.906801	0.903300
9	0.036455	0.890893	0.996120	0.533576	0.906563	0.903000
9	0.035963	0.890935	0.996280	0.532582	0.906277	0.903300
```

また、flatten_1 (Flatten)  (None, 320)だと、分類精度９２％、画像精度９１％程度である。

```
4	0.036846	0.864633	0.999540	0.548062	0.902541	0.922500
7	0.034431	0.867351	0.999460	0.559103	0.899032	0.923100
15	0.031585	0.869216	0.999540	0.569139	0.912605	0.920400
```

###画像出力
ここまでくると、元画像と生成画像は変わらないね。
![WresnetACE009.png](https://qiita-image-store.s3.amazonaws.com/0/233744/fea3d9ba-5f5e-de80-dc34-e727a6b992f0.png)
一応、以下に履歴をアニメーションすると、少しは画像が変化するはずです。大きく拡大して、画像をじっと見ていると滲みが変化しているのがわかると思います。まあ、元画像も微妙に滲みが動くんですけど、。。。
![Wresnet160.gif](https://qiita-image-store.s3.amazonaws.com/0/233744/29a21b78-3e1f-5be9-39b7-771000015c96.gif)

###工夫した点
今回もoptimizer=sgdはうまく収束しなかった。
しかし、optimizer=admなんだけど、前回より少し進歩して以下のとおり```lr_scheduler = LearningRateScheduler(lr_sch)```を導入してepoch回数によりパラメータを変化させた。

```
# Learning rate schedule
def lr_sch(epoch):
    if epoch < 4:
        return 0.0001  #0.1
    elif epoch < 8:  #120
        return 0.00005 #0.02
    elif epoch < 12:  #160
        return 0.00001  #0.004
    else:
        return 0.000005  #0.0008

# Learning rate scheduler callback
lr_scheduler = LearningRateScheduler(lr_sch)

adm = Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.)
```
ということで、パラメーターは最初少し大きめの値で準安定解から脱出して安定解へ向かうイメージでフィッティングできたと思う。
※上記はかなり収束が進んだ段階での最終的なフィッティんで利用したものである

###まとめ
・分類精度と画像生成精度の両方を兼ね備えた最高精度Classifier_Autoencoderが作成できた
###課題
・オプティマイザーとしてＳＧＤが使えなかったが、理由は不明である
・本来Autoencoderは、次元圧縮等で有効という話があったが、今回はそこを無視して生成画像の精度のみを追求した。両者を兼ね備えたAutoencoderが可能なのかをさらに探求する必要がある
・入出力の大きさは固定されており、物体認識等では入力サイズフリーが望ましいので、挑戦しようと思う