文章目录
论文名称
原文:
Effective Approaches to Attention-based Neural Machine Translation
本篇主要记录的是global attention的用法
文章中,机器翻译的模型是encoder-decoder模型。
其中将LSTM隐层状态的输出作为注意力层的输入。
这里将输入的向量记为
h
t
h_t
ht
文章里面提到了两个向量:
第一步:计算出contect_vector:
c
t
c_t
ct:,通过该向量我们可以推断出当前的target对象和所有source的联系。
第二步:计算出
c
t
c_t
ct之后,将其和原本的
h
t
h_t
ht进行拼接,从而可以产生一个attention_state
公式如下:
第三步:将得到的
h
t
′
h'_{t}
ht′通过一个softmax函数,从而就可以求得它的概率分布:公式如下
那么如何计算
c
t
c_t
ct呢?
global attention
需要注意的是,global考虑编码器的所有隐层状态。
这里提到一个对齐向量(align_vector),它是由当前的target_hidden_state和每一个source_hidden_state推出来的,这里其实可以将
a
t
a_t
at看作每一个输入结点对于该输出结点的注意力分配系数。
计算公式如下
那么这里的score要如何计算呢?三种计算方法:
下面看一下代码吧:
这里的attention_mechanism:是seq to one的,而文章中提到的机器翻译其实是seq to seq的。
自定义一个Attention layer层。
这里Attention 是继承了Layer层的
一._call_函数中:
- hidden_states就等于当前的输入。
- hidden_size的值代表输入向量的维度的大小
- score_second_part:将输入的hidden_states放入Dense层中,其实就是
h
s
′
h'_s
hs′。
如何得到 h ’ s h’_s h’s:输入向量乘以1个权重矩阵W。这里的W是可训练的。
其中:hidden_state的维度大小为(batch,time_step,hidden_size).
W的维度大小为(hidden_size,hidden_size)。
矩阵的相乘:不需要管batch,即(time_step,hidden_size)和(hidden_size,hidden_size)作dot
输出的score_second_part的维度大小为
(batch,time_step,hidden_size). - h_t:作者将得到的the last hidden state作为hidden_target.
- 计算attention_weight(score)即
h
t
和
h
‘
s
h_t和h`_s
ht和h‘s作dot,内积是在h_s的2维度和h_t的一维度作内积。
所以下面的代码中出现了axes=[1,2]。具体为什么,可以参考之前写的tensordot函数的原理,这里大概提一下,由于hidden_size和hidden_size的大小是相同的,所以会被抵消掉维度。
h_s的维度:(batch,time_step,hidden_size)
h_t的维度:(batch,hidden_size)
得到的attention_weight的形状大小为
(batch,time_step)。 - 计算context_vector:即输入的hidden_state和attention_weight作内积,对应元素相加再求和。
这里需要注意以下:dot的时候要选择对应的轴。和上面是一样的。 - 之后的几步就是上面的公式了,将得到的 c t c_t ct和 h t h_t ht进行拼接,然后输入到全连接层中,使用的激活函数是tanh。得到最终的attention_vector
from tensorflow.keras.layers import Dense, Lambda, Dot, Activation, Concatenate
from tensorflow.keras.layers import Layer
class Attention(Layer):
def __init__(self, units=128, **kwargs):
self.units = units
super().__init__(**kwargs)
def __call__(self, inputs):
"""
Many-to-one attention mechanism for Keras.
@param inputs: 3D tensor with shape (batch_size, time_steps, input_dim).
@return: 2D tensor with shape (batch_size, 128)
@author: felixhao28, philipperemy.
"""
hidden_states = inputs
hidden_size = int(hidden_states.shape[2])
score_second_part = Dense(hidden_size, use_bias=False, name='attention_score_vec')(hidden_states)
h_t = Lambda(lambda x: x[:,-1,:], output_shape=(hidden_size,), name='last_hidden_state')(hidden_states)
score = Dot(axes=[1, 2], name='attention_score')([h_t, score_second_part])
attention_weights = Activation('softmax', name='attention_weight')(score)
context_vector = Dot(axes=[1, 1], name='context_vector')([hidden_states, attention_weights])
pre_activation = Concatenate(name='attention_output')([context_vector, h_t])
attention_vector = Dense(self.units, use_bias=False, activation='tanh', name='attention_vector')(pre_activation)
return attention_vector
def get_config(self):
return {'units': self.units}
@classmethod
def from_config(cls, config):
return cls(**config)
大体的思路就是这样的,我自己也是刚看这块的内容,记录一下,要不然很快就会忘记。
如果有错误,希望大家可以指出来。
贴一下这个项目的整理代码:
项目整个代码
python知识点:
python中的self相当于c++中的this指针,打印出来可以看到,是test对象的地址。
_call函数:可以使得对象像函数一样被调用:如下所示:
class test:
def __init__(self,name,age):
self.x=name
self.y=age
def __call__(self):
print("姓名是",self.x)
print("年龄是",self.y)
print(self)
a=test("tianjinghua",5)
a()