Motivation:
- 靠attention机制,不使用rnn和cnn,并行度高
- 通过attention,抓长距离依赖关系比rnn强
Scaled Dot-Product Attention
其中,
其中因子起到调节作用,使得内积不至于太大(太大的话softmax后就非0即1了,不够“soft”了)。
1.给定输入数据,转换成对应的内容 embedding。
2.得到Q,K,V
3.计算Query和Key的相似度
4.增加mask
- query和key有些部分是填充的,这些需要用mask屏蔽,一个简单的方法就是赋予一个很小很小的值或者直接变为0值。
- 对于decoder的来说,我们是不能看到未来的信息的,所以对于decoder的输入,我们只能计算它和它之前输入的信息的相似度。
对encoder的 Key 进行mask,mask成负无穷(mask成负无穷的位置经过softmax后数值为0)
对decoder的Key进行mask, mask成0,
5.对Query进行mask
6.和Value进行相乘
完整的Scaled Dot-Product Attention的代码如下:
def scaled_dotproduct_attention(queries,keys,num_units=None,
num_heads = 0,
dropout_rate = 0,
is_training = True,
causality = False,
scope = "mulithead_attention",
reuse = None):
with tf.variable_scope(scope,reuse=reuse):
if num_units is None:
num_units = queries.get_shape().as_list[-1]
# Linear projection
Q = tf.layers.dense(queries,num_units,activation=tf.nn.relu) #
K = tf.layers.dense(keys,num_units,activation=tf.nn.relu) #
V = tf.layers.dense(keys,num_units,activation=tf.nn.relu) #
outputs = tf.matmul(Q,tf.transpose(K,[0,2,1]))
outputs = outputs / (K.get_shape().as_list()[-1] ** 0.5)
# 这里是对填充的部分进行一个mask,这些位置的attention score变为极小,我们的embedding操作中是有一个padding操作的,
# 填充的部分其embedding都是0,加起来也是0,我们就会填充一个很小的数。
key_masks = tf.sign(tf.abs(tf.reduce_sum(keys,axis=-1)))
key_masks = tf.tile(tf.expand_dims(key_masks,1),[1,tf.shape(queries)[1],1])
paddings = tf.ones_like(outputs) * (-2 ** 32 + 1)
outputs = tf.where(tf.equal(key_masks,0),paddings,outputs)
# 这里其实就是进行一个mask操作,不给模型看到未来的信息。
if causality:
diag_vals = tf.ones_like(outputs[0,:,:])
tril = tf.contrib.linalg.LinearOperatorTriL(diag_vals).to_dense()
masks = tf.tile(tf.expand_dims(tril,0),[tf.shape(outputs)[0],1,1])
paddings = tf.ones_like(masks) * (-2 ** 32 + 1)
outputs = tf.where(tf.equal(masks,0),paddings,outputs)
outputs = tf.nn.softmax(outputs)
# Query Mask
query_masks = tf.sign(tf.abs(tf.reduce_sum(queries,axis=-1)))
query_masks = tf.tile(tf.expand_dims(query_masks,-1),[1,1,tf.shape(keys)[1]])
outputs *= query_masks
# Dropout
outputs = tf.layers.dropout(outputs,rate = dropout_rate,training = tf.convert_to_tensor(is_training))
# Weighted sum
outputs = tf.matmul(outputs,V)
# Residual connection
outputs += queries
# Normalize
outputs = normalize(outputs)
return outputs
Multi-Head Attention
其中,,,
把Q,K,V通过参数矩阵映射一下,然后再做Attention,把这个过程重复做h次(参数不共享),结果拼接起来。
Position-wise Feed-Forward Networks
两层全连接前向网络,第一层的输出后接Relu激活函数,第二层不接激活函数。
Positional Encoding
在加入Positional Encoding之前,如果将K,V按行打乱顺序(相当于句子中的词序打乱),Attention的结果还是一样的,这样的模型并不能捕捉序列的顺序!此时,Attention模型顶多是一个非常精妙的“词袋模型”而已。
和为位置序号,为Positional Encoding向量的长度.
假如Position_Embedding是拼接到原来的词向量中,那么将cos和sin前后连接还是交叉连接,都是没区别的,因为下一步都是接一个变换矩阵而已.
Encoder和Decoder的区别
编码可以并行计算,一次性全部encoding出来,但解码不是一次把所有序列解出来的,而是像rnn一样一个一个解出来的,因为要用上一个位置的输入当作attention的query
Decoder多了一个Multi-head attention,该Attention的输入是decoder的self attention作为query, encoder的self attention作为key和value,计算注意力权重矩阵。