如何使用 keras-self-attention 包可视化注意力 LSTM?

Posted

技术标签:

【中文标题】如何使用 keras-self-attention 包可视化注意力 LSTM?【英文标题】:How visualize attention LSTM using keras-self-attention package? 【发布时间】:2020-02-09 21:40:35 【问题描述】:

我正在使用(keras-self-attention) 在 KERAS 中实现注意力 LSTM。训练模型后如何可视化注意力部分?这是一个时间序列预测案例。

from keras.models import Sequential
from keras_self_attention import SeqWeightedAttention
from keras.layers import LSTM, Dense, Flatten

model = Sequential()
model.add(LSTM(activation = 'tanh' ,units = 200, return_sequences = True, 
               input_shape = (TrainD[0].shape[1], TrainD[0].shape[2])))
model.add(SeqSelfAttention())
model.add(Flatten())    
model.add(Dense(1, activation = 'relu'))

model.compile(optimizer = 'adam', loss = 'mse')

【问题讨论】:

【参考方案1】:

一种方法是获取给定输入的SeqSelfAttention 的输出,并组织它们以显示预测每个通道(见下文)。如需更高级的内容,请查看iNNvestigate library(包括使用示例)。

更新:我也可以推荐See RNN,我写的一个包。


说明show_features_1D 获取 layer_name(可以是子字符串)层输出并显示每个通道(标记)的预测,沿 x 轴的时间步长和沿 y 轴的输出值。 input_data = 单批 形状 (1, input_shape) 的数据 prefetched_outputs = 已经获得的层输出;覆盖input_data max_timesteps = 要显示的最大时间步数 max_col_subplots = 沿水平方向的最大子图数 equate_axes = 强制所有 x 轴和 y 轴相等(建议进行公平比较) show_y_zero = 是否将 y=0 显示为红线 channel_axis = 层特征维度(例如,LSTM 的 units,这是最后一个) scale_width, scale_height = 缩放显示的图像宽度和高度 dpi = 图像质量(每英寸点数)

视觉(下)解释

首先查看提取特征的形状很有用,无论大小如何 - 提供有关例如频率 内容 第二个有助于查看特征关系 - 例如相对幅度、偏差和频率。下面的结果与上面的图像形成鲜明对比,因为运行print(outs_1) 表明所有幅度都非常小并且变化不大,因此包括 y=0 点和等轴轴会产生类似线的视觉效果,这可以被解释为自我关注是偏向的。 Third 对于可视化太多而无法像上面那样可视化的特征很有用;使用batch_shape 而不是input_shape 定义模型会删除打印形状中的所有?,我们可以看到第一个输出的形状是(10, 60, 240),第二个是(10, 240, 240)。换句话说,第一个输出返回 LSTM 通道注意,第二个输出返回“时间步注意”。下面的热图结果可以解释为显示注意力“冷却”w.r.t。时间步长。

SeqWeightedAttention 更容易可视化,但没有太多可视化;您需要删除上面的Flatten 才能使其正常工作。然后注意力的输出形状变为(10, 60)(10, 240) - 您可以使用简单的直方图plt.hist(只需确保排除批处理维度 - 即提要(60,)(240,))。


from keras.layers import Input, Dense, LSTM, Flatten, concatenate
from keras.models import Model
from keras.optimizers import Adam
from keras_self_attention import SeqSelfAttention
import numpy as np 

ipt   = Input(shape=(240,4))
x     = LSTM(60, activation='tanh', return_sequences=True)(ipt)
x     = SeqSelfAttention(return_attention=True)(x)
x     = concatenate(x)
x     = Flatten()(x)
out   = Dense(1, activation='sigmoid')(x)
model = Model(ipt,out)
model.compile(Adam(lr=1e-2), loss='binary_crossentropy')

X = np.random.rand(10,240,4) # dummy data
Y = np.random.randint(0,2,(10,1)) # dummy labels
model.train_on_batch(X, Y)

outs = get_layer_outputs(model, 'seq', X[0:1], 1)
outs_1 = outs[0]
outs_2 = outs[1]

show_features_1D(model,'lstm',X[0:1],max_timesteps=100,equate_axes=False,show_y_zero=False)
show_features_1D(model,'lstm',X[0:1],max_timesteps=100,equate_axes=True, show_y_zero=True)
show_features_2D(outs_2[0])  # [0] for 2D since 'outs_2' is 3D


