Grad-CAM++は、その名前が示す通りGrad-CAMを改良した手法です。Grad-CAMはCNNの構造に関わらず汎用的に使えるよう、クラスへの寄与を勾配を使って計算しています。一方、Grad-CAM++では、この勾配の計算をさらに細かく工夫することで、Grad-CAMの欠点であった「各ピクセルの重要度を均一に扱ってしまう点」を改善しています。これにより、各ピクセルごとのクラス判定への寄与をより正確に評価できるようになりました。

Grad-CAM++とは

Grad-CAM++(Gradient-weighted Class Activation Mapping++)とは、CNNが画像内のどこに注目して分類したかを可視化する「Grad-CAM」を改良した手法です。Grad-CAMでは、CNNの分類結果に対して各特徴マップの重要度を勾配を使って計算していましたが、その際に特徴マップの重要度は全ピクセルの勾配の平均によって決定するため、曖昧性が強いという課題がありました。具体的には、ピクセル範囲としては小さいがすごく重要な部分(勾配が大きい)がある場合でも、平均を取ってしまうため、特徴マップとしては重要ではないと判断されてしまう場合があるということです。そのため特徴マップ内の空間的な違いを無視してしまう手法になっていました。

Grad-CAM++ではこの点を改善し、各特徴マップ内の個々のピクセルごとに細かく重要度(重み)を計算する仕組みを導入しています。具体的には、勾配の情報をより詳細に解析(高次の微分を利用)することで、特徴マップ上でのピクセル単位の寄与を正確に捉えられるようになりました。この改良により、特徴マップにおける重要度をより厳密に求めることができるようになったのがGrad-CAM++です。

Grad-CAM++の利用結果

下記の画像は同じ画像に対してGrad-CAMとGrad-CAM++を適用した結果です。結果を見ると、Grad-CAM++はより正確に猫の部分がヒートマップ上で重要視されていることが分かります。このようにGrad-CAM++は複雑な計算をすることでより正確なヒートマップを実現しています。

Grad-CAM++の仕組み

Grad-CAM++がGrad-CAMと異なるのは、特徴マップの重要度の決定方法です。以下はGrad-CAM++の特徴マップの重要度を求める式です。

Grad-CAMと大きく異なる点は複数次元での微分を行うことです。これによる利点は、1次元の微分では一時的な増減はわかりますが、それが継続的なものかであるかがわかりません。一方、Grad-CAM++では、勾配そのものに加えて、勾配の変化量(二階・三階微分)も考慮することで、より精密に「本当に重要な領域」を見極めることができます。これにより、単に一時的に勾配が大きいだけの領域ではなく、継続的にモデルの出力に強く影響しているピクセルを特定することが可能になります。イメージとしては関数の増減表などに近いかもしれません。このようにしてGrad-CAM++は各ピクセルの重要度をより正確に把握し、特徴マップの重要度を決定しているわけです。

やってみよう

以下はGrad-CAM++のコードおよびその実験結果です。

import numpy as np
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
from tensorflow.keras.applications.resnet50 import ResNet50, preprocess_input, decode_predictions

# モデルと対象の層
base_model = ResNet50(weights='imagenet')
target_layer = base_model.get_layer("conv5_block3_out")

# Grad-CAM++ 実装
def grad_cam_plus_plus(model, image_path, class_index=None):
    img = cv2.imread(image_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, (224, 224))
    input_tensor = preprocess_input(np.expand_dims(img.astype(np.float32), axis=0))

    grad_model = tf.keras.models.Model(
        [model.inputs], [target_layer.output, model.output]
    )

    with tf.GradientTape() as tape:
        conv_outputs, predictions = grad_model(input_tensor)
        if class_index is None:
            class_index = tf.argmax(predictions[0])
        output = predictions[:, class_index]

    grads = tape.gradient(output, conv_outputs)[0]  # shape=(H, W, C)
    conv_outputs = conv_outputs[0]  # shape=(H, W, C)

    # 二階勾配と三階勾配の近似
    grads_squared = tf.square(grads)
    grads_cubed = tf.pow(grads, 3)
    global_sum = tf.reduce_sum(conv_outputs, axis=[0, 1])

    alpha_numer = grads_squared
    alpha_denom = 2 * grads_squared + global_sum * grads_cubed
    alpha_denom = tf.where(alpha_denom != 0.0, alpha_denom, tf.ones_like(alpha_denom))
    alphas = alpha_numer / (alpha_denom + 1e-7)

    weights = tf.reduce_sum(alphas * tf.nn.relu(grads), axis=[0, 1])  # shape=(C,)
    cam = tf.reduce_sum(weights * conv_outputs, axis=-1)  # shape=(H,W)
    cam = tf.nn.relu(cam)
    cam = cam.numpy()
    cam = (cam - cam.min()) / (cam.max() - cam.min() + 1e-8)
    return cam, img

