【问题标题】:Solved: How to combine tf.gradients with tf.data.dataset and keras models已解决:如何将 tf.gradients 与 tf.data.dataset 和 keras 模型结合起来
【发布时间】:2019-08-16 18:58:32
【问题描述】:

我正在尝试构建一个使用 tf.data.dataset 批处理和迭代器的工作流。出于性能原因,我真的在尽量避免使用 placeholder->feed_dict 循环工作流程。

我正在尝试实现的过程涉及 grad-cam(它需要相对于 CNN 的最终卷积层的损失梯度)作为中间步骤,理想情况下我希望能够尝试它在几个 Keras 预训练模型上得到了验证,包括像 ResNet 这样的非序列模型。

我发现的大多数 grad-cam 实现都依赖于手工制作对 tensorflow 感兴趣的 CNN。我找到了一个实现,https://github.com/jacobgil/keras-grad-cam,它是为 keras 模型制作的,按照这个例子,我得到了


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 不会随着后续评估而改变。

我尝试以各种方式将K.function 替换为Model,但似乎没有任何效果。我通常会得到一个错误,提示 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)

为每个批次提供所需的输出。

【问题讨论】:

    标签: python tensorflow keras gradient tensorflow-datasets


    【解决方案1】:

    是的,K.function 返回 numpy 数组,因为它评估图形中的符号计算。我认为你应该做的是将所有符号保持在K.function,并在获得梯度后,使用 numpy 执行 Grad-CAM 权重和最终显着图的所有计算。

    然后您可以迭代您的数据集,在一批新数据上评估 gradient_function,并计算显着性图。

    如果你想保持一切符号化,那么你不应该使用K.function来产生梯度函数,而是使用符号梯度(K.gradient的输出,没有lambda)和卷积特征图(conv_output)并在此基础上执行显着图计算,然后构建一个函数(使用K.function)接受模型输入并输出显着图。

    希望解释足够。

    【讨论】:

    • 谢谢!我知道我需要在这一步摆脱 K.function 以保持对毕业后其他计算的象征意义,但我没有想到将它向下使用并包含其他输出节点作为输出sess.run(...)
    猜你喜欢
    • 2019-09-17
    • 2021-11-02
    • 2011-10-04
    • 2018-09-21
    • 2019-04-28
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    • 2013-03-05
    相关资源
    最近更新 更多