2
4

More than 1 year has passed since last update.

EfficientNetV2の学習済みモデルでGrad-CAMを試す

Last updated at Posted at 2022-04-24

はじめに

CNN可視化手法であるGrad-CAMを試してみようと思います。
画像分類AIの予測根拠の可視化などに使われたりしている技術です。

  • 環境
    OS: Ubuntu18.04LTS
    CPU: Intel® Core™ i7-8700 CPU @ 3.20GHz × 12
    GPU: GeForce RTX2080
    Python: Python3.7.9(Anacondaで作成した仮想環境)
    tensorflow:2.8.0
    matplotlib:3.5.1
    numpy:1.21.5
    opencv-python:4.5.5.64

  • 参考記事
    kerasとtensorflowでGrad-CAMを実装してみた

学習済みモデルの読み込み

# ライブラリの読み込み
import tensorflow as tf
import numpy as np
import cv2
import matplotlib.pyplot as plt

# 学習済みEfficientNet_v2の読み込み
model = tf.keras.applications.efficientnet_v2.EfficientNetV2B0(weights="imagenet")

Grad-CAMのアルゴリズム

Grad-CAMの関数です。参考記事①を参考にしました。

def grad_cam(input_model, image, layer_name):
    # 前処理
    img_arr = tf.keras.preprocessing.image.img_to_array(image)
    x = img_arr[tf.newaxis]
    x = tf.keras.applications.efficientnet_v2.preprocess_input(x)

    grad_model = tf.keras.models.Model([input_model.inputs], [input_model.get_layer(layer_name).output, input_model.output])

    with tf.GradientTape() as tape:
        conv_outputs, predictions = grad_model(x)
        class_idx = np.argmax(predictions[0])
        loss = predictions[:, class_idx]

    # 勾配を計算
    output = conv_outputs[0]
    grads = tape.gradient(loss, conv_outputs)[0]

    gate_f = tf.cast(output > 0, 'float32')
    gate_r = tf.cast(grads > 0, 'float32')

    guided_grads = gate_f * gate_r * grads

    # 重みを平均化して、レイヤーの出力に乗じる
    weights = np.mean(guided_grads, axis=(0, 1))
    cam = np.dot(output, weights)

    # 画像を元画像と同じ大きさにスケーリング
    cam = cv2.resize(cam, (x.shape[1], x.shape[2]), cv2.INTER_LINEAR)
    # ReLUの代わり
    cam  = np.maximum(cam, 0)
    # ヒートマップを計算
    heatmap = cam / cam.max()

    # モノクロ画像に疑似的に色をつける
    jet_cam = cv2.applyColorMap(np.uint8(255.0*heatmap), cv2.COLORMAP_JET)
    # RGBに変換
    rgb_cam = np.float32(cv2.cvtColor(jet_cam, cv2.COLOR_BGR2RGB))
    # もとの画像に合成
    output_arr = cv2.addWeighted(src1=img_arr, alpha=0.7, src2=rgb_cam, beta=0.3, gamma=0)
    output_image = tf.keras.preprocessing.image.array_to_img(output_arr)
    
    return output_image

前処理の部分や、最後の画像合成のところは少し変えています。 

実施結果

予測画像には以下の画像を使用します。
Penguin
学習済みモデルによる予測結果は以下のようになります。

[[('n02056570', 'king_penguin', 0.8875521), ('n01798484', 'prairie_chicken', 0.0006106787), ('n02071294', 'killer_whale', 0.0005540343), ('n03743016', 'megalith', 0.0005340129), ('n01629819', 'European_fire_salamander', 0.00049976655)]]

Grad-CAMを実行します。

# 画像読み込み
base_input_shape = model.input_shape[1:]
img_pil = tf.keras.preprocessing.image.load_img("./penguin.jpg", target_size=(base_input_shape[0], base_input_shape[1]))
# Grad-CAM
layer_name = 'top_activation'
cam = grad_cam(model, img_pil, layer_name)

layer_nameは一番最後の層の名前を指定します。model.summary()で確認することができます。
grad_camの出力(元の画像とヒートマップを合成した画像)は↓のようになりました。
output.png
全てのペンギンの顔部分と、一番左にいるペンギンの全身が判断根拠となっていますね。
顔部分の黄色い毛は確かに皇帝ペンギンの特徴的な部分ですし、一番左のペンギンは横を向いていて黒い毛と白い毛がどちらも見えているのでこちらもペンギンの特徴としては重要そうです。

おわりに

EfficientNetV2の学習済みモデルを使ってGrad-CAMを試してみました。
Grad-CAM自体は別にEfficientNetじゃなくても使えるので、画像分類AI案件で性能を説明する際などに使用できればなと思います。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4