# ヒートマップと重ね合わせ画像を表示
def show_gradcam(image_path, cam_map):
    original = cv2.imread(image_path)
    original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB)
    heatmap = cv2.resize(cam_map, (original.shape[1], original.shape[0]))
    heatmap = np.uint8(255 * heatmap)
    heatmap_color = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
    heatmap_color = cv2.cvtColor(heatmap_color, cv2.COLOR_BGR2RGB)
    overlay = cv2.addWeighted(original, 0.6, heatmap_color, 0.4, 0)

    plt.figure(figsize=(12, 5))
    plt.subplot(1, 2, 1)
    plt.imshow(original)
    plt.title("Original Image")
    plt.axis("off")
    plt.subplot(1, 2, 2)
    plt.imshow(overlay)
    plt.title("Grad-CAM++")
    plt.axis("off")
    plt.show()

# 使用例
if __name__ == "__main__":
    image_path = ""  # 画像のパス
    cam_map, _ = grad_cam_plus_plus(base_model, image_path)
    show_gradcam(image_path, cam_map)

画像の結果を見るとGrad-CAMに比べて正確に犬全体を捉えていることが分かります。これは、ピクセル単位により細かく注目したことで、重要な部分の見落としが少なくなったためだと考えられます。

Grad-CAMとGrad-CAM++のスコア比較

こちらはGrad-CAM++の論文である”Grad-CAM++: Improved Visual Explanations for
Deep Convolutional Networks”に掲載されているGrad-CAMとの性能比較の結果です。

Average Drop

こちらはそれぞれの手法で重要と判断されたピクセルのみを画像解析モデルに与えた際に、画像全体を与えたときと比較してどれほど予測精度が低下するかを表したものです。Grad-CAMでは重要領域のみをモデルに入力した場合、およそ50%の精度低下が見られましたが、Grad-CAM++では約37%の低下にとどまりました。これは、Grad-CAM++の方がGrad-CAMに比べて約10%高い予測精度を維持できていることを示しており、より効果的な重要領域の抽出が行えていると考えられます。

Increase in Confidence

こちらは重要領域と判定された以外の部分を隠した画像を用いて分類を行った際にどれほど精度が上昇したかを示すものです。なぜ精度が上がるかというと画像の分類において重要ではない部分を取り除くため誤判定が減るからです。Grad-CAMとGrad-CAM++のスコアを比較すると、Grad-CAMは1%未満の精度向上であるのに対して、Grad-CAM++は6.7%の精度向上を達成しています。このことからGrad-CAM++のほうがGrad-CAMと比較して画像分類において重要ではない部分を判別できているということになります。

まとめ

今回はGrad-CAM++について紹介しました。Grad-CAM++は現在でも研究の最前線で使われることのある画期的な手法であるためぜひ試してみてください。

\ シェア /

E資格スピードパッケージ2023#2修了者合格率100%達成

zero to one E資格 jdla

zero to oneの「E資格」向け認定プログラム

日本ディープラーニング協会の実施するE資格の受験ならzero to oneの「E資格」向け認定プログラム (税込165,000円) をおすすめします。当講座は、東京大学大学院工学系研究科の松尾豊教授と東北大学大学院情報科学研究科の岡谷貴之教授が監修する実践的なプログラムとなっています。
厚生労働省の教育訓練給付制度対象のE資格認定プログラムの中では最安値※となり、実質負担額49,500円~(支給割合70%の場合)で受講可能です。※2023年弊社調べ zero to one E資格 jdla

人工知能基礎講座を提供中

人工知能の第一人者である東京大学の松尾豊教授が監修した人工知能基礎講座を受講してみませんか? 人工知能の歴史から自然言語処理、機械学習、深層学習といった最先端のトピックやAIに関わる法律問題まで網羅しているので全てのビジネスパーソン・AIの初学者におすすめです。

サンプル動画

人工知能基礎講座はこちら↓ zero to one G検定 人工知能基礎 jdla

AI初学者・ビジネスパーソン向けのG検定対策講座

G検定受験前にトレーニングしたい方向けの問題集「G検定実践トレーニング」も提供中です。 zero to one E資格 jdla