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,計算注意力權重矩陣。

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