睿智的seq2seq模型3——注意力机制概念详解与其在LSTM中的使用
学习前言
注意力机制很重要噢,要好好学习。
什么是注意力机制
利用神经网络找到输入进来的特征,哪一部分更有用。
假设我们要翻译一句话:
打电脑游戏。
也就是play computer game。
如果不引入注意力机制,那么我们从Encoder获得语义编码c之后,这个语义编码在Decoder中传递,其内容就和Encoder无关了。
但是事实上我们希望在翻译打电脑游戏中的打的时候,我们更注意打->play的转换,此时我们希望Decoder更加注意Encoder从打中提取出来的特征。
这就是注意力机制的概念,它的本意是让神经网络模型在做特定的事的时候可以注意到它需要注意的地方。
由于神经网络是一堆数字的传递,每个事物的特征也是由一堆数字组成的,比如打字的特征也是一堆数字,电脑的特征也是一堆数字,游戏的特征也是一堆数字,语义编码c就是这么多特征的组合。
那么如何使得神经网络模型对某个内容进行注意呢?其实就是将改变不同内容的权重,当我们需要神经网络注意到打的时候,我们只需要提高打字的特征的权重就可以了。
假设函数 f 可以用于提取特征,函数 g 可以实现解码。那么如果我们要神经网络注意到打,可以通过如下方式进行。
注意力机制的Keras实现
我将用一个小例子给大家演示一下Keras中的注意力机制的实现以及其作用。
假设我们存在一个TIME_STEP为10,INPUT_DIM为2的输入。
当我们的TIME_STEP为2的输入为[0,0]时,其输出为0;
当我们的TIME_STEP为2的输入为[1,1]时,其输出为1;
其它TIME_STEP等于其它的时候,如0,1,3,……9时,其对应的时间的输入为为符合正态分布的数。
具体如下所示:
#-------------------------------------#
x = [[[14.05795148 10.6586937 ]
[-5.17788409 3.0967234 ]
[ 1. 1. ]
[-7.16327903 7.36591461]
[ 3.07887461 18.46302035]
[ 8.7123103 15.77254757]
[-7.6266161 -4.56511326]
[ 1.64038985 0.10782463]
[ 3.62548177 3.22431191]
[ 0.76630364 -3.95249622]]]
y = [[1]]
#-------------------------------------#
#-------------------------------------#
x = [[[ -4.22167643 1.98029051]
[ -1.00985459 15.08588672]
[ 0. 0. ]
[ 13.48448467 -0.66743308]
[ 31.3199347 3.0311851 ]
[ -4.81579489 1.62016606]
[ 7.40993759 4.25739609]
[ 13.37376609 -11.63055067]
[ -6.46277603 -13.94173142]
[-12.01871193 -9.53632924]]]
y = [[0]]
#-------------------------------------#
因此,输出y只与TIME_STEP为2时的输入有关,如果我们为x添加了注意力机制来预测y,注意力机制应该非常注意TIME_STEP为2时的输入,也就是,该部分的权重会非常高。
1、生成数据集
我们可以通过如下方式生成数据集:
import numpy as np
def get_data_recurrent(n, time_steps, input_dim, attention_column=2):
x = np.random.normal(loc=0, scale=10, size=(n, time_steps, input_dim))
y = np.random.randint(low=0, high=2, size=(n, 1))
x[:, attention_column, :] = np.tile(y[:], (1, input_dim))
return x, y
x, y = get_data_recurrent(1, 10, 2)
print("x =",x)
print("y =",y)
2、建立注意力模型
注意力机制在神经网络中的实现方式如下:
我们将一串时间序列传入到LSTM中,可以获得一个维度为(batch_size, time_steps, lstm_units)的输出,我们可以把其当作每一个时间节点的特征,我们把这样的一个输出作为上述图片的Input。
经过Permute将2、1轴翻转后,其维度从(batch_size, time_steps, lstm_units)转化成(batch_size, lstm_units, time_steps)。
再经过一个全连接层和Softmax后,其维度仍为(batch_size, lstm_units, time_steps),其实际内涵为,利用全连接层计算每一个time_steps的权重。
再经过Permute将2、1轴翻转后,其维度从(batch_size, lstm_units, time_steps)转化成(batch_size, time_steps, lstm_units)。代表每一个STEP中每一个特征的权重。
最后将这个结果与Input相乘,也就是将每个STEP的权重,乘上他们的特征。
实现代码如下:
#-------------------------------------------#
# 注意力模块
#-------------------------------------------#
def attention_3d_block(inputs):
# inputs.shape = (batch_size, time_steps, lstm_units)
# (batch_size, time_steps, lstm_units) -> (batch_size, lstm_units, time_steps)
a = Permute((2, 1))(inputs)
# 对最后一维进行全连接
# (batch_size, lstm_units, time_steps) -> (batch_size, lstm_units, time_steps)
a = Dense(TIME_STEPS, activation='softmax')(a)
# (batch_size, lstm_units, time_steps) -> (batch_size, time_steps, lstm_units)
a_probs = Permute((2, 1), name='attention_vec')(a)
# 相乘
# 相当于获得每一个step中,每个维度在所有step中的权重
output_attention_mul = merge([inputs, a_probs], name='attention_mul', mode='mul')
return output_attention_mul
3、建立整体神经网络
在上一步中,我们已经获得了具有权重的特征组合。
平铺和全连接后,就可以获得预测结果。
实现代码如下:
#-------------------------------------------#
# 建立注意力模型
#-------------------------------------------#
def get_attention_model():
inputs = Input(shape=(TIME_STEPS, INPUT_DIM,))
lstm_units = 32
# (batch_size, time_steps, INPUT_DIM) -> (batch_size, input_dim, lstm_units)
lstm_out = LSTM(lstm_units, return_sequences=True)(inputs)
attention_mul = attention_3d_block(lstm_out)
# (batch_size, input_dim, lstm_units) -> (batch_size, input_dim*lstm_units)
attention_mul = Flatten()(attention_mul)
output = Dense(1, activation='sigmoid')(attention_mul)
model = Model(input=[inputs], output=output)
return model
代码&效果
1、实现代码
from keras.layers import merge
from keras.layers.core import *
from keras.layers.recurrent import LSTM
from keras.models import *
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
INPUT_DIM = 2
TIME_STEPS = 10
#-------------------------------------------#
# 对每一个step的INPUT_DIM的attention几率
# 求平均
#-------------------------------------------#
def get_activations(model, inputs, layer_name=None):
inp = model.input
for layer in model.layers:
if layer.name == layer_name:
Y = layer.output
model = Model(inp,Y)
out = model.predict(inputs)
out = np.mean(out[0],axis=-1)
return out
#-------------------------------------------#
# 获得数据集
# attention_column代表我们希望被注意的列
#-------------------------------------------#
def get_data_recurrent(n, time_steps, input_dim, attention_column=2):
x = np.random.normal(loc=0, scale=10, size=(n, time_steps, input_dim))
y = np.random.randint(low=0, high=2, size=(n, 1))
x[:, attention_column, :] = np.tile(y[:], (1, input_dim))
return x, y
#-------------------------------------------#
# 注意力模块
#-------------------------------------------#
def attention_3d_block(inputs):
# inputs.shape = (batch_size, time_steps, lstm_units)
# (batch_size, time_steps, lstm_units) -> (batch_size, lstm_units, time_steps)
a = Permute((2, 1))(inputs)
# 对最后一维进行全连接
# (batch_size, lstm_units, time_steps) -> (batch_size, lstm_units, time_steps)
a = Dense(TIME_STEPS, activation='softmax')(a)
# (batch_size, lstm_units, time_steps) -> (batch_size, time_steps, lstm_units)
a_probs = Permute((2, 1), name='attention_vec')(a)
# 相乘
# 相当于获得每一个step中,每个维度在所有step中的权重
output_attention_mul = merge([inputs, a_probs], name='attention_mul', mode='mul')
return output_attention_mul
#-------------------------------------------#
# 建立注意力模型
#-------------------------------------------#
def get_attention_model():
inputs = Input(shape=(TIME_STEPS, INPUT_DIM,))
lstm_units = 32
# (batch_size, time_steps, INPUT_DIM) -> (batch_size, input_dim, lstm_units)
lstm_out = LSTM(lstm_units, return_sequences=True)(inputs)
attention_mul = attention_3d_block(lstm_out)
# (batch_size, input_dim, lstm_units) -> (batch_size, input_dim*lstm_units)
attention_mul = Flatten()(attention_mul)
output = Dense(1, activation='sigmoid')(attention_mul)
model = Model(input=[inputs], output=output)
return model
if __name__ == '__main__':
N = 100000
X, Y = get_data_recurrent(N, TIME_STEPS, INPUT_DIM)
model = get_attention_model()
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
print(model.summary())
model.fit(X, Y, epochs=1, batch_size=64, validation_split=0.1)
attention_vectors = []
for i in range(300):
testing_X, testing_Y = get_data_recurrent(1, TIME_STEPS, INPUT_DIM)
attention_vector = get_activations(model,testing_X,layer_name='attention_vec')
print('attention =', attention_vector)
assert (np.sum(attention_vector) - 1.0) < 1e-5
attention_vectors.append(attention_vector)
attention_vector_final = np.mean(np.array(attention_vectors), axis=0)
pd.DataFrame(attention_vector_final, columns=['attention (%)']).plot(kind='bar',
title='Attention Mechanism as '
'a function of input'
' dimensions.')
plt.show()
2、实现效果
我们可以发现,第二个SETP的特征的权重最大。