【特徴部位可視化】Grad-CAMとGuided Grad-CAMで各Layer可視化ムービー！

このシリーズもこれが四本目で、どうにかやっていることもキャッチアップ出来つつあり、今回は深い層が何をみているかを描画可視化することとします。
※実際にはその層から最終層へ一番寄与する勾配の大きさを評価する技術がGrad-CAMでその視覚的な描画がGuided-Grad-CAMということになります
![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/79bd69d1-e725-756a-17c4-29ad2a60678f.png)
上記の説明図のAにあたる層を上流に変化させたときのそれぞれの寄与をインプット画像に重畳して示しているものになります。
代表的なものとしてVGG16(weights='imagenet')に適用し、以下の動画が得られました。
![layer_VGG16_model_36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/b7e4c192-a28d-3336-968d-845e3a8d44fa.gif)
###やったこと
・各モデルに適用するための工夫
・適用にあたってのコードのポイント
・適用・結果
###・各モデルに適用するための工夫
これは前回の「[【文字列リスト】条件を満たす要素の一括抽出の仕方](https://qiita.com/MuAuan/items/3cbb4d0096043b42adab)」のおまけにコードを置きましたが、モデルを変更するのは、Fine-tuningより簡単です。しかも、学習するのでなければメモリーもあまり必要ありません。
ただし、各モデルはそれなりに大きく特徴（使っている層）がかなり異なります。また、学習データは同じものを学習しているものと思っていましたが、そうでもないようです。
※つまり同一のあのCat_Dogの画像に対して予測されたカテゴリが同じものもありますが、基本異なりました。（というか比較的最近のモデルでは犬や猫を学習していないようです）
しかし、ここではそれを無視して適当に出てきたものを解析することとしました。
そして、何よりモデル構成が異なるのでそれに対応するのが最大の工夫ですが、昨夜のコードを利用してうまく対応できましたので、以下のコードのポイントで述べることとします。

###・適用にあたってのコードのポイント
適用するにあたって、コードを整理してリンクのような構成にしました。
１．できるだけ関数化する
前回のカテゴリの動画から変更したコードは以下のとおりです。
今回は、モデルを変更したら、それに合うように解析するLayerを自動的に求めるのが肝であるが、それにともなって処々変更した。
①def preprocessed()の引数にlayer_nameを追加、その層のgrad_camを計算する。そのネットワークモデル（グラフ）としてmodelも入力できるようにし、
model = ResNet50(weights='imagenet')の定義をdef main()に移動した。
②一応、カテゴリの入力ｓを残した
③以下の関数で一番悩ましかったのは、grad_camのモデルとcompile_saliency_functionのモデルを何にするかといういことである。
本来なら、grad_camは元のmodel、compile_saliency_functionはguided_model(keras.activation.reluをtf.nn.reluに変更したモデル)とすべきだが、ResNet50やXceptionなどの長ーいモデルではactivation層とadd層などがlayer.nemeがmodelとguided_modelで同一でないためgrad_camのところでエラーではじかれる。以下の結果で例と理由を示すが、最終的に以下のコードのようにどちらもguided_modelを入力するのが妥当であるという結論となった。

```py
def preprocessed(model,guided_model,preprocessed_input,s,layer_name='block5_conv3'):
    predictions = model.predict(preprocessed_input)
    top_1 = decode_predictions(predictions)[0][s]
    predicted_class = predictions.argsort()[0][::-1][s]
    cam, heatmap = grad_cam(guided_model, preprocessed_input, predicted_class, layer_name=layer_name) #guided_model
    saliency_fn = compile_saliency_function(guided_model,activation_layer=layer_name) 
    saliency = saliency_fn([preprocessed_input, 0])
    gradcam = saliency[0] * heatmap[..., np.newaxis]
    return predictions, top_1, cam, gradcam
```
最初に利用するmodelとguided_model(Guided Grad-CAM計算用)を定義する。
そして、解析する画像をインプットする。

```py
def main():
    model = ResNet50(weights='imagenet') #ResNet50 VGG16
    model.summary()
    category=0
    ...
    s1=0
    size=(224,224)
    register_gradient()
    guided_model = modify_backprop(model, 'GuidedBackProp')
    frame = cv2.imread("cat_dog.png")
    ...
```
そして昨夜の記事で解説した以下のコードを利用して、解析したいlayer.nameをリスト化する。ここでは、ResNet50の場合のものを示している。このモデルは、おまけに置いたようにかなり深いがそのうちaddと最終層のactivation_98という層を抽出している。本来activation_49が最終層なのだが、上記のguided_modelを作成するとaddとactivation層はmodelからの通し番号が振られ、guided_modelではこんなに大きな値になる。

```py
    name_list=[l.name for l in guided_model.layers[1:]]  #guided_model
    name_list = [layer for layer in name_list if 'add' in layer or 'activation_98' in layer]
    print(len(name_list),name_list)
```
以下のような結果が得られます。

```
17 ['add_17', 'add_18', 'add_19', 'add_20', 'add_21', 'add_22', 'add_23', 'add_24', 'add_25', 'add_26', 'add_27', 'add_28', 'add_29', 'add_30', 'add_31', 'add_32', 'activation_98']
```
動画は時間で切っています。
層の種類の変更はs =s1%(len(name_list))で繰り返すようにしています。このsを使って上記のリストから指定してactivation_layerに代入してgrad-camなどを計算します。

```py
    timer0=time.time()
    while True:
        s =s1%(len(name_list))
        activation_layer=name_list[s]
        print(s)
        input= cv2.resize(frame, (480,480))
        input = cv2.cvtColor(input, cv2.COLOR_BGR2RGB)
        ax1.imshow(input)
        ax1.set_title("original_"+str(category)+"_ResNet50 "+str(activation_layer))
```
まず、画像を４次元に変更してpreprocessed()関数に入力します。

```py
        preprocessed_input= cv2.resize(frame, size)
        preprocessed_input= np.expand_dims(preprocessed_input, axis=0)
        predictions, top_1, cam, gradcam = preprocessed( model, guided_model, preprocessed_input, category, layer_name=activation_layer)
```
動画はcv2版もできますが、ここではplt版にしています。タイトルにいろいろな属性を記載しています。


```py
        input= cv2.resize(cam, (480,480))
        ax2.imshow(input)
        ax2.set_title("gradcam_ResNet50_"+str(top_1[1])+"_"+ str(int(top_1[2]*1000)/10)+" %")
        input= cv2.resize(deprocess_image(gradcam), (480,480))
        ax3.imshow(input)
        ax3.set_title("guided_gradcam_ResNet50"+str(top_1[1])+"_"+ str(int(top_1[2]*1000)/10)+" %")
        plt.pause(1)
        cv2.imshow("guided_gradcam_"+str(top_1[1])+"_"+ str(int(top_1[2]*1000)/10)+" %",input)
        s1 += 1
        plt.savefig("output/image_ResNet50_"+str(s1)+".jpg")
        dst = cv2.imread('output/image_ResNet50_'+str(s1)+'.jpg')
        img_dst = cv2.resize(dst, (int(480), 480))
        out.write(img_dst)
```
動画にするには、上記のcv2.imshowが大切で、これを描画すると以下のキー入力が安定して使えます。実際にはその下のifの部分で120秒記録して終了します。
ここで、上記の大量なjpg画像ファイルとoutで出力したmp4ファイルが保存されています。
そして、jpg画像ファイルから綺麗なGifアニメーションを生成したのが、最初に掲示した動画です。ほぼ同じような動画がmp4で保存されています。
※動画保存のおまじないなどのコードは今回は示していませんが、コード全体には含まれているので、参考にしてください

```py
        k = cv2.waitKey(1)&0xff
        if k == ord('q'):
            cv2.destroyAllWindows()
            break
        else:
            cv2.destroyAllWindows()
        print(time.time()-timer0)
        if time.time()-timer0>=120:
            out.release()
            break
if __name__ == '__main__':
    main()
```
###コードは以下に置きました
・[cheating_DL/plot_grad-cam_categoryC_layer.py](https://github.com/MuAuan/cheating_DL/blob/master/plot_grad-cam_categoryC_layer.py)
###適用・結果
VGG16：guided_modelは上記のとおりです。
VGG16 model compile_saliency_function;modelで実施
![layer_vgg16_m18.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/c30e01df-125d-1647-0bf6-dca6ea658229.gif)
これだと、Guided-Grad-CAMの画像がつぶれています。
そして、VGG16、VGG19モデルでは、どちらもmodelとguided_modelは同じlayer.nameを持っているので、model compile_saliency_function;にguided_modelを使えばどちらを使っても同じような画像が得られました。
しかし、それ以外ではこれらは異なります。以下、ResNet50の場合を比較します。
grad-cam;model compile_saliency_function;modelの場合
![layer_ResNet50_model_36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/250b38ca-784e-6a57-e8a3-89f9c73ba237.gif)
grad-cam,compile_saliency_function;guided_modelの場合
![layer_ResNet50_guided_model_21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/5c4429ae-98f8-71e5-c0be-e64246435f55.gif)
上記の動画はgrad-camはほぼ同じ動画が得られていますが、Guided-Grad-CAMはmodel-modelではVGG16と同じように、元の下絵が再現できていないのが分かります。
このことは、やはり先日の記事でreluの差し替えの問題で指摘しています。
ということで、直接画像を見ると以下のとおりになっています。
![image_ResNet50_17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/0effe40c-2627-eb43-fed8-da679bd734db.jpeg)
![image_ResNet50_17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/b1897801-0c9d-cf53-4131-debd677cbcd4.jpeg)
最後に、VGG19とVGG16を並べて貼っておきます。比較的似ている画像が動いているのが分かります。

|VGG16|VGG19|
|:-:|:-:|
|![layer_vgg16_g18.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/093cfe68-880d-5df6-b8e9-5969544b76fb.gif)|![layer_vgg19_22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/2b6ee3fd-c544-73c9-dcfc-299aed90aca6.gif)|
###まとめ
・いろいろなモデルの層毎のGrad-camとGuidel-Crad-camの可視化を実施した
・VGG16とVGG19だと、だいたい最後の二層程度で急激に選んだカテゴリに集中する
・そしてResNet50でさえ最後の層のGrad-camやGuided-Grad-CAMの画像は酷似している。
・compile_saliency_function;はguided_model(tf.nn.relu)でないと綺麗な画像は得られない

・この技術をAdversarialAttackの分析に応用したいと思う
・確率が低いカテゴリも綺麗に覚えていることもあるので、さらに利用の仕方を考えようと思う
###おまけ
```ResNet50
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to
==================================================================================================
input_1 (InputLayer)            (None, 224, 224, 3)  0
__________________________________________________________________________________________________
conv1_pad (ZeroPadding2D)       (None, 230, 230, 3)  0           input_1[0][0]
__________________________________________________________________________________________________
conv1 (Conv2D)                  (None, 112, 112, 64) 9472        conv1_pad[0][0]
__________________________________________________________________________________________________
bn_conv1 (BatchNormalization)   (None, 112, 112, 64) 256         conv1[0][0]
__________________________________________________________________________________________________
activation_1 (Activation)       (None, 112, 112, 64) 0           bn_conv1[0][0]
__________________________________________________________________________________________________
pool1_pad (ZeroPadding2D)       (None, 114, 114, 64) 0           activation_1[0][0]
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)  (None, 56, 56, 64)   0           pool1_pad[0][0]
__________________________________________________________________________________________________
res2a_branch2a (Conv2D)         (None, 56, 56, 64)   4160        max_pooling2d_1[0][0]
__________________________________________________________________________________________________
bn2a_branch2a (BatchNormalizati (None, 56, 56, 64)   256         res2a_branch2a[0][0]
__________________________________________________________________________________________________
activation_2 (Activation)       (None, 56, 56, 64)   0           bn2a_branch2a[0][0]
__________________________________________________________________________________________________
res2a_branch2b (Conv2D)         (None, 56, 56, 64)   36928       activation_2[0][0]
__________________________________________________________________________________________________
bn2a_branch2b (BatchNormalizati (None, 56, 56, 64)   256         res2a_branch2b[0][0]
__________________________________________________________________________________________________
activation_3 (Activation)       (None, 56, 56, 64)   0           bn2a_branch2b[0][0]
__________________________________________________________________________________________________
res2a_branch2c (Conv2D)         (None, 56, 56, 256)  16640       activation_3[0][0]
__________________________________________________________________________________________________
res2a_branch1 (Conv2D)          (None, 56, 56, 256)  16640       max_pooling2d_1[0][0]
__________________________________________________________________________________________________
bn2a_branch2c (BatchNormalizati (None, 56, 56, 256)  1024        res2a_branch2c[0][0]
__________________________________________________________________________________________________
bn2a_branch1 (BatchNormalizatio (None, 56, 56, 256)  1024        res2a_branch1[0][0]
__________________________________________________________________________________________________
add_1 (Add)                     (None, 56, 56, 256)  0           bn2a_branch2c[0][0]
                                                                 bn2a_branch1[0][0]
__________________________________________________________________________________________________
。。。
__________________________________________________________________________________________________
add_15 (Add)                    (None, 7, 7, 2048)   0           bn5b_branch2c[0][0]
                                                                 activation_43[0][0]
__________________________________________________________________________________________________
activation_46 (Activation)      (None, 7, 7, 2048)   0           add_15[0][0]
__________________________________________________________________________________________________
res5c_branch2a (Conv2D)         (None, 7, 7, 512)    1049088     activation_46[0][0]
__________________________________________________________________________________________________
bn5c_branch2a (BatchNormalizati (None, 7, 7, 512)    2048        res5c_branch2a[0][0]
__________________________________________________________________________________________________
activation_47 (Activation)      (None, 7, 7, 512)    0           bn5c_branch2a[0][0]
__________________________________________________________________________________________________
res5c_branch2b (Conv2D)         (None, 7, 7, 512)    2359808     activation_47[0][0]
__________________________________________________________________________________________________
bn5c_branch2b (BatchNormalizati (None, 7, 7, 512)    2048        res5c_branch2b[0][0]
__________________________________________________________________________________________________
activation_48 (Activation)      (None, 7, 7, 512)    0           bn5c_branch2b[0][0]
__________________________________________________________________________________________________
res5c_branch2c (Conv2D)         (None, 7, 7, 2048)   1050624     activation_48[0][0]
__________________________________________________________________________________________________
bn5c_branch2c (BatchNormalizati (None, 7, 7, 2048)   8192        res5c_branch2c[0][0]
__________________________________________________________________________________________________
add_16 (Add)                    (None, 7, 7, 2048)   0           bn5c_branch2c[0][0]
                                                                 activation_46[0][0]
__________________________________________________________________________________________________
activation_49 (Activation)      (None, 7, 7, 2048)   0           add_16[0][0]
__________________________________________________________________________________________________
avg_pool (GlobalAveragePooling2 (None, 2048)         0           activation_49[0][0]
__________________________________________________________________________________________________
fc1000 (Dense)                  (None, 1000)         2049000     avg_pool[0][0]
==================================================================================================
Total params: 25,636,712
Trainable params: 25,583,592
Non-trainable params: 53,120
__________________________________________________________________________________________________
```

guided_model ResNet50

```
Layer (type)                    Output Shape         Param #     Connected to
==================================================================================================
input_2 (InputLayer)            (None, 224, 224, 3)  0
__________________________________________________________________________________________________
conv1_pad (ZeroPadding2D)       (None, 230, 230, 3)  0           input_2[0][0]
__________________________________________________________________________________________________
conv1 (Conv2D)                  (None, 112, 112, 64) 9472        conv1_pad[0][0]
__________________________________________________________________________________________________
bn_conv1 (BatchNormalization)   (None, 112, 112, 64) 256         conv1[0][0]
__________________________________________________________________________________________________
activation_50 (Activation)      (None, 112, 112, 64) 0           bn_conv1[0][0]
__________________________________________________________________________________________________
pool1_pad (ZeroPadding2D)       (None, 114, 114, 64) 0           activation_50[0][0]
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)  (None, 56, 56, 64)   0           pool1_pad[0][0]
________________________________________________________________________________

。。。
________________________________________________________________________________
__________________
add_31 (Add)                    (None, 7, 7, 2048)   0           bn5b_branch2c[0][0]
                                                                 activation_92[0][0]
__________________________________________________________________________________________________
activation_95 (Activation)      (None, 7, 7, 2048)   0           add_31[0][0]
__________________________________________________________________________________________________
res5c_branch2a (Conv2D)         (None, 7, 7, 512)    1049088     activation_95[0][0]
__________________________________________________________________________________________________
bn5c_branch2a (BatchNormalizati (None, 7, 7, 512)    2048        res5c_branch2a[0][0]
__________________________________________________________________________________________________
activation_96 (Activation)      (None, 7, 7, 512)    0           bn5c_branch2a[0][0]
__________________________________________________________________________________________________
res5c_branch2b (Conv2D)         (None, 7, 7, 512)    2359808     activation_96[0][0]
__________________________________________________________________________________________________
bn5c_branch2b (BatchNormalizati (None, 7, 7, 512)    2048        res5c_branch2b[0][0]
__________________________________________________________________________________________________
activation_97 (Activation)      (None, 7, 7, 512)    0           bn5c_branch2b[0][0]
__________________________________________________________________________________________________
res5c_branch2c (Conv2D)         (None, 7, 7, 2048)   1050624     activation_97[0][0]
__________________________________________________________________________________________________
bn5c_branch2c (BatchNormalizati (None, 7, 7, 2048)   8192        res5c_branch2c[0][0]
__________________________________________________________________________________________________
add_32 (Add)                    (None, 7, 7, 2048)   0           bn5c_branch2c[0][0]
                                                                 activation_95[0][0]
__________________________________________________________________________________________________
activation_98 (Activation)      (None, 7, 7, 2048)   0           add_32[0][0]
__________________________________________________________________________________________________
avg_pool (GlobalAveragePooling2 (None, 2048)         0           activation_98[0][0]
__________________________________________________________________________________________________
fc1000 (Dense)                  (None, 1000)         2049000     avg_pool[0][0]
==================================================================================================
Total params: 25,636,712
Trainable params: 25,583,592
Non-trainable params: 53,120
__________________________________________________________________________________________________
17 ['add_17', 'add_18', 'add_19', 'add_20', 'add_21', 'add_22', 'add_23', 
'add_24', 'add_25', 'add_26', 'add_27', 'add_28', 'add_29', 'add_30', 'add_31', 
'add_32', 'activation_98']
```