KaiNDを作っていくぞ会 第5回 --MobileNetV2をGrad-CAMで可視化--
こんにちは。
開発メンバー、AI担当のKです。
最近、毎日簡易アエロバイクをこいでいるおかげか、6 [kg] 減量しました。
この調子でスマートになります。
目次
0. 前回まで
↓前回のブログ
https://tottorisnow33.hatenablog.com/entry/2022/07/23/094350
引き続き、KaiNDシリーズを拡張・改善しています。一緒にやっているメンバーで以下のように分担しています。
松原: KaiND Web
K : AI自体の改善
Y :スマホアプリのUIの改善
今回は、KaiNDを作っていくぞ会 第2回 --AI改善-- - 茨城エンジニアのPython開発日記 の続きで、可視化にチャレンジです。
1. 使用するNetwork
前回は可視化のためにResNetを使っていましたが、今回はKaiNDのアプリに載せているMobileNetV2で可視化します。
MobileNetV2は事前学習済みのものを用意して、転移学習でパグとブルドッグの識別をできるようにします。
MobileNetV2の転移学習については、以下を参考にしました。
www.tensorflow.org
2. Grad-CAMで可視化するために注意したこと
Grad-CAMで可視化する場合、Conv layerの出力が必要です。
値を取得するために、Conv layerの名前を指定する必要があります。
しかし、Tensorflow公式のやり方のように tf.keras.applications.MobileNetV2() の後に自分でlayerを追加すると、Conv layerを指定できません。
model.summary()で見ると分かるのですが、MobileNetV2のところが、"mobilenetv2_1.00_224"という1つの塊になってしまいます。
# Tensorflowの公式チュートリアルを参考にしたソース IMAGE_SIZE = (224, 224) IMG_SHAPE = IMAGE_SIZE + (3,) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet') base_model.trainable = False inputs = tf.keras.Input(shape=IMG_SHAPE) x = base_model(inputs, training=False) # 層追加 x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = tf.keras.layers.Dense(y_train.shape[-1], kernel_regularizer=tf.keras.regularizers.l2(0.0001), activation="softmax")(x) model = tf.keras.Model(inputs, outputs) model.summary()
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 224, 224, 3)] 0
_________________________________________________________________
mobilenetv2_1.00_224 (Model) (None, 7, 7, 1280) 2257984
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280) 0
_________________________________________________________________
dropout (Dropout) (None, 1280) 0
_________________________________________________________________
dense (Dense) (None, 3) 3843
=================================================================
Total params: 2,261,827
Trainable params: 3,843
Non-trainable params: 2,257,984
_________________________________________________________________
そこで、ソースを以下のように少し書き換えて、MobileNetV2のconv layerの名前を取得できるようにしました。
ポイントは、base_model.outputを使って、MobileNetV2の出力を追加したい層の入力とすることと、tf.keras.Modelのinputにはbase_model.inputを使うことです。
# アレンジしたソース IMAGE_SIZE = (224, 224) IMG_SHAPE = IMAGE_SIZE + (3,) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet') base_model.trainable = False x = base_model.output # 層追加 x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = tf.keras.layers.Dense(y_train.shape[-1], kernel_regularizer=tf.keras.regularizers.l2(0.0001), activation="softmax")(x) model = tf.keras.Model(base_model.input, outputs) model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
============================================================
input_1 (InputLayer) [(None, 224, 224, 3) 0
__________________________________________________________________________________________________
Conv1_pad (ZeroPadding2D) (None, 225, 225, 3) 0 input_1[0][0]
__________________________________________________________________________________________________
Conv1 (Conv2D) (None, 112, 112, 32) 864 Conv1_pad[0][0]
__________________________________________________________________________________________________
bn_Conv1 (BatchNormalization) (None, 112, 112, 32) 128 Conv1[0][0]
__________________________________________________________________________________________________
Conv1_relu (ReLU) (None, 112, 112, 32) 0 bn_Conv1[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise (Depthw (None, 112, 112, 32) 288 Conv1_relu[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise_BN (Bat (None, 112, 112, 32) 128 expanded_conv_depthwise[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise_relu (R (None, 112, 112, 32) 0 expanded_conv_depthwise_BN[0][0]
__________________________________________________________________________________________________
expanded_conv_project (Conv2D) (None, 112, 112, 16) 512 expanded_conv_depthwise_relu[0][0
__________________________________________________________________________________________________
expanded_conv_project_BN (Batch (None, 112, 112, 16) 64 expanded_conv_project[0][0]
__________________________________________________________________________________________________
block_1_expand (Conv2D) (None, 112, 112, 96) 1536 expanded_conv_project_BN[0][0]
__________________________________________________________________________________________________
block_1_expand_BN (BatchNormali (None, 112, 112, 96) 384 block_1_expand[0][0]
__________________________________________________________________________________________________
block_1_expand_relu (ReLU) (None, 112, 112, 96) 0 block_1_expand_BN[0][0]
__________________________________________________________________________________________________
block_1_pad (ZeroPadding2D) (None, 113, 113, 96) 0 block_1_expand_relu[0][0]
__________________________________________________________________________________________________
block_1_depthwise (DepthwiseCon (None, 56, 56, 96) 864 block_1_pad[0][0]
__________________________________________________________________________________________________
block_1_depthwise_BN (BatchNorm (None, 56, 56, 96) 384 block_1_depthwise[0][0]
__________________________________________________________________________________________________
block_1_depthwise_relu (ReLU) (None, 56, 56, 96) 0 block_1_depthwise_BN[0][0]
__________________________________________________________________________________________________
block_1_project (Conv2D) (None, 56, 56, 24) 2304 block_1_depthwise_relu[0][0]
__________________________________________________________________________________________________
block_1_project_BN (BatchNormal (None, 56, 56, 24) 96 block_1_project[0][0]
__________________________________________________________________________________________________
.....[中略]......
block_16_expand (Conv2D) (None, 7, 7, 960) 153600 block_15_add[0][0]
__________________________________________________________________________________________________
block_16_expand_BN (BatchNormal (None, 7, 7, 960) 3840 block_16_expand[0][0]
__________________________________________________________________________________________________
block_16_expand_relu (ReLU) (None, 7, 7, 960) 0 block_16_expand_BN[0][0]
__________________________________________________________________________________________________
block_16_depthwise (DepthwiseCo (None, 7, 7, 960) 8640 block_16_expand_relu[0][0]
__________________________________________________________________________________________________
block_16_depthwise_BN (BatchNor (None, 7, 7, 960) 3840 block_16_depthwise[0][0]
__________________________________________________________________________________________________
block_16_depthwise_relu (ReLU) (None, 7, 7, 960) 0 block_16_depthwise_BN[0][0]
__________________________________________________________________________________________________
block_16_project (Conv2D) (None, 7, 7, 320) 307200 block_16_depthwise_relu[0][0]
__________________________________________________________________________________________________
block_16_project_BN (BatchNorma (None, 7, 7, 320) 1280 block_16_project[0][0]
__________________________________________________________________________________________________
Conv_1 (Conv2D) (None, 7, 7, 1280) 409600 block_16_project_BN[0][0]
__________________________________________________________________________________________________
Conv_1_bn (BatchNormalization) (None, 7, 7, 1280) 5120 Conv_1[0][0]
__________________________________________________________________________________________________
out_relu (ReLU) (None, 7, 7, 1280) 0 Conv_1_bn[0][0]
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 1280) 0 out_relu[0][0]
__________________________________________________________________________________________________
dropout (Dropout) (None, 1280) 0 global_average_pooling2d[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, 3) 3843 dropout[0][0]
============================================================
Total params: 2,261,827
Trainable params: 3,843
Non-trainable params: 2,257,984
__________________________________________________________________________________________________
これで、Conv layerの名前を取得できるようになりました。
後は↓の参考ソースのように、Conv layerのLayer Name (とモデルのoutput) から勾配を取る感じです。
Grad CAM implementation with Tensorflow 2 · GitHub
3. MobileNetV2でGrad-CAMをやってみた結果
自作ResNetでやってみた結果は↓です。
【勉強会報告】
— ごまあぶら (@tsukuruiroirop1) July 6, 2022
MobileNetV2から転移学習したものでGrad-camの可視化をしました。
1枚目が入力画像。
2枚目が、前回のうまく学習できていないResNetを可視化。3枚目が、事前学習済みMobileNetV2を転移学習したもの。
2枚目は注目しているところがおかしい。
3枚目はパグの特徴的なところを注目してる。 pic.twitter.com/BT8rttOTKd
【勉強会報告】
— ごまあぶら (@tsukuruiroirop1) July 30, 2022
MobileNetV2から転移学習したものでGrad-camの可視化をブルドッグでも見てみました。
1枚目が入力画像。
2枚目が可視化画像です。
顔や耳、背中あたりを注目しているのがわかります。 pic.twitter.com/tUC6cfhjtm