Post

可解释性-积分梯度算法(Integrated Gradients)

最近阅读文章看到一篇可解释性的相关的,往上追溯到了积分梯度算法(Integrated Gradients),蛮有意思,写篇博客整理一下思路 参考论文:Axiomatic Attribution for Deep Networks https://arxiv.org/abs/1703.01365

motivation

这篇文章研究的是如何将模型的预测归因到模型的输入上,以对模型进行debug、提取规则以更好的使用模型。 先前的基于经验的归因评估方法,例如通过归因挑选Top k个像素,随机变化其值然后衡量得分下降的幅度。但是这种方法不够自然,因为模型可能没见过变化后的图像因此给出一个较低的得分。其他的经验评估技术都无法区分源于扰动数据的伪影、行为不当的模型和行为不当的归因方法。 因此本篇文章基于两个基本公理:Sensitivity和Implementation Invariance来设计自己的归因方法。

两个基本公理

Sensitivity

对于每个输入而言,如果baseline和与其在一个特征和预测值上不相同,那么这个特征应该被给予一个非零的归因。 用梯度作为归因,和Sensitivity是相违背的,例如函数\(f(x) = 1 - ReLU(1-x) = \[ f(x) = \begin{cases} x & \text{if } x < 1 \\ 0 & \text{otherwise} \end{cases} \]\) 当x大于等于1时,尽管此时的x与baseline不同,但是其梯度(即归因)为零。因此以梯度作为归因会导致focus到一些不相关的特征

Implementation Invariance

对于两个在功能上等价的神经网络(输入相同时网络输出相同,但实现方法可能不同),他们对于同一输入的归因也必须是相同的。 而基于梯度的归因是依赖于网络具体实现的,不满足该性质。

Our Method:Integrated Gradients

该方法不直接使用梯度,而是对梯度进行积分。对于输入x和baseline x’,沿着第i维的积分梯度定义如下: img.png 实际计算时选择一个路径进行积分,该论文选择直线,即 img.png 比较容易证明,这个方法是满足上面两个公理的 实验也发现,效果挺不错的,不愧是被引4000+的文章 img.png

算法核心代码大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def _compute_ig(sess, input_tensors, embedding_tensor,
                gradient_tensor, output_tensor, transformed_input_df,
                baseline_df, num_reps):
    batch_size = 20  # keep small enough to ensure that we do not run out of
    # memory
    num_reps = num_reps

    tensor_values = sess.run(embedding_tensor,
                             _get_feed_dict(input_tensors,
                                            transformed_input_df))

    tensor_baseline_values = sess.run(embedding_tensor,
        _get_feed_dict(input_tensors, baseline_df))

    # 计算
    scaled_embeddings = _get_scaled_inputs(tensor_values[0],
                                           tensor_baseline_values[0],
                                           batch_size, num_reps)
    scaled_input_feed = {}
    for key, tensor_info in input_tensors.items():
        scaled_input_feed[
            get_tensor(sess, tensor_info.name)] = _get_unscaled_inputs(
            transformed_input_df[key][0], batch_size)

    scores = []
    path_gradients = []

    # 积分值估计计算
    for i in range(num_reps):
        scaled_input_feed[embedding_tensor] = scaled_embeddings[i]
        path_gradients_rep, scores_rep = sess.run(
            [gradient_tensor, output_tensor[:, 1]], scaled_input_feed)
        path_gradients.append(path_gradients_rep[0])
        scores.append(scores_rep)

    baseline_prediction = scores[0][
        0]  # first score is the baseline prediction
    prediction = scores[-1][-1]  # last score is the input prediction

    # integrating the gradients and multiplying with the difference of the
    # baseline and input.
    ig = np.concatenate(path_gradients, axis=0)
    integral = _calculate_integral(ig)
    integrated_gradients = (tensor_values[0] - tensor_baseline_values[
        0]) * integral
    integrated_gradients = np.sum(integrated_gradients, axis=-1)

    return integrated_gradients, baseline_prediction, prediction

# 获取积分路径中各点
def _get_scaled_inputs(input_val, baseline_val, batch_size, num_reps):
    list_scaled_embeddings = []
    scaled_embeddings = \
        [baseline_val + (float(i) / (num_reps * batch_size - 1)) *
         (input_val - baseline_val) for i in range(0, num_reps * batch_size)]

    for i in range(num_reps):
        list_scaled_embeddings.append(
            np.array(scaled_embeddings[i * batch_size:i * batch_size +
                                                      batch_size]))

    return list_scaled_embeddings
This post is licensed under CC BY 4.0 by the author.