如何在TensorFlow的ResNet152V2图像分类模型中实现Grad-CAM以查看激活图/热力图
如何在TensorFlow的ResNet152V2图像分类模型中实现Grad-CAM以查看激活图/热力图
嘿,看起来你已经在基于ResNet152V2的自闭症/非自闭症图像分类项目上取得了不错的进展!想要给你的模型加上Grad-CAM来可视化模型关注的核心区域,这绝对是个能帮你直观理解模型决策逻辑的好想法。我会把实现步骤拆解开来,直接整合到你现有的Train-Predict.py脚本里,你可以照着调整:
第一步:明确Grad-CAM需要用到的模型层
Grad-CAM的核心是利用模型最后一个卷积层的激活输出,结合预测类别的梯度来生成热力图。对于ResNet152V2来说,它的最后一个卷积层默认名称是conv5_block3_out,你可以通过model.summary()输出模型结构来确认这个名称。
第二步:添加Grad-CAM核心函数
在你的脚本导入库的代码之后,或者训练/预测逻辑之前,加入下面这两个核心函数:
def grad_cam(model, img_array, last_conv_layer_name="conv5_block3_out", pred_index=None): # 构建能同时输出最后卷积层激活和模型预测结果的新模型 grad_model = tf.keras.models.Model( [model.inputs], [model.get_layer(last_conv_layer_name).output, model.output] ) # 用GradientTape记录梯度信息 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(), preds[0] def overlay_heatmap(heatmap, img, alpha=0.4, colormap=cv2.COLORMAP_JET): # 把热力图放大到和原图一致的尺寸 heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) # 转成RGB格式并应用颜色映射 heatmap = np.uint8(255 * heatmap) heatmap = cv2.applyColorMap(heatmap, colormap) # 将热力图叠加到原图上 superimposed_img = cv2.addWeighted(img, alpha, heatmap, 1 - alpha, 0) return superimposed_img
第三步:整合到你的预测流程中
假设你已经加载了训练好的模型(或者训练完成后),可以添加一个带Grad-CAM可视化的预测函数:
def predict_with_gradcam(model, img_path): # 加载并预处理测试图片 img = cv2.imread(img_path) img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_resized = cv2.resize(img_rgb, (img_size, img_size)) img_array = np.expand_dims(img_resized, axis=0) / 255.0 # 和训练时的归一化逻辑保持一致 # 生成热力图和预测结果 heatmap, preds = grad_cam(model, img_array) class_names = ["非自闭症", "自闭症"] # 根据你的数据集类别调整 pred_class = class_names[np.argmax(preds)] pred_confidence = np.max(preds) * 100 # 把热力图叠加到原图上 superimposed_img = overlay_heatmap(heatmap, img_rgb) # 可视化三张图:原图、热力图、叠加后的图 plt.figure(figsize=(12, 6)) plt.subplot(1, 3, 1) plt.imshow(img_rgb) plt.title(f"原图\n预测结果: {pred_class} ({pred_confidence:.2f}%)") plt.axis("off") plt.subplot(1, 3, 2) plt.imshow(heatmap, cmap="jet") plt.title("Grad-CAM热力图") plt.axis("off") plt.subplot(1, 3, 3) plt.imshow(cv2.cvtColor(superimposed_img, cv2.COLOR_BGR2RGB)) plt.title("叠加热力图后的原图") plt.axis("off") plt.show() return pred_class, pred_confidence
然后在你的脚本预测逻辑部分(比如training=False时),这样调用即可:
# 加载训练好的模型(和你训练时的模型结构保持一致) base_model = ResNet152V2(weights=None, include_top=False, input_shape=(img_size, img_size, 3)) model = Sequential([ base_model, GlobalAveragePooling2D(), BatchNormalization(), Dropout(0.5), Dense(128, activation='relu'), BatchNormalization(), Dropout(0.5), Dense(2, activation='softmax') # 二分类任务,输出单元数为2 ]) model.load_weights("trained_weights.hdf5") # 替换成你的测试图片路径,运行即可看到可视化结果 test_img_path = "path/to/your/test/image.jpg" predict_with_gradcam(model, test_img_path)
一些小提醒
- 确认卷积层名称:如果你的模型结构有修改,记得通过
model.summary()确认最后一个卷积层的名称,替换到grad_cam函数的last_conv_layer_name参数里。 - 预处理对齐:确保测试图片的预处理逻辑(比如归一化、尺寸调整)和训练时完全一致,否则热力图可能不准确。
- 多分类适配:如果是多分类任务,只需要调整
class_names列表和模型最后一层Dense的输出单元数即可。
这样修改后,你就能在预测时直观看到模型到底在关注图片的哪些区域,帮你验证模型的决策是否合理~
备注:内容来源于stack exchange,提问作者unknown name
相关产品推荐
相关产品推荐

