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

R语言中Keras模型GradCAM可视化报错问题求助

在R语言中用Keras实现GradCAM(解决Eager执行相关错误)

问题根源

你遇到的两个错误本质是TensorFlow版本API适配问题:

  • TF2默认启用即时执行(Eager Execution),tf$gradients是TF1的静态图API,在Eager模式下无法使用,必须改用tf$GradientTape追踪梯度。
  • 禁用Eager后切换到TF1静态图模式,Tensor对象的属性逻辑和Eager模式完全不同,导致gradient属性不存在,反而增加实现复杂度。

正确的做法是基于TF2的Eager模式实现GradCAM,以下是完整可运行的代码示例:


1. 加载依赖库与数据

library(keras)
library(tensorflow)
library(imager)

# 加载Fashion MNIST数据集
fashion_mnist <- dataset_fashion_mnist()
c(train_images, train_labels) %<-% fashion_mnist$train
c(test_images, test_labels) %<-% fashion_mnist$test

# 预处理:添加通道维度、归一化
train_images <- array_reshape(train_images, c(nrow(train_images), 28, 28, 1)) / 255
test_images <- array_reshape(test_images, c(nrow(test_images), 28, 28, 1)) / 255

# 类别名称
class_names <- c("T-shirt/top", "Trouser", "Pullover", "Dress", "Coat",
                 "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot")

2. 构建带可访问卷积层的模型

GradCAM需要获取最后一层卷积层的输出和梯度,因此要给最后卷积层命名:

# 定义模型结构
input <- layer_input(shape = c(28, 28, 1))
x <- input %>%
  layer_conv_2d(filters = 32, kernel_size = 3, activation = "relu") %>%
  layer_max_pooling_2d(pool_size = 2) %>%
  layer_conv_2d(filters = 64, kernel_size = 3, activation = "relu") %>%
  layer_max_pooling_2d(pool_size = 2) %>%
  layer_conv_2d(filters = 128, kernel_size = 3, activation = "relu", name = "last_conv_layer") # 标记最后卷积层

# 分类头部
x_class <- x %>%
  layer_flatten() %>%
  layer_dense(units = 128, activation = "relu") %>%
  layer_dense(units = 10, activation = "softmax")

model <- keras_model(inputs = input, outputs = x_class)

# 编译并训练模型
model %>% compile(
  optimizer = "adam",
  loss = "sparse_categorical_crossentropy",
  metrics = "accuracy"
)

model %>% fit(train_images, train_labels, epochs = 5, validation_split = 0.1)

3. 实现GradCAM核心逻辑

用tf$GradientTape追踪梯度,计算热力图:

grad_cam <- function(model, img_array, class_index, last_conv_layer_name = "last_conv_layer") {
  # 创建包含最后卷积层输出和模型预测的新模型
  last_conv_layer <- model$get_layer(last_conv_layer_name)
  grad_model <- keras_model(inputs = model$input, outputs = list(last_conv_layer$output, model$output))
  
  # 用GradientTape记录梯度计算过程
  with(tf$GradientTape() %as% tape, {
    inputs <- tf$cast(img_array, tf$float32)
    c(last_conv_output, preds) %<-% grad_model(inputs)
    class_channel <- preds[,,,class_index] # 目标类别的预测值
  })
  
  # 获取目标类别对最后卷积层输出的梯度
  grads <- tape$gradient(class_channel, last_conv_output)
  
  # 计算梯度的全局平均池化
  pooled_grads <- tf$reduce_mean(grads, axis = c(1, 2, 3))
  
  # 将卷积层输出与池化梯度加权求和,得到热力图
  last_conv_output <- last_conv_output[1,,,] # 取单样本的卷积输出
  heatmap <- last_conv_output * pooled_grads$numpy()
  heatmap <- tf$reduce_sum(heatmap, axis = -1)
  
  # 归一化热力图到0-1区间
  heatmap <- tf$max(heatmap, 0) / tf$reduce_max(heatmap)
  return(heatmap$numpy())
}

4. 可视化热力图与原图叠加

plot_grad_cam <- function(img, heatmap, alpha = 0.4) {
  # 将单通道灰度图转为RGB
  img <- array_reshape(img, c(28, 28)) %>% as.cimg() %>% grayscale_to_rgb()
  
  # 调整热力图尺寸匹配原图
  heatmap <- resize(as.cimg(heatmap), size_x = 28, size_y = 28)
  
  # 给热力图添加jet颜色映射
  heatmap <- heatmap %>% normalize() %>% map_jet()
  
  # 叠加原图与热力图
  superimposed_img <- img * (1 - alpha) + heatmap * alpha
  
  # 显示结果
  plot(superimposed_img, axes = FALSE)
}

5. 测试运行

# 选择一个测试样本
img_index <- 1
img <- test_images[img_index,,,]
img_array <- array_reshape(img, c(1, 28, 28, 1))

# 获取模型预测的类别
preds <- model %>% predict(img_array)
pred_class <- which.max(preds[1,]) - 1 # 转换为TF的0索引类别

# 生成GradCAM热力图
heatmap <- grad_cam(model, img_array, pred_class)

# 对比显示原图与热力图
par(mfrow = c(1, 2))
plot(as.cimg(img), main = paste("原图\n预测类别:", class_names[pred_class + 1]), axes = FALSE)
plot_grad_cam(img, heatmap, alpha = 0.5)
title(main = "GradCAM热力图")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 07:15:34