[英]Solved: How to combine tf.gradients with tf.data.dataset and keras models
我正在尝试构建使用tf.data.dataset
批处理和迭代器的工作流。 出于性能原因,我实际上是在尝试避免使用占位符-> feed_dict循环工作流程。
我正在尝试实现的过程涉及grad-cam(这需要相对于CNN最终卷积层的损耗梯度)作为中间步骤,理想情况下,我希望能够对其进行尝试几种Keras预先训练的模型,包括非顺序模型,例如ResNet。
我发现,大多数grad-cam实现都依赖于对Tensorflow中感兴趣的CNN的手工制作。 我找到了一个针对keras模型的实现https://github.com/jacobgil/keras-grad-cam ,在该示例之后,我得到了
def safe_norm(x):
return x / tf.sqrt(tf.reduce_mean(x ** 2) + 1e-8)
vgg_ = VGG19()
dataset = tf.data.Dataset.from_tensor_slices((filenames))
#preprocessing...
it = dataset.make_one_shot_iterator()
files, batch = it.get_next()
conv5_4 = vgg_.layers[-6]
h_k, w_k, c_k = conv5_4.output.shape[1:]
vgg_model = Model(inputs=vgg_.input, outputs=vgg_.output)
conv_model = Model(inputs=vgg_.input, outputs=conv5_4.output)
probs = vgg_model(batch)
predicted_class = tf.argmax(probs, axis=-1)
layer_name = 'block5_conv4'
target_layer = lambda x: target_category_loss(x, predicted_class, n_categories)
x = Lambda(target_layer)(vgg_model.outputs[0])
model = Model(inputs=vgg_model.inputs[0], outputs=x)
loss = K.sum(model.output, axis=-1)
conv_output = [l for l in model.layers if l.name is layer_name][0].output
grads = Lambda(safe_norm)(K.gradients(loss, [conv_output])[0])
gradient_function = K.function([model.input], [conv_output, grads])
output, grads_val = gradient_function([batch])
weights = tf.reduce_mean(grads_val, axis = (1, 2))
cam = tf.ones([batch_size, h_k, w_k], dtype = tf.float32)
cam += tf.reduce_sum(output * tf.reshape(weights, [-1, 1, 1, weights.shape[-1]]), axis=-1)
cam = tf.squeeze(tf.image.resize_images(images=tf.expand_dims(cam, axis=-1), size=(224, 224)))
cam = tf.maximum(cam, 0)
heatmap = cam / tf.reshape(tf.reduce_max(cam, axis=[1, 2]), shape=[-1, 1, 1])
问题在于, gradient_function([batch])
返回一个numpy数组,其值由第一批决定,因此heatmap
不会随后heatmap
评估而改变。
我试图以各种方式用Model
替换K.function
,但是似乎没有任何效果。 我通常最终会或者与一个错误提示grads
计算结果为None
或者一个模型或另一期待一个feed_dict和不接收一个。
此代码可挽救吗? 除了多次遍历数据(一次获取所有grad-cams,然后再次拥有它们,然后再次使用)或使用占位符和feed_dicts之外,还有更好的方法吗?
编辑:
def safe_norm(x):
return x / tf.sqrt(tf.reduce_mean(x ** 2) + 1e-8)
vgg_ = VGG19()
dataset = tf.data.Dataset.from_tensor_slices((filenames))
#preprocessing...
it = dataset.make_one_shot_iterator()
files, batch = it.get_next()
conv5_4 = vgg_.layers[-6]
h_k, w_k, c_k = conv5_4.output.shape[1:]
vgg_model = Model(inputs=vgg_.input, outputs=vgg_.output)
conv_model = Model(inputs=vgg_.input, outputs=conv5_4.output)
probs = vgg_model(batch)
predicted_class = tf.argmax(probs, axis=-1)
layer_name = 'block5_conv4'
target_layer = lambda x: target_category_loss(x, predicted_class, n_categories)
x = Lambda(target_layer)(vgg_model.outputs[0])
model = Model(inputs=vgg_model.inputs[0], outputs=x)
loss = K.sum(model.output, axis=-1)
conv_output = [l for l in model.layers if l.name is layer_name][0].output
grads = Lambda(safe_norm)(K.gradients(loss, [conv_output])[0])
gradient_function = K.function([model.input], [conv_output, grads])
output, grads_val = gradient_function([batch])
weights = tf.reduce_mean(grads_val, axis = (1, 2))
cam = tf.ones([batch_size, h_k, w_k], dtype = tf.float32)
cam += tf.reduce_sum(output * tf.reshape(weights, [-1, 1, 1, weights.shape[-1]]), axis=-1)
cam = tf.squeeze(tf.image.resize_images(images=tf.expand_dims(cam, axis=-1), size=(224, 224)))
cam = tf.maximum(cam, 0)
heatmap = cam / tf.reshape(tf.reduce_max(cam, axis=[1, 2]), shape=[-1, 1, 1])
# other operations on heatmap and batch ...
# ...
output_function = K.function(model.input, [node1, ..., nodeN])
for batch in range(n_batches):
outputs1, ... , outputsN = output_function(batch)
给我每批所需的输出。
是的, K.function
返回numpy数组,因为它会评估图形中的符号计算。 我认为您应该做的是将所有符号保持在K.function
,并在获得梯度之后,使用numpy执行Grad-CAM权重和最终显着性图的所有计算。
然后,您可以对数据集进行迭代,对新的一批数据求gradient_function
函数,并计算显着性图。
如果要保留所有符号,则不应使用K.function
来生成梯度函数,而应使用符号梯度( K.gradient
的输出,不包含lambda)和卷积特征图( conv_output
)并执行显着图在此基础上进行计算,然后构建一个函数(使用K.function
)以获取模型输入,并输出显着性图。
希望解释够了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.