首页 > 解决方案 > 我的 CNN 中的激活看起来不正确 - 还是热图有问题?

问题描述

我正在为通过 Keras 制作的卷积神经网络制作热图,如此处所述。当我为香草VGG16网络运行该算法时,热图看起来不错:

在此处输入图像描述

然后我创建了自己的自定义模型,基于该VGG16网络,但使用自定义顶层:

input_layer = layers.Input(shape=(img_size, img_size, 3), name="model_input")
vgg16_base = VGG16(weights="imagenet", include_top=False, input_tensor=input_layer)
temp_model = vgg16_base.output
temp_model = layers.Flatten()(temp_model)
temp_model = layers.Dense(256, activation="relu")(temp_model)
temp_model = layers.Dense(1, activation="sigmoid")(temp_model)
custom = models.Model(inputs=input_layer, outputs=temp_model)

但是,当我使用相同的代码/函数为我自己的自定义网络的同一层(即来自VGG16基础的最后一个转换层,作为我的新网络的一部分)生成热图时,热图看起来不正确:

在此处输入图像描述

我的自定义网络的验证/测试准确度为 97-98%,所以我认为它工作正常。那么激活/热图怎么会如此关闭呢?还是我在这里想念其他东西?

PS:供您参考,热图是通过此处列出的函数创建的。它是这样调用的:

# Load the image from disk and preprocess it via Keras tools
img_path = "/path/to/image.jpg"
img = image.load_img(img_path, target_size=(224, 224))
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)
img_tensor = preprocess_input(img_tensor)

# At this point I either load the VGG16 model directly (heatmapü works),
# or I create my own custom VGG16-based model (heatmap does not work)
# The model itself is then stored into the variable "model"

preds = model.predict(img_tensor)
model_prediction = model.output[:, np.argmax(preds[0])]

# Then I call the custom function referred to above
input_layer = model.get_layer("model_input")
conv_layer = model.get_layer("block5_conv3")
plot_conv_heat_map(model_prediction, input_layer, conv_layer, img_tensor, img_path)

标签: pythonmachine-learningkerasconv-neural-networkheatmap

解决方案


简短回答:由于您在训练阶段将狗标记为 1,将猫标记为 0,因此您需要替换model_prediction1 - model_prediction以查找与猫相关的区域:

plot_conv_heat_map(1 - model_prediction, ...)

长答案:当您使用原始 VGG 模型时,最后一层有 1000 个神经元(假设您使用的是预训练的 ImageNet 模型),每个神经元对应 1000 个不同的类:

# last layer in VGG model
x = layers.Dense(classes, activation='softmax', name='predictions')(x)

这些神经元中的每一个都有一个从零到一的输出值(条件是输出之和必须为一)。所以最活跃的神经元(即输出最高的神经元)对应于预测的类别。所以你会发现它是这样的:

model_prediction = model.output[:, np.argmax(preds[0])]
                                        \
                                         \___ finds the index of the neuron with maximum output

然后将其传递给可视化函数以计算其相对于所选卷积层的梯度并可视化热图:

plot_conv_heat_map(model_prediction, ...)

到目前为止,一切都很好。但是,在您的自定义模型中,您已将问题从多类分类任务转换为二元分类任务,即狗与猫。您正在使用具有一个单一单元的 sigmoid 层作为最后一层,并将神经元的活动状态(即接近 1 的输出)视为狗,将神经元的非活动状态(即接近 0 的输出)视为猫。所以你的网络本质上是一个狗检测器,如果没有狗,那么我们假设图像中有一只猫。

好吧,你可能会问“这有什么问题?” 答案是模型的训练没有问题,而且正如你所建议的,你得到了很好的训练准确性。但是,请记住可视化函数背后的假设:它将具有最高输出的神经元作为输入,该神经元对应于图像中检测到的类别。所以,给你的自定义模型一个猫图像,最后一层的输出将是一个非常低的数字,比如 0.01。所以对这个数字的一​​种解释是,这张图片是狗的概率是 0.01。那么当你将它直接提供给你的可视化函数时会发生什么?是的,你猜对了:它会找到图像中与狗最相关的所有区域!你可能仍然反对“但我给了它一个猫的形象!!!” 不要紧,由于该神经元在狗出现时激活,因此当您获取其相对于卷积层的梯度时,与狗最相关的区域将被高度表示并显示在热图中。但是,如果您为模型提供狗图像,则可视化将是正确的。

“那么当我们想要可视化与猫最相关的区域时应该怎么做?” 很简单:只要把那个神经元变成猫探测器。“如何?” 只需创建它的补码:1 - model_prediction. 这为您提供了图像中出现猫的概率。您可以像这样轻松地使用它来绘制图像中与猫相关的区域:

plot_conv_heat_map(1 - model_prediction, ...)

或者,您可以将模型的最后一层更改为具有 2 个softmax激活的神经元,然后重新训练它:

temp_model = layers.Dense(2, activation="softmax")(temp_model)

这样,每个类别,即狗和猫,都有自己的神经元,因此在可视化激活热图时不会出现问题。


推荐阅读