说明
- 大部分代码来源于网上,但网上的代码一下子可能难以入门或因版本原因报错,此处整理后进行详细分析。
参考的代码来源1:Attention mechanism Implementation for Keras.网上大部分代码都源于此,直接使用时注意Keras版本,若版本不对应,在merge处会报错,解决办法为:导入Multiply层并将attention_dense.py第17行的:
attention_mul = merge([inputs, attention_probs], output_shape=32, name=‘attention_mul’, mode=‘mul’),改为:attention_mul = Multiply()([inputs, attention_probs])即可。
参考的代码来源2:[深度应用]·Keras极简实现Attention结构。这相当于来源1的简化版本,其将注意力层还做了封装,可直接使用。但此方法运用了两个注意力层,使我有些不太理解,这个问题在后面会进行讨论。
本文主体将在来源1的基础上进行分析探讨。 - Attention机制大致过程就是分配权重,所有用到权重的地方都可以考虑使用它,另外它是一种思路,不局限于深度学习的实现方法,此处仅代码上分析,且为深度学习的实现版本。更多理论请看解读大牛文章深度学习中的注意力机制(2017版),还可以看解读这篇文章的大牛文章:[深度概念]·Attention机制实践解读。
- 此处仅介绍Dense+Attention,进阶篇LSTM+Attention请看【深度学习】 基于Keras的Attention机制代码实现及剖析——LSTM+Attention。
如果你对本系列感兴趣,可接以下传送门:
实验目的
- 在简单的分类模型(如最简的全连接网络)基础上实现Attention机制的运用。
- 检验Attention是否真的捕捉到了关键特征,即被Attention分配的关键特征的权重是否更高。
- 在已有的模型基础上适当做些变化,如调参或新加层,看看Attention的稳定性如何。
数据集构造
因为是在分类问题上进行应用,所以需要构造特征(X)和标签(Y),此处数据随机产生,但为了进行Attention机制的有效性验证,我们将特征X的某一列置成和标签完全相同,如果Attention有效,那么模型学出来,自然这一列的权重就要最高。
默认设置:attention_column=1,即将 第“1”列(从0开始数) 与标签置成相同。
同时为了简化问题,将分类设置为二分类问题,即randint的参数high设置为2。(注意randint是左闭右开,所以当high=2时,y要么为0,要么为1)
def get_data(n, input_dim, attention_column=1):
"""
Data generation. x is purely random except that it's first value equals the target y.
In practice, the network should learn that the target = x[attention_column].
Therefore, most of its attention should be focused on the value addressed by attention_column.
:param n: the number of samples to retrieve.
:param input_dim: the number of dimensions of each element in the series.
:param attention_column: the column linked to the target. Everything else is purely random.
:return: x: model inputs, y: model targets
"""
x = np.random.standard_normal(size=(n, input_dim))
y = np.random.randint(low=0, high=2, size=(n, 1))
x[:, attention_column] = y[:, 0]
return x, y
我们输出X,Y的前三行,看看是不是和我们想要的一致。可以看到每一个x∈X的“第1列”都等于标签号,一致了。
模型搭建
下面开始在单隐层全连接网络的基础上用keras搭建注意力层。
def build_model():
K.clear_session() #清除之前的模型,省得压满内存
inputs = Input(shape=(input_dim,)) #输入层
# ATTENTION PART STARTS HERE 注意力层
attention_probs = Dense(input_dim, activation='softmax', name='attention_vec')(inputs)
attention_mul = Multiply()([inputs, attention_probs])
# ATTENTION PART FINISHES HERE
attention_mul = Dense(64)(attention_mul) #原始的全连接
output = Dense(1, activation='sigmoid')(attention_mul) #输出层
model = Model(input=[inputs], output=output)
return model
可以看到注意力层就两行代码,分别是一个Dense(全连接)层和一个Multiply操作,注意Multiply是对应元素相乘。
如果画出加Attention前后的结构图,可如下图所示:
模型训练及验证
设置随机种子可以调试对比每次结果的异同,输入必要参数,调用模型就可以开始训练了,由于是二分类问题,所以损失函数用二分类交叉熵。训练集测试集8:2进行验证。
if __name__ == '__main__':
np.random.seed(1337) # for reproducibility
input_dim = 32 #特征数
N = 10000 #数据集总记录数
inputs_1, outputs = get_data(N, input_dim) #构造数据集
m = build_model() #构造模型
m.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
m.summary()
m.fit([inputs_1], outputs, epochs=20, batch_size=64, validation_split=0.2)
后台会输出我们的模型架构,看看是不是和设计的一样:
之前设置的是20个Epoch,看看训练结果: