在 Tensorflow 2.0 中使用带引导反向传播的 Gradcam 进行迁移学习
Gradcam with guided backprop for transfer learning in Tensorflow 2.0
我在 TF 2.0 中使用带有迁移学习的梯度可视化时出错。梯度可视化适用于不使用迁移学习的模型。
当我 运行 我的代码出现错误时:
assert str(id(x)) in tensor_dict, 'Could not compute output ' + str(x)
AssertionError: Could not compute output Tensor("block5_conv3/Identity:0", shape=(None, 14, 14, 512), dtype=float32)
当我运行下面的代码出错时。我认为命名约定或将基本模型 vgg16 的输入和输出连接到我要添加的层存在问题。非常感谢您的帮助!
"""
Broken example when grad_model is created.
"""
!pip uninstall tensorflow
!pip install tensorflow==2.0.0
import cv2
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
import matplotlib.pyplot as plt
IMAGE_PATH = '/content/cat.3.jpg'
LAYER_NAME = 'block5_conv3'
model_layer = 'vgg16'
CAT_CLASS_INDEX = 281
imsize = (224,224,3)
img = tf.keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(224, 224))
plt.figure()
plt.imshow(img)
img = tf.io.read_file(IMAGE_PATH)
img = tf.image.decode_jpeg(img)
img = tf.cast(img, dtype=tf.float32)
# img = tf.keras.preprocessing.image.img_to_array(img)
img = tf.image.resize(img, (224,224))
img = tf.reshape(img, (1, 224,224,3))
input = layers.Input(shape=(imsize[0], imsize[1], imsize[2]))
base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet',
input_shape=(imsize[0], imsize[1], imsize[2]))
# base_model.trainable = False
flat = layers.Flatten()
dropped = layers.Dropout(0.5)
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
fc1 = layers.Dense(16, activation='relu', name='dense_1')
fc2 = layers.Dense(16, activation='relu', name='dense_2')
fc3 = layers.Dense(128, activation='relu', name='dense_3')
prediction = layers.Dense(2, activation='softmax', name='output')
for layr in base_model.layers:
if ('block5' in layr.name):
layr.trainable = True
else:
layr.trainable = False
x = base_model(input)
x = global_average_layer(x)
x = fc1(x)
x = fc2(x)
x = prediction(x)
model = tf.keras.models.Model(inputs = input, outputs = x)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
这部分代码是错误所在。我不确定标记输入和输出的正确方法是什么。
# Create a graph that outputs target convolution and output
grad_model = tf.keras.models.Model(inputs = [model.input, model.get_layer(model_layer).input],
outputs=[model.get_layer(model_layer).get_layer(LAYER_NAME).output,
model.output])
print(model.get_layer(model_layer).get_layer(LAYER_NAME).output)
# Get the score for target class
# Get the score for target class
with tf.GradientTape() as tape:
conv_outputs, predictions = grad_model(img)
loss = predictions[:, 1]
以下部分用于绘制 gradcam 的热图。
print('Prediction shape:', predictions.get_shape())
# Extract filters and gradients
output = conv_outputs[0]
grads = tape.gradient(loss, conv_outputs)[0]
# Apply guided backpropagation
gate_f = tf.cast(output > 0, 'float32')
gate_r = tf.cast(grads > 0, 'float32')
guided_grads = gate_f * gate_r * grads
# Average gradients spatially
weights = tf.reduce_mean(guided_grads, axis=(0, 1))
# Build a ponderated map of filters according to gradients importance
cam = np.ones(output.shape[0:2], dtype=np.float32)
for index, w in enumerate(weights):
cam += w * output[:, :, index]
# Heatmap visualization
cam = cv2.resize(cam.numpy(), (224, 224))
cam = np.maximum(cam, 0)
heatmap = (cam - cam.min()) / (cam.max() - cam.min())
cam = cv2.applyColorMap(np.uint8(255 * heatmap), cv2.COLORMAP_JET)
output_image = cv2.addWeighted(cv2.cvtColor(img.astype('uint8'), cv2.COLOR_RGB2BGR), 0.5, cam, 1, 0)
plt.figure()
plt.imshow(output_image)
plt.show()
我也在 github https://github.com/tensorflow/tensorflow/issues/37680 上向 tensorflow 团队询问了这个问题。
我明白了。如果你设置模型扩展 vgg16 基础模型与你自己的层,而不是像层一样将基础模型插入到新模型中,那么它就可以工作。
首先设置模型,一定要声明input_tensor。
inp = layers.Input(shape=(imsize[0], imsize[1], imsize[2]))
base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet', input_tensor=inp,
input_shape=(imsize[0], imsize[1], imsize[2]))
这样我们就不必包含像 x=base_model(inp)
这样的行来显示我们想要输入的内容。这已经包含在 tf.keras.applications.VGG16(...)
.
中
与其将此 vgg16 基础模型放入另一个模型,不如通过向基础模型本身添加层来更容易地进行 gradcam。我抓取了 VGG16 最后一层的输出(去掉了顶部),也就是池化层。
block5_pool = base_model.get_layer('block5_pool')
x = global_average_layer(block5_pool.output)
x = fc1(x)
x = prediction(x)
model = tf.keras.models.Model(inputs = inp, outputs = x)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
现在,我抓取图层进行可视化,LAYER_NAME='block5_conv3'
。
# Create a graph that outputs target convolution and output
grad_model = tf.keras.models.Model(inputs = [model.input],
outputs=[model.output, model.get_layer(LAYER_NAME).output])
print(model.get_layer(LAYER_NAME).output)
# Get the score for target class
# Get the score for target class
with tf.GradientTape() as tape:
predictions, conv_outputs = grad_model(img)
loss = predictions[:, 1]
print('Prediction shape:', predictions.get_shape())
# Extract filters and gradients
output = conv_outputs[0]
grads = tape.gradient(loss, conv_outputs)[0]
我们(我和一些开发项目的团队成员)发现了一个与我们在 tutorial.
中发现的实现 Grad-CAM 的代码类似的问题
该代码不适用于由 VGG19 基本模型加上在其上添加的一些额外层组成的模型。问题是 VGG19 基础模型作为 "layer" 插入到我们的模型中,显然 GradCAM 代码不知道如何处理它 - 我们遇到了 "Graph disconnected..." 错误。然后经过一些调试(由另一个团队成员进行,而不是我),我们设法修改了原始代码,使其适用于这种包含另一个模型的模型。这个想法是将内部模型添加为 class GradCAM 的额外参数。因为这可能对其他人有帮助,所以我在下面包含修改后的代码(我们还将 GradCAM class 重命名为 My_GradCAM)。
class My_GradCAM:
def __init__(self, model, classIdx, inner_model=None, layerName=None):
self.model = model
self.classIdx = classIdx
self.inner_model = inner_model
if self.inner_model == None:
self.inner_model = model
self.layerName = layerName
[...]
gradModel = tensorflow.keras.models.Model(inputs=[self.inner_model.inputs],
outputs=[self.inner_model.get_layer(self.layerName).output,
self.inner_model.output])
然后 class 可以通过添加内部模型作为额外参数来实例化,例如:
cam = My_GradCAM(model, None, inner_model=model.get_layer("vgg19"), layerName="block5_pool")
希望对您有所帮助。
编辑: 感谢 Mirtha Lucas 进行调试并找到解决方案。
经过一番折腾,总结出大家使用迁移学习时绘制热图的方法。这是keras official tutorial
The issue I encounter is that when I'm trying to draw the heat map
from my model, the densenet can be only seen as functional layer in my
model. So the make_gradcam_heatmap can not figure out the layer that
inside functional layer. As the 5th layer shows.
因此,为了模拟Keras官方文档,我只需要使用densenet作为模型进行可视化即可。这是步骤
只从你的模型中取出模型
dense_model = dense_model.get_layer('densenet121')
将权重从密集模型复制到新启动的模型
inputs = tf.keras.Input(shape=(224, 224, 3))
model = model_builder(weights="imagenet", include_top=True, input_tensor=inputs)
for layer, dense_layer in zip(model.layers[1:], dense_model.layers[1:]):
layer.set_weights(dense_layer.get_weights())
relu = model.get_layer('relu')
x = tf.keras.layers.GlobalAveragePooling2D()(relu.output)
outputs = tf.keras.layers.Dense(5)(x)
model = tf.keras.models.Model(inputs = inputs, outputs = outputs)
绘制热图
preprocess_input = keras.applications.densenet.preprocess_input
img_array = preprocess_input(get_img_array(img_path, size=(224, 224)))
heatmap = make_gradcam_heatmap(img_array, model, 'bn')
plt.matshow(heatmap)
plt.show()
get_img_array
、make_gradcam_heatmap
和 save_and_display_gradcam
保持不变。按照 keras 教程进行操作,然后你就可以开始了。
我在 TF 2.0 中使用带有迁移学习的梯度可视化时出错。梯度可视化适用于不使用迁移学习的模型。
当我 运行 我的代码出现错误时:
assert str(id(x)) in tensor_dict, 'Could not compute output ' + str(x)
AssertionError: Could not compute output Tensor("block5_conv3/Identity:0", shape=(None, 14, 14, 512), dtype=float32)
当我运行下面的代码出错时。我认为命名约定或将基本模型 vgg16 的输入和输出连接到我要添加的层存在问题。非常感谢您的帮助!
"""
Broken example when grad_model is created.
"""
!pip uninstall tensorflow
!pip install tensorflow==2.0.0
import cv2
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
import matplotlib.pyplot as plt
IMAGE_PATH = '/content/cat.3.jpg'
LAYER_NAME = 'block5_conv3'
model_layer = 'vgg16'
CAT_CLASS_INDEX = 281
imsize = (224,224,3)
img = tf.keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(224, 224))
plt.figure()
plt.imshow(img)
img = tf.io.read_file(IMAGE_PATH)
img = tf.image.decode_jpeg(img)
img = tf.cast(img, dtype=tf.float32)
# img = tf.keras.preprocessing.image.img_to_array(img)
img = tf.image.resize(img, (224,224))
img = tf.reshape(img, (1, 224,224,3))
input = layers.Input(shape=(imsize[0], imsize[1], imsize[2]))
base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet',
input_shape=(imsize[0], imsize[1], imsize[2]))
# base_model.trainable = False
flat = layers.Flatten()
dropped = layers.Dropout(0.5)
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
fc1 = layers.Dense(16, activation='relu', name='dense_1')
fc2 = layers.Dense(16, activation='relu', name='dense_2')
fc3 = layers.Dense(128, activation='relu', name='dense_3')
prediction = layers.Dense(2, activation='softmax', name='output')
for layr in base_model.layers:
if ('block5' in layr.name):
layr.trainable = True
else:
layr.trainable = False
x = base_model(input)
x = global_average_layer(x)
x = fc1(x)
x = fc2(x)
x = prediction(x)
model = tf.keras.models.Model(inputs = input, outputs = x)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
这部分代码是错误所在。我不确定标记输入和输出的正确方法是什么。
# Create a graph that outputs target convolution and output
grad_model = tf.keras.models.Model(inputs = [model.input, model.get_layer(model_layer).input],
outputs=[model.get_layer(model_layer).get_layer(LAYER_NAME).output,
model.output])
print(model.get_layer(model_layer).get_layer(LAYER_NAME).output)
# Get the score for target class
# Get the score for target class
with tf.GradientTape() as tape:
conv_outputs, predictions = grad_model(img)
loss = predictions[:, 1]
以下部分用于绘制 gradcam 的热图。
print('Prediction shape:', predictions.get_shape())
# Extract filters and gradients
output = conv_outputs[0]
grads = tape.gradient(loss, conv_outputs)[0]
# Apply guided backpropagation
gate_f = tf.cast(output > 0, 'float32')
gate_r = tf.cast(grads > 0, 'float32')
guided_grads = gate_f * gate_r * grads
# Average gradients spatially
weights = tf.reduce_mean(guided_grads, axis=(0, 1))
# Build a ponderated map of filters according to gradients importance
cam = np.ones(output.shape[0:2], dtype=np.float32)
for index, w in enumerate(weights):
cam += w * output[:, :, index]
# Heatmap visualization
cam = cv2.resize(cam.numpy(), (224, 224))
cam = np.maximum(cam, 0)
heatmap = (cam - cam.min()) / (cam.max() - cam.min())
cam = cv2.applyColorMap(np.uint8(255 * heatmap), cv2.COLORMAP_JET)
output_image = cv2.addWeighted(cv2.cvtColor(img.astype('uint8'), cv2.COLOR_RGB2BGR), 0.5, cam, 1, 0)
plt.figure()
plt.imshow(output_image)
plt.show()
我也在 github https://github.com/tensorflow/tensorflow/issues/37680 上向 tensorflow 团队询问了这个问题。
我明白了。如果你设置模型扩展 vgg16 基础模型与你自己的层,而不是像层一样将基础模型插入到新模型中,那么它就可以工作。 首先设置模型,一定要声明input_tensor。
inp = layers.Input(shape=(imsize[0], imsize[1], imsize[2]))
base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet', input_tensor=inp,
input_shape=(imsize[0], imsize[1], imsize[2]))
这样我们就不必包含像 x=base_model(inp)
这样的行来显示我们想要输入的内容。这已经包含在 tf.keras.applications.VGG16(...)
.
与其将此 vgg16 基础模型放入另一个模型,不如通过向基础模型本身添加层来更容易地进行 gradcam。我抓取了 VGG16 最后一层的输出(去掉了顶部),也就是池化层。
block5_pool = base_model.get_layer('block5_pool')
x = global_average_layer(block5_pool.output)
x = fc1(x)
x = prediction(x)
model = tf.keras.models.Model(inputs = inp, outputs = x)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
现在,我抓取图层进行可视化,LAYER_NAME='block5_conv3'
。
# Create a graph that outputs target convolution and output
grad_model = tf.keras.models.Model(inputs = [model.input],
outputs=[model.output, model.get_layer(LAYER_NAME).output])
print(model.get_layer(LAYER_NAME).output)
# Get the score for target class
# Get the score for target class
with tf.GradientTape() as tape:
predictions, conv_outputs = grad_model(img)
loss = predictions[:, 1]
print('Prediction shape:', predictions.get_shape())
# Extract filters and gradients
output = conv_outputs[0]
grads = tape.gradient(loss, conv_outputs)[0]
我们(我和一些开发项目的团队成员)发现了一个与我们在 tutorial.
中发现的实现 Grad-CAM 的代码类似的问题该代码不适用于由 VGG19 基本模型加上在其上添加的一些额外层组成的模型。问题是 VGG19 基础模型作为 "layer" 插入到我们的模型中,显然 GradCAM 代码不知道如何处理它 - 我们遇到了 "Graph disconnected..." 错误。然后经过一些调试(由另一个团队成员进行,而不是我),我们设法修改了原始代码,使其适用于这种包含另一个模型的模型。这个想法是将内部模型添加为 class GradCAM 的额外参数。因为这可能对其他人有帮助,所以我在下面包含修改后的代码(我们还将 GradCAM class 重命名为 My_GradCAM)。
class My_GradCAM:
def __init__(self, model, classIdx, inner_model=None, layerName=None):
self.model = model
self.classIdx = classIdx
self.inner_model = inner_model
if self.inner_model == None:
self.inner_model = model
self.layerName = layerName
[...]
gradModel = tensorflow.keras.models.Model(inputs=[self.inner_model.inputs],
outputs=[self.inner_model.get_layer(self.layerName).output,
self.inner_model.output])
然后 class 可以通过添加内部模型作为额外参数来实例化,例如:
cam = My_GradCAM(model, None, inner_model=model.get_layer("vgg19"), layerName="block5_pool")
希望对您有所帮助。
编辑: 感谢 Mirtha Lucas 进行调试并找到解决方案。
经过一番折腾,总结出大家使用迁移学习时绘制热图的方法。这是keras official tutorial
The issue I encounter is that when I'm trying to draw the heat map from my model, the densenet can be only seen as functional layer in my model. So the make_gradcam_heatmap can not figure out the layer that inside functional layer. As the 5th layer shows.
因此,为了模拟Keras官方文档,我只需要使用densenet作为模型进行可视化即可。这是步骤
只从你的模型中取出模型
dense_model = dense_model.get_layer('densenet121')
将权重从密集模型复制到新启动的模型
inputs = tf.keras.Input(shape=(224, 224, 3)) model = model_builder(weights="imagenet", include_top=True, input_tensor=inputs) for layer, dense_layer in zip(model.layers[1:], dense_model.layers[1:]): layer.set_weights(dense_layer.get_weights()) relu = model.get_layer('relu') x = tf.keras.layers.GlobalAveragePooling2D()(relu.output) outputs = tf.keras.layers.Dense(5)(x) model = tf.keras.models.Model(inputs = inputs, outputs = outputs)
绘制热图
preprocess_input = keras.applications.densenet.preprocess_input img_array = preprocess_input(get_img_array(img_path, size=(224, 224))) heatmap = make_gradcam_heatmap(img_array, model, 'bn') plt.matshow(heatmap) plt.show()
get_img_array
、make_gradcam_heatmap
和save_and_display_gradcam
保持不变。按照 keras 教程进行操作,然后你就可以开始了。