Attention Is All You Need

Motivation:

  • 靠attention机制,不使用rnn和cnn,并行度高
  • 通过attention,抓长距离依赖关系比rnn强

 

 

Scaled Dot-Product Attention

d_{k}=d_{model}/h


其中, 

其中因子起到调节作用,使得内积不至于太大(太大的话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模型顶多是一个非常精妙的“词袋模型”而已

2i2i+1为位置序号,d_{model}为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,计算注意力权重矩阵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章