RNN 或 LSTM 中的可变重要性

机器算法验证 神经网络 lstm 循环神经网络 重要性
2022-03-03 06:28:03

已经设计了几种方法来访问或量化 MLP 神经网络模型中的变量重要性(即使只是相对于彼此):

  • 连接权重
  • 加森算法
  • 偏导数
  • 输入扰动
  • 敏感性分析
  • 正向逐步加法
  • 向后逐步消除
  • 改进的逐步选择 1
  • 改进的逐步选择 2

(这些在http://dx.doi.org/10.1016/j.ecolmodel.2004.03.013中有描述)

是否有任何方法可以应用于 RNN 或 LSTM 神经网络?

1个回答

简而言之,的,您可以对基于 RNN 的模型的变量重要性进行一些度量。我不会遍历问题中列出的所有建议,但我将深入介绍敏感性分析的示例。

数据

我的 RNN 的输入数据将由具有三个特征的时间序列组成,每个特征都将从随机均匀分布中抽取。我的 RNN 的目标变量将是一个时间序列(我输入中每个时间步长的一个预测):x1x2x3

y={0,if x1x20.251,if x1x2<0.25

正如我们所见,目标仅依赖于前两个特征。因此,一个好的变量重要性度量应该显示前两个变量很重要,而第三个变量不重要。

该模型

该模型是一个简单的三层 LSTM,在最后一层有一个 sigmoid 激活。该模型将在 5 个 epoch 中进行训练,每个 epoch 有 1000 个批次。

可变重要性

为了衡量变量的重要性,我们将对数据进行大样本(250 个时间序列)并计算模型的预测然后,对于每个变量,我们将通过以 0 为中心、尺度为 0.2 的随机正态分布扰动该变量(并且仅该变量)并计算预测和扰动之间的均方根差来测量这种扰动的影响较大的均方根差异意味着变量“更重要”。x^y^xiyi^y^yi^

显然,用于扰动数据的确切机制,以及如何测量扰动和未扰动输出之间的差异,将高度依赖于您的特定数据集。

结果

完成上述所有操作后,我们看到以下重要性:

Variable 1, perturbation effect: 0.1162
Variable 2, perturbation effect: 0.1185
Variable 3, perturbation effect: 0.0077

正如我们所料,发现变量 1 和 2 比变量 3 更重要(大约 15 倍)!

重现的 Python 代码

from tensorflow import keras  # tensorflow v1.14.0 was used
import numpy as np            # numpy v1.17.1 was used

np.random.seed(2019)

def make_model():
    inp = keras.layers.Input(shape=(10, 3))
    x = keras.layers.LSTM(10, activation='relu', return_sequences=True)(inp)
    x = keras.layers.LSTM(5, activation='relu', return_sequences=True)(x)
    x = keras.layers.LSTM(1, activation='sigmoid', return_sequences=True)(x)
    out = keras.layers.Flatten()(x)
    return keras.models.Model(inp, out)

def data_gen():
    while True:
        x = np.random.rand(5, 10, 3)  # batch x time x features
        yield x, x[:, :, 0] * x[:, :, 1] < 0.25

def var_importance(model):
    g = data_gen()
    x = np.concatenate([next(g)[0] for _ in range(50)]) # Get a sample of data
    orig_out = model.predict(x)
    for i in range(3):  # iterate over the three features
        new_x = x.copy()
        perturbation = np.random.normal(0.0, 0.2, size=new_x.shape[:2])
        new_x[:, :, i] = new_x[:, :, i] + perturbation
        perturbed_out = model.predict(new_x)
        effect = ((orig_out - perturbed_out) ** 2).mean() ** 0.5
        print(f'Variable {i+1}, perturbation effect: {effect:.4f}')

def main():
    model = make_model()
    model.compile('adam', 'binary_crossentropy')
    print(model.summary())
    model.fit_generator(data_gen(), steps_per_epoch=500, epochs=10)
    var_importance(model)

if __name__ == "__main__":
    main()

代码的输出:

Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         [(None, 10, 3)]           0
_________________________________________________________________
lstm (LSTM)                  (None, 10, 10)            560
_________________________________________________________________
lstm_1 (LSTM)                (None, 10, 5)             320
_________________________________________________________________
lstm_2 (LSTM)                (None, 10, 1)             28
_________________________________________________________________
flatten (Flatten)            (None, 10)                0
=================================================================
Total params: 908
Trainable params: 908
Non-trainable params: 0
_________________________________________________________________
Epoch 1/5
1000/1000 [==============================] - 14s 14ms/step - loss: 0.6261
Epoch 2/5
1000/1000 [==============================] - 12s 12ms/step - loss: 0.4901
Epoch 3/5
1000/1000 [==============================] - 13s 13ms/step - loss: 0.4631
Epoch 4/5
1000/1000 [==============================] - 14s 14ms/step - loss: 0.4480
Epoch 5/5
1000/1000 [==============================] - 14s 14ms/step - loss: 0.4440
Variable 1, perturbation effect: 0.1162
Variable 2, perturbation effect: 0.1185
Variable 3, perturbation effect: 0.0077