You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于ResNet50V2的Grad-CAM无法生成热力图,求问题排查

3分类图像模型Grad-CAM热力图无法显示问题

我训练了一个用于3分类任务的图像分类模型,参考Keras官网的Grad-CAM教程操作,步骤如下:

加载模型

RN50V2 = tf.keras.models.load_model('/content/drive/MyDrive/Grad_CAM_Test/ResNet50V2_/RN50V2.h5')

通过RN50V2.summary()确定最后卷积层名称为last_conv_layer_name = 'conv5_block3_3_conv',随后加载测试集:

batch_size = 32
test_dataset = tf.keras.utils.image_dataset_from_directory(
    '/content/drive/MyDrive/Test_150',
    image_size = (224, 224),
    batch_size = batch_size,
    shuffle = False
)

遍历测试数据集批次

images_batch = [] # 存储测试集图像的列表
labels_batch = [] # 存储测试集标签的列表
for dataset in test_dataset.unbatch():
  image_batchh, label_batchh = dataset
  images = image_batchh.numpy() # 将图像张量转为numpy数组
  labels = label_batchh.numpy() # 将标签张量转为numpy数组
  images_batch.append(images)
  labels_batch.append(labels)

准备测试图像

将测试集中的第一张图像转换为模型输入格式:

array_of_img = keras.preprocessing.image.img_to_array(images_batch[0]) # 转为数组
array_of_img = np.expand_dims(array_of_img, axis=0) # 形状变为(1,224,224,3)
print(array_of_img)

Grad-CAM算法实现

def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
    # 创建映射输入到最后卷积层激活和输出预测的模型
    grad_model = tf.keras.models.Model(
        [RN50V2.inputs], [RN50V2.get_layer('conv5_block3_3_conv').output, RN50V2.output]
    )

    # 计算输入图像对应最高预测类别的梯度,相对于最后卷积层的激活
    with tf.GradientTape() as tape:
        last_conv_layer_output, preds = grad_model(img_array)
        if pred_index is None:
            pred_index = tf.argmax(preds[0])
        class_channel = preds[:, pred_index]

    # 输出神经元相对于最后卷积层特征图的梯度
    grads = tape.gradient(class_channel, last_conv_layer_output)

    # 计算每个特征图通道的梯度均值
    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))

    # 加权求和特征图通道得到热力图
    last_conv_layer_output = last_conv_layer_output[0]
    heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
    heatmap = tf.squeeze(heatmap)

    # 归一化热力图到0-1区间
    heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
    return heatmap.numpy()

测试Grad-CAM

# 准备输入图像
img_array = array_of_img

# 加载模型
model = RN50V2

# 移除最后一层的softmax激活
model.layers[-1].activation = None

# 打印最高预测类别
preds = model.predict(img_array)

# 生成热力图
heatmap = make_gradcam_heatmap(img_array, RN50V2, 'conv5_block3_3_conv')

# 显示热力图
plt.matshow(heatmap)
plt.show()

测试后无法看到最后卷积层的热力图,请问哪里操作出错了?


问题排查与解决建议

  1. Grad-CAM函数硬编码模型实例
    你在make_gradcam_heatmap函数里创建grad_model时,直接使用了全局的RN50V2,而非函数参数传入的model。这会导致函数无法复用,且如果后续对传入的model做了修改(比如移除softmax),函数内部依然使用原始模型,梯度计算会出现偏差。修改方式:

    grad_model = tf.keras.models.Model(
        [model.inputs], [model.get_layer(last_conv_layer_name).output, model.output]
    )
    
  2. 图像预处理与训练流程不匹配
    ResNet50V2训练时通常需要特定预处理(比如将像素值归一化到[0,1],或使用ImageNet均值方差标准化)。你直接将图像转成数组就输入模型,若预处理和训练时不一致,模型预测结果会异常,进而导致热力图无有效信息。检查训练代码中的预处理步骤,比如训练时如果用了:

    preprocess_input = tf.keras.applications.resnet_v2.preprocess_input
    

    测试时需对array_of_img做同样处理:

    array_of_img = preprocess_input(array_of_img)
    
  3. 移除softmax的操作无效
    直接修改model.layers[-1].activation = None不会自动更新模型计算图,因为模型已经编译过。实际上Grad-CAM不需要移除softmax——softmax是单调递增函数,不会改变梯度的相对大小,这一步完全可以省略。

  4. 检查热力图数值是否异常
    在显示热力图前,打印heatmap的数值范围和内容:

    print(heatmap.min(), heatmap.max())
    print(heatmap)
    

    如果数值全为0或接近0,说明梯度计算出现问题,大概率是模型输入预处理错误,或是最后卷积层选择有误。

  5. 确认GradientTape正确追踪张量
    确保last_conv_layer_output在tf.GradientTape作用域内是可追踪的张量。若模型加载时设置了run_eagerly=False(默认),通常不会有问题,但如果有自定义层,需检查层的可追踪性。


内容的提问来源于stack exchange,提问作者Jaturong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 15:40:28