def show_features_1D(model=None, layer_name=None, input_data=None,
                     prefetched_outputs=None, max_timesteps=100,
                     max_col_subplots=10, equate_axes=False,
                     show_y_zero=True, channel_axis=-1,
                     scale_width=1, scale_height=1, dpi=76):
    if prefetched_outputs is None:
        layer_outputs = get_layer_outputs(model, layer_name, input_data, 1)[0]
    else:
        layer_outputs = prefetched_outputs
    n_features    = layer_outputs.shape[channel_axis]

    for _int in range(1, max_col_subplots+1):
      if (n_features/_int).is_integer():
        n_cols = int(n_features/_int)
    n_rows = int(n_features/n_cols)

    fig, axes = plt.subplots(n_rows,n_cols,sharey=equate_axes,dpi=dpi)
    fig.set_size_inches(24*scale_width,16*scale_height)

    subplot_idx = 0
    for row_idx in range(axes.shape[0]):
      for col_idx in range(axes.shape[1]): 
        subplot_idx += 1
        feature_output = layer_outputs[:,subplot_idx-1]
        feature_output = feature_output[:max_timesteps]
        ax = axes[row_idx,col_idx]

        if show_y_zero:
            ax.axhline(0,color='red')
        ax.plot(feature_output)

        ax.axis(xmin=0,xmax=len(feature_output))
        ax.axis('off')

        ax.annotate(str(subplot_idx),xy=(0,.99),xycoords='axes fraction',
                    weight='bold',fontsize=14,color='g')
    if equate_axes:
        y_new = []
        for row_axis in axes:
            y_new += [np.max(np.abs([col_axis.get_ylim() for 
                                     col_axis in row_axis]))]
        y_new = np.max(y_new)
        for row_axis in axes:
            [col_axis.set_ylim(-y_new,y_new) for col_axis in row_axis]
    plt.show()
def show_features_2D(data, cmap='bwr', norm=None,
                     scale_width=1, scale_height=1):
    if norm is not None:
        vmin, vmax = norm
    else:
        vmin, vmax = None, None  # scale automatically per min-max of 'data'

    plt.imshow(data, cmap=cmap, vmin=vmin, vmax=vmax)
    plt.xlabel('Timesteps', weight='bold', fontsize=14)
    plt.ylabel('Attention features', weight='bold', fontsize=14)
    plt.colorbar(fraction=0.046, pad=0.04)  # works for any size plot

    plt.gcf().set_size_inches(8*scale_width, 8*scale_height)
    plt.show()

def get_layer_outputs(model, layer_name, input_data, learning_phase=1):
    outputs   = [layer.output for layer in model.layers if layer_name in layer.name]
    layers_fn = K.function([model.input, K.learning_phase()], outputs)
    return layers_fn([input_data, learning_phase])

SeqWeightedAttention 示例 每个请求:

ipt   = Input(batch_shape=(10,240,4))
x     = LSTM(60, activation='tanh', return_sequences=True)(ipt)
x     = SeqWeightedAttention(return_attention=True)(x)
x     = concatenate(x)
out   = Dense(1, activation='sigmoid')(x)
model = Model(ipt,out)
model.compile(Adam(lr=1e-2), loss='binary_crossentropy')

X = np.random.rand(10,240,4) # dummy data
Y = np.random.randint(0,2,(10,1)) # dummy labels
model.train_on_batch(X, Y)

outs = get_layer_outputs(model, 'seq', X, 1)
outs_1 = outs[0][0] # additional index since using batch_shape
outs_2 = outs[1][0]

plt.hist(outs_1, bins=500); plt.show()
plt.hist(outs_2, bins=500); plt.show()

【讨论】:

@user2991243 我自己从来没有使用过注意力,所以不确定你指的是什么——但如果你提供注意力层的输出形状(例如print(model.layers[1].output.shape)),我仍然可以回答。这个想法将是“分而治之” @user2991243 我刚刚加载了您的注意力层,您描述的功能似乎需要return_attention=True - 对吗?否则,该层似乎只应用了一种无法有意义地一分为二的转换 @user2991243 是的,我现在正在测试 - 你需要一个 concatenate 层。此外,我强烈建议在大多数情况下使用 Model API (Model),因为它更容易使用高级功能 - 但 Sequential 也可以在这里工作。所以确认一下:return_attention=True 返回两个输出,而这两个是你指的?如果是这样,我会用完整的例子更新答案 @user2991243 是的,我测试过,它可以工作 - 现在这是你的使用问题(见之前的评论) @user2991243 如果batch_shape(10,1,240),那么您将提供1 个具有240 个特征的时间步——LSTM 的输出形状应该是(10, 1, 200)。然后,加权注意力层输出(10, 1, 1) 似乎是有意义的(因为它的模式似乎是(batch_size, timesteps, timesteps)),“1”的值可能意味着在一个时间步上 100% 的注意力。然而,第一个加权注意力输出不仅仅是 1,并且具有输出形状 (10, 1, 200)

以上是关于如何使用 keras-self-attention 包可视化注意力 LSTM?的主要内容,如果未能解决你的问题,请参考以下文章

如果加入条件,我该如何解决。如果使用字符串连接,我如何使用

如何使用本机反应创建登录以及如何验证会话

如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]

如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?

如何使用laravel保存所有行数据每个行名或相等

如何使用 Math.Net 连接矩阵。如何使用 Math.Net 调用特定的行或列?