如何使用keras后端计算显着图

Jan*_*lly 4 computer-vision heatmap deep-learning keras tensorflow

我正在尝试使用 keras 为 MNIST 构建一个基本的“普通梯度”显着性热图(基于梯度的特征归因)。我知道有像这样的库可以计算显着性热图,但我想从头开始构建它,因为普通梯度方法在概念上似乎很容易实现。我使用函数模型定义在 Keras 中训练了以下数字分类器:

input = layers.Input(shape=(28,28,1), name='input')
conv2d_1 = layers.Conv2D(32, kernel_size=(3, 3), activation='relu')(input)
maxpooling2d_1 = layers.MaxPooling2D(pool_size=(2, 2), name='maxpooling2d_1')(conv2d_1)
conv2d_2 = layers.Conv2D(64, kernel_size=(3, 3), activation='relu')(maxpooling2d_1)
maxpooling2d_2 = layers.MaxPooling2D(pool_size=(2, 2))(conv2d_2)
flatten = layers.Flatten(name='flatten')(maxpooling2d_2)
dropout = layers.Dropout(0.5, name='dropout')(flatten)
dense = layers.Dense(num_classes, activation='softmax', name='dense')(dropout)

model = keras.models.Model(inputs=input, outputs=dense)
Run Code Online (Sandbox Code Playgroud)

现在,我想计算单个 MNIST 图像的显着图。由于最后一层有一个softmax激活并且分母是一个归一化项(以便输出节点加起来为1),我相信我需要要么采用softmax前的输出,要么改变训练模型的激活线性计算显着图。我会做后者。

model.layers[-1].activation = tf.keras.activations.linear # swap activation to linear
input = loaded_model.layers[0].input
output = loaded_model.layers[-1].output
input_image = x_test[0] # shape is (28, 28, 1)
pred = np.argmax(loaded_model.predict(np.expand_dims(input_image, axis=0))) # predicted class
Run Code Online (Sandbox Code Playgroud)

但是,我不知道除此之外还能做什么。我知道我可以使用以下内容K.gradients(output, input)来计算梯度。话虽这么说,我相信我应该计算预测类相对于输入图像的梯度,而不是计算整个输出的梯度。我该怎么做?另外,我不确定如何评估特定图像/预测的显着性热图。我想我将不得不使用sess = tf.keras.backend.get_session()and sess.run(),但不确定。我将非常感谢任何帮助完成显着性热图代码的帮助。谢谢!

小智 5

如果将激活作为单个层添加到最后一个密集层之后:

keras.layers.Activation('softmax')

你可以做:

linear_model = keras.Model(input=model, output=model.layers[-2].output)

然后计算梯度,如下所示:

def get_saliency_map(model, image, class_idx):
    with tf.GradientTape() as tape:
        tape.watch(image)
        predictions = model(image)
        
        loss = predictions[:, class_idx]
    
    # Get the gradients of the loss w.r.t to the input image.
    gradient = tape.gradient(loss, image)
    
    # take maximum across channels
    gradient = tf.reduce_max(gradient, axis=-1)
    
    # convert to numpy
    gradient = gradient.numpy()
    
    # normaliz between 0 and 1
    min_val, max_val = np.min(gradient), np.max(gradient)
    smap = (gradient - min_val) / (max_val - min_val + keras.backend.epsilon())
    
    return smap
Run Code Online (Sandbox Code Playgroud)