解决:如何将tf.gradients与tf.data.dataset和keras模型结合

时间:2019-08-16 18:58:32

标签: python tensorflow keras gradient tensorflow-datasets

我正在尝试构建使用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不会随随后的求值而改变。

我尝试以各种方式将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)

给我每批所需的产量。

1 个答案:

答案 0 :(得分:0)

是的,public int[] makeMiddle(int[] nums) { if(nums.length>=2){ if(nums[nums.length-1]%2==0) { int[] arrEven=new int[2]; arrEven[0]=nums[(nums.length/2)-1]; arrEven[1]=nums[(nums.length/2)]; return arrEven; } else { int[] arrOdd=new int[1]; arrOdd[0]=nums[(nums.length/2)]; return arrOdd; } } return nums; } 返回numpy数组,因为它会评估图形中的符号计算。我认为您应该做的是将所有符号保持不超过K.function,并在获取梯度后,使用numpy执行Grad-CAM权重和最终显着性图的所有计算。

然后,您可以迭代数据集,对一批新数据评估K.function,并计算显着性图。

如果要使所有符号保持符号状态,则不应使用gradient_function来生成梯度函数,而应使用符号梯度(K.function的输出,不包含lambda)和卷积特征图( K.gradient)并在此基础上执行显着性图计算,然后构建一个函数(使用conv_output),该函数接受模型输入并输出显着性图。

希望解释就足够了。