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

使用自定义CNN实现Keras Grad-CAM时遇ValueError问题求助

问题描述

尝试在自定义CNN模型上使用Keras的Grad-CAM功能,参考官方示例实现了make_gradcam_heatmap函数,但运行时抛出错误:

ValueError: The layer sequential has never been called and thus has no defined output

输入是维度为(240,146)的numpy数组,原代码如下:

import numpy as np
import os
import tensorflow as tf
import keras
from tensorflow.keras.models import load_model
import cv2
from tensorflow.keras.models import Model

os.environ["KERAS_BACKEND"] = "tensorflow"

from IPython.display import Image, display
import matplotlib as mpl
import matplotlib.pyplot as plt

img_path = '/Users/.../image_1.npy'

model = load_model('/Users/.../particle_classifier_model.h5')

model_builder = keras.applications.xception.Xception
preprocess_input = keras.applications.xception.preprocess_input
decode_predictions = keras.applications.xception.decode_predictions


image = np.load(img_path)
img_size = image.shape # should be an array of shape (240, 146)

def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
    # First, we create a model that maps the input image to the activations
    # of the last conv layer as well as the output predictions
    grad_model = keras.models.Model(
        model.inputs, [model.get_layer(last_conv_layer_name).output, model.output]
    )

    # Then, we compute the gradient of the top predicted class for our input image
    # with respect to the activations of the last conv layer
    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]

    # This is the gradient of the output neuron (top predicted or chosen)
    # with regard to the output feature map of the last conv layer
    grads = tape.gradient(class_channel, last_conv_layer_output)

    # This is a vector where each entry is the mean intensity of the gradient
    # over a specific feature map channel
    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))

    # We multiply each channel in the feature map array
    # by "how important this channel is" with regard to the top predicted class
    # then sum all the channels to obtain the heatmap class activation
    last_conv_layer_output = last_conv_layer_output[0]
    heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
    heatmap = tf.squeeze(heatmap)

    # For visualization purpose, we will also normalize the heatmap between 0 & 1
    heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
    return heatmap.numpy()


last_conv_layer_name = "max_pooling2d_4"

image = image.reshape(1, 240, 146, 1)
preds = model.predict(image)

model.layers[-1].activation = None

heatmap = make_gradcam_heatmap(image, model, last_conv_layer_name)
plt.matshow(heatmap)
plt.show()
错误原因及修复方案

这个错误的核心是修改模型输出层激活函数后,未重新触发前向传播,导致模型计算图未正确初始化,以下是具体修复步骤:

  • 正确替换输出层激活函数
    直接修改model.layers[-1].activation不会更新模型的计算图,需要重新构建模型输出层:

    # 移除原输出层的激活函数,重新构建模型
    x = model.layers[-2].output
    # 保持原输出层的单元数,设置activation=None
    new_output = keras.layers.Dense(model.layers[-1].units, activation=None)(x)
    model = keras.models.Model(inputs=model.inputs, outputs=new_output)
    
  • 修改模型后重新执行前向传播
    新模型构建完成后,必须用输入数据做一次预测,让模型的计算图被正确调用:

    # 重新执行预测,初始化计算图
    preds = model.predict(image)
    
  • 确认最后卷积层的有效性
    Grad-CAM依赖卷积层的特征图,建议将last_conv_layer_name替换为模型中最后一个卷积层的名称(而非池化层)。可以通过model.summary()查看所有层的名称,找到类似conv2d_*的最后一层。

  • 清理无关代码
    代码中导入的Xception相关函数(model_builder、preprocess_input等)未被使用,可直接删除,避免混淆。

修复后的完整代码
import numpy as np
import os
import tensorflow as tf
import keras
from tensorflow.keras.models import load_model
import matplotlib as mpl
import matplotlib.pyplot as plt

os.environ["KERAS_BACKEND"] = "tensorflow"

img_path = '/Users/.../image_1.npy'
model = load_model('/Users/.../particle_classifier_model.h5')

# 加载输入数据
image = np.load(img_path)
# 调整为模型接受的输入形状:(batch_size, height, width, channels)
image = image.reshape(1, 240, 146, 1)

def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
    grad_model = keras.models.Model(
        model.inputs, [model.get_layer(last_conv_layer_name).output, model.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)
    heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
    return heatmap.numpy()

# 替换为你模型中实际的最后一个卷积层名称
last_conv_layer_name = "conv2d_xxx"

# 修复:重新构建输出层,移除激活函数
x = model.layers[-2].output
new_output = keras.layers.Dense(model.layers[-1].units, activation=None)(x)
model = keras.models.Model(inputs=model.inputs, outputs=new_output)

# 修复:重新执行预测,初始化模型计算图
preds = model.predict(image)

# 生成热力图并展示
heatmap = make_gradcam_heatmap(image, model, last_conv_layer_name)
plt.matshow(heatmap)
plt.show()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:35:58