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