bert代碼解讀2之模型transformer的解讀

論文attention is all your need

github:https://github.com/google-research/bert

解讀翻譯:https://www.jiqizhixin.com/articles/2018-11-01-9    

https://baijiahao.baidu.com/s?id=1616001262563114372&wfr=spider&for=pc

https://zhuanlan.zhihu.com/p/34781297《attention is all you need》解讀

https://blog.csdn.net/weixin_39470744

總結前篇的核心:

任務一:Masked LM

  • 80% 的時間:用 [Mask] token 掩蓋之前選擇的單詞。例如:my dog is hairymy dog is [Mask].
  • 10% 的時間:用隨機單詞掩蓋這個單詞。例如:my dog is hairymy dog is apple.
  • 10% 的時間:保持單詞不被掩蓋。例如:my dog is hairymy dog is hairy. (這樣做的目的是將表徵偏向於實際觀察到的單詞)

任務二:Next Sentence Prediciton

  • Input = [CLS] the man went to [MASK] store [SEP] he bought a gallon [MASK] milk [SEP]
  • Label = IsNext
  • Input = [CLS] the man [MASK] to the store [SEP] penguin [MASK] are flight ##less birds [SEP]
  • Label = NotNext

transformer模型部分 : Kyubyong實現版的解讀

# -*- coding: utf-8 -*-
#/usr/bin/python2
'''
June 2017 by kyubyong park. 
[email protected].
https://www.github.com/kyubyong/transformer
transfermer主要結構是由encoder和decoder構成。其中,encoder是由embedding + positional_encoding作爲輸入,
然後加一個dropout層,然後輸入放到6個multihead_attention構成的結構中,每個multihead_attention後面跟一個feedforward。
而decoder是由decoder embedding + positional_encoding作爲輸入,輸入到dropout層,
然後後面跟六個self multihead_attention+ multihead_attention,最後後面跟一個feedward。最後加一個liner projection
https://blog.csdn.net/weixin_38569817/article/details/81357650?utm_source=blogxgwz3
https://www.jianshu.com/p/ef41302edeef
rivastava R K, Greff K, Schmidhuber J. Highway networks[J]. arXiv preprintarXiv:1505.00387, 2015
'''
from __future__ import print_function
import tensorflow as tf
import numpy as np
def normalize(inputs, 
              epsilon = 1e-8,
              scope="ln",
              reuse=None):
    '''Applies layer normalization.
    Args:
      inputs: A tensor with 2 or more dimensions, where the first dimension has
        `batch_size`.
      epsilon: A floating number. A very small number for preventing ZeroDivision Error.
      scope: Optional scope for `variable_scope`.
      reuse: Boolean, whether to reuse the weights of a previous layer
        by the same name.
      
    Returns:
      A tensor with the same shape and data dtype as `inputs`.
    '''
    with tf.variable_scope(scope, reuse=reuse):
        inputs_shape = inputs.get_shape()
        params_shape = inputs_shape[-1:]
    
        mean, variance = tf.nn.moments(inputs, [-1], keep_dims=True)
        beta= tf.Variable(tf.zeros(params_shape))
        gamma = tf.Variable(tf.ones(params_shape))
        normalized = (inputs - mean) / ( (variance + epsilon) ** (.5) )
        outputs = gamma * normalized + beta
        
    return outputs

def embedding(inputs, 
              vocab_size, 
              num_units, 
              zero_pad=True, 
              scale=True,
              scope="embedding", 
              reuse=None):
    '''Embeds a given tensor.

    Args:
      inputs: A `Tensor` with type `int32` or `int64` containing the ids
         to be looked up in `lookup table`.
      vocab_size: An int. Vocabulary size.
      num_units: An int. Number of embedding hidden units.
      zero_pad: A boolean. If True, all the values of the fist row (id 0)
        should be constant zeros.
      scale: A boolean. If True. the outputs is multiplied by sqrt num_units.
      scope: Optional scope for `variable_scope`.
      reuse: Boolean, whether to reuse the weights of a previous layer
        by the same name.

    Returns:
      A `Tensor` with one more rank than inputs's. The last dimensionality
        should be `num_units`.
        
    For example,
    
    ```
    import tensorflow as tf
    
    inputs = tf.to_int32(tf.reshape(tf.range(2*3), (2, 3)))
    outputs = embedding(inputs, 6, 2, zero_pad=True)
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print sess.run(outputs)
    >>
    [[[ 0.          0.        ]
      [ 0.09754146  0.67385566]
      [ 0.37864095 -0.35689294]]

     [[-1.01329422 -1.09939694]
      [ 0.7521342   0.38203377]
      [-0.04973143 -0.06210355]]]
    ```
    
    ```
    import tensorflow as tf
    
    inputs = tf.to_int32(tf.reshape(tf.range(2*3), (2, 3)))
    outputs = embedding(inputs, 6, 2, zero_pad=False)
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print sess.run(outputs)
    >>
    [[[-0.19172323 -0.39159766]
      [-0.43212751 -0.66207761]
      [ 1.03452027 -0.26704335]]

     [[-0.11634696 -0.35983452]
      [ 0.50208133  0.53509563]
      [ 1.22204471 -0.96587461]]]    
    ```    
    '''
    with tf.variable_scope(scope, reuse=reuse):
        lookup_table = tf.get_variable('lookup_table',  #name
                                       dtype=tf.float32,
                                       shape=[vocab_size, num_units],  #形狀
                                       initializer=tf.contrib.layers.xavier_initializer())
        #print(lookup_table) #<tf.Variable 'encoder/enc_embed/lookup_table:0' shape=(9796, 512) dtype=float32_ref>
        #<tf.Variable 'decoder/dec_embed/lookup_table:0' shape=(8767, 512) dtype=float32_ref>
        if zero_pad:
            lookup_table = tf.concat((tf.zeros(shape=[1, num_units]),
                                      lookup_table[1:, :]), 0)
            #print(lookup_table) #Tensor("decoder/dec_embed/concat:0", shape=(8767, 512), dtype=float32)
            #Tensor("encoder/enc_embed/concat:0", shape=(9796, 512), dtype=float32)
        outputs = tf.nn.embedding_lookup(lookup_table, inputs)
        
        if scale:
            outputs = outputs * (num_units ** 0.5)
            #print(outputs) #shape=(32, 10, 512)
    return outputs
    

def positional_encoding(inputs,
                        num_units,
                        zero_pad=True,
                        scale=True,
                        scope="positional_encoding",
                        reuse=None):
    '''Sinusoidal Positional_Encoding.

    Args:
      inputs: A 2d Tensor with shape of (N, T).
      num_units: Output dimensionality
      zero_pad: Boolean. If True, all the values of the first row (id = 0) should be constant zero
      scale: Boolean. If True, the output will be multiplied by sqrt num_units(check details from paper)
      scope: Optional scope for `variable_scope`.
      reuse: Boolean, whether to reuse the weights of a previous layer
        by the same name.
    Returns:
        A 'Tensor' with one more rank than inputs's, with the dimensionality should be 'num_units'
    '''

    N, T = inputs.get_shape().as_list()
    with tf.variable_scope(scope, reuse=reuse):
        position_ind = tf.tile(tf.expand_dims(tf.range(T), 0), [N, 1])

        # First part of the PE function: sin and cos argument
        position_enc = np.array([
            [pos / np.power(10000, 2.*i/num_units) for i in range(num_units)]
            for pos in range(T)])
        #print(position_enc)

        # Second part, apply the cosine to even columns and sin to odds.
        position_enc[:, 0::2] = np.sin(position_enc[:, 0::2])  # dim 2i
        position_enc[:, 1::2] = np.cos(position_enc[:, 1::2])  # dim 2i+1

        # Convert to a tensor
        lookup_table = tf.convert_to_tensor(position_enc)

        if zero_pad:
            lookup_table = tf.concat((tf.zeros(shape=[1, num_units]),
                                      lookup_table[1:, :]), 0)
        outputs = tf.nn.embedding_lookup(lookup_table, position_ind)

        if scale:
            outputs = outputs * num_units**0.5

        return outputs

def multihead_attention(queries,
                        keys,
                        num_units=None, 
                        num_heads=8, 
                        dropout_rate=0,
                        is_training=True,
                        causality=False,
                        scope="multihead_attention", 
                        reuse=None):
    '''Applies multihead attention.
    Args:
      queries: A 3d tensor with shape of [N, T_q, C_q].
      keys: A 3d tensor with shape of [N, T_k, C_k].
      num_units: A scalar. Attention size.
      dropout_rate: A floating point number.
      is_training: Boolean. Controller of mechanism for dropout.
      causality: Boolean. If true, units that reference the future are masked. 
      num_heads: An int. Number of heads.
      scope: Optional scope for `variable_scope`.
      reuse: Boolean, whether to reuse the weights of a previous layer
        by the same name.
        
    Returns
      A 3d tensor with shape of (N, T_q, C)  
    '''
    with tf.variable_scope(scope, reuse=reuse):
        # Set the fall back option for num_units
        if num_units is None:
            num_units = queries.get_shape().as_list[-1]
        # print(num_units) #512
        
        # Linear projections #全連接層
        #print(queries) #shape=(32, 10, 512)
        Q = tf.layers.dense(queries, num_units, activation=tf.nn.relu) # (N, T_q, C) #self.enc,
        K = tf.layers.dense(keys, num_units, activation=tf.nn.relu) # (N, T_k, C)  #self.enc,
        V = tf.layers.dense(keys, num_units, activation=tf.nn.relu) # (N, T_k, C)  #self.enc,
        #print(Q) #shape=(32, 10, 512)
        # Split and concat
        Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # (h*N, T_q, C/h)
        #print(Q_)  #shape=(256, 10, 64) 拆成8個
        K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # (h*N, T_k, C/h) 
        V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # (h*N, T_k, C/h)
        # Multiplication
        outputs = tf.matmul(Q_, tf.transpose(K_, [0, 2, 1])) # (h*N, T_q, T_k)
        #print(outputs) #shape=(256, 10, 10)
        # Scale
        #print(K_.get_shape().as_list()[-1] ) #64
        #print(K_.get_shape().as_list()) #[256, 10, 64]
        outputs = outputs / (K_.get_shape().as_list()[-1] ** 0.5)  #weight值

        '''
        key_masks它是想讓那些key值的unit爲0的key對應的attention score極小,這樣在加權計算value的時候相當於對結果不造成影響。 
        首先用一個reduce_sum(keys, axis=-1))將最後一個維度上的值加起來,keys的shape也從[N, T_k, C_k]變爲[N,T_k]
        然後再用abs取絕對值,即其值只能爲0,或正數
        然後用到了tf.sign(x, name=None),該函數返回符號 y = sign(x) = -1 if x < 0; 0 if x == 0; 1 if x > 0,sign會將原tensor對應的每
        個值變爲-1,0,或者1。則經此操作,得到key_masks,有兩個值,0或者1。0代表原先的keys第三維度所有值都爲0,反之則爲1,我們要mask的就是這些爲0的key。 
        tf.tile把key_masks轉化爲shape爲(h*N, T_k)的key_masks
        每個queries都要對應這些keys,而mask的key對每個queries都是mask的。而之前的key_masks只相當於一份mask,所以擴充之前key_masks的維度,
        在中間加上一個維度大小爲queries的序列長度。然後利用tile函數複製相同的mask值即可。
        定義一個和outputs同shape的paddings,該tensor每個值都設定的極小。
        用where函數比較,當對應位置的key_masks值爲0也就是需要mask時,outputs的該值(attention score)設置爲極小的值(利用paddings實現),
        否則保留原來的outputs值。 
        '''
        # Key Masking
        key_masks = tf.sign(tf.abs(tf.reduce_sum(keys, axis=-1))) # (N, T_k)
        #print(keys) #(32, 10, 512)
        #print(key_masks ) #shape=(32, 10)
        key_masks = tf.tile(key_masks, [num_heads, 1]) # (h*N, T_k)
        #print(key_masks) #shape=(256, 10) #8
        key_masks = tf.tile(tf.expand_dims(key_masks, 1), [1, tf.shape(queries)[1], 1]) # (h*N, T_q, T_k)
        #print(key_masks) #shape=(256, 10, 10) 在axes=1上增加一個維度
        paddings = tf.ones_like(outputs)*(-2**32+1) #全1的矩陣,形狀類似與outputs的形狀
        #print(paddings) ##shape=(256, 10, 10)
        #tf.equal功能:對比兩個矩陣/向量的元素是否相等,如果相等就返回True,反之返回False。
        #where(condition, x=None, y=None, name=None)
        #condition, x, y 相同維度,condition是bool型值,True/False where(condition)的用法,
        #返回值,是condition中元素爲True對應的索引
        outputs = tf.where(tf.equal(key_masks, 0), paddings, outputs) # (h*N, T_q, T_k)
        #等於0的位置是TRUE,其他的位置爲False ,TRUE取padding中的值,false爲outputs中的值
        #print(outputs) #shape=(256, 10, 10) #8 * 64 ,10,10
        # Causality = Future blinding
        '''
        causality參數告知我們是否屏蔽未來序列的信息(解碼器self attention的時候不能看到自己之後的那些信息),這裏即causality爲True時的屏蔽操作。 
        
        '''

        if causality:
            diag_vals = tf.ones_like(outputs[0, :, :]) # (T_q, T_k)
            #print(diag_vals) #shape=(10, 10)
            #https://devdocs.io/tensorflow~python/tf/linalg/linearoperatorlowertriangular--線性代數庫
            '''
            將該矩陣轉爲三角陣tril。三角陣中,對於每一個T_q,凡是那些大於它角標的T_k值全都爲0,這樣作爲mask就可以讓query只取它之前的key
            (self attention中query即key)。由於該規律適用於所有query,接下來仍用tile擴展堆疊其第一個維度,構成masks,
            shape爲(h*N, T_q,T_k).------就是下三角矩陣,以上操作就可以當不需要來自未來的key值時將未來位置的key的score設置爲極小
            '''
            tril = tf.linalg.LinearOperatorLowerTriangular(diag_vals).to_dense() # (T_q, T_k)
            #print(tril) #shape=(10, 10)
            masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(outputs)[0], 1, 1]) # (h*N, T_q, T_k)
            #print(masks) #shape=(256, 10, 10)
            #當不需要來自未來的key值時將未來位置的key的score設置爲極小
            paddings = tf.ones_like(masks)*(-2**32+1)
            outputs = tf.where(tf.equal(masks, 0), paddings, outputs) # (h*N, T_q, T_k)
            #print("-----")
        # Activation 將attention score了利用softmax轉化爲加起來爲1的權值,

        outputs = tf.nn.softmax(outputs) # (h*N, T_q, T_k)

        # Query Masking
        '''
        所謂要被mask的內容,就是本身不攜帶信息或者暫時禁止利用其信息的內容。這裏query mask也是要將那些初始值爲0的queryies
        (比如一開始句子被PAD填充的那些位置作爲query)mask住。代碼前三行和key mask的方式大同小異,只是擴展維度等是在最後一個維度展開的。
        操作之後形成的query_masks的shape爲[h*N, T_q, T_k]。
    第四行代碼直接用outputs的值和query_masks相乘。這裏的outputs是之前已經softmax之後的權值。所以此步之後,需要mask的權值會乘以0,
    不需要mask的乘以之前取的正數的sign爲1所以權值不變。實現了query_masks的目的。
    這裏源代碼中的註釋應該寫錯了,outputs的shape不應該是(N, T_q, C)而應該和query_masks 的shape一樣,爲(h*N, T_q, T_k)。
        '''
        query_masks = tf.sign(tf.abs(tf.reduce_sum(queries, axis=-1))) # (N, T_q)
        query_masks = tf.tile(query_masks, [num_heads, 1]) # (h*N, T_q)
        '''
        由於每個queries都要對應這些keys,而mask的key對每個queries都是mask的。而之前的key_masks只相當於一份mask,所以擴充之前key_masks的維度,
        在中間加上一個維度大小爲queries的序列長度。然後利用tile函數複製相同的mask值即可。 
        '''
        query_masks = tf.tile(tf.expand_dims(query_masks, -1), [1, 1, tf.shape(keys)[1]]) # (h*N, T_q, T_k)
        #print(query_masks ) #shape=(256, 10, 10)
        outputs *= query_masks # broadcasting. (N, T_q, C) #對應位置的元素相乘
        '''
        query mask也是要將那些初始值爲0的queryies(比如一開始句子被PAD填充的那些位置作爲query) mask住。
        outputs的值和query_masks相乘。這裏的outputs是之前已經softmax之後的權值。所以此步之後,需要mask的權值會乘以0,
        不需要mask的乘以之前取的正數的sign爲1所以權值不變。實現了query_masks的目的。 
        '''
        #print(outputs) #shape=(256, 10, 10)
        # Dropouts
        outputs = tf.layers.dropout(outputs, rate=dropout_rate, training=tf.convert_to_tensor(is_training))
        # Weighted sum
        outputs = tf.matmul(outputs, V_) # ( h*N, T_q, C/h)
        #print(V_) #shape=(256, 10, 64)
        #print(outputs) #shape=(256, 10, 64)
        # Restore shape
        outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2 ) # (N, T_q, C)
        #print(outputs) #shape=(32, 10, 512)
        # Residual connection
        outputs += queries  #殘差網絡的思想
        # Normalize
        outputs = normalize(outputs) # (N, T_q, C)
        #print(outputs) #shape=(32, 10, 512)
    return outputs

def feedforward(inputs, 
                num_units=[2048, 512],
                scope="multihead_attention", 
                reuse=None):
    '''Point-wise feed forward net.
    
    Args:
      inputs: A 3d tensor with shape of [N, T, C].
      num_units: A list of two integers.
      scope: Optional scope for `variable_scope`.
      reuse: Boolean, whether to reuse the weights of a previous layer
        by the same name.
        
    Returns:
      A 3d tensor with the same shape and dtype as inputs
    '''
    with tf.variable_scope(scope, reuse=reuse):
        # Inner layer
        params = {"inputs": inputs, "filters": num_units[0], "kernel_size": 1,
                  "activation": tf.nn.relu, "use_bias": True}
        outputs = tf.layers.conv1d(**params)
        
        # Readout layer
        params = {"inputs": outputs, "filters": num_units[1], "kernel_size": 1,
                  "activation": None, "use_bias": True}
        outputs = tf.layers.conv1d(**params)
        
        # Residual connection
        outputs += inputs
        
        # Normalize
        outputs = normalize(outputs)
    
    return outputs
#把之前的one_hot中的0改成了一個很小的數,1改成了一個比較接近於1的數
def label_smoothing(inputs, epsilon=0.1):
    '''Applies label smoothing. See https://arxiv.org/abs/1512.00567.
    
    Args:
      inputs: A 3d tensor with shape of [N, T, V], where V is the number of vocabulary.
      epsilon: Smoothing rate.
    
    For example,
    
    ```
    import tensorflow as tf
    inputs = tf.convert_to_tensor([[[0, 0, 1], 
       [0, 1, 0],
       [1, 0, 0]],

      [[1, 0, 0],
       [1, 0, 0],
       [0, 1, 0]]], tf.float32)
       
    outputs = label_smoothing(inputs)
    
    with tf.Session() as sess:
        print(sess.run([outputs]))
    
    >>
    [array([[[ 0.03333334,  0.03333334,  0.93333334],
        [ 0.03333334,  0.93333334,  0.03333334],
        [ 0.93333334,  0.03333334,  0.03333334]],

       [[ 0.93333334,  0.03333334,  0.03333334],
        [ 0.93333334,  0.03333334,  0.03333334],
        [ 0.03333334,  0.93333334,  0.03333334]]], dtype=float32)]   
    ```    
    '''
    K = inputs.get_shape().as_list()[-1] # number of channels
    return ((1-epsilon) * inputs) + (epsilon / K)
    
    

            

模型配置的參數:

" attention_probs_dropout_prob": 0.1, #乘法attention時,softmax後dropout概率
"hidden_act": "gelu", #激活函數
 "hidden_dropout_prob": 0.1, #隱藏層dropout概率
 "hidden_size": 768, #隱藏單元數
 "initializer_range": 0.02, #初始化範圍 
"intermediate_size": 3072, #升維維度 
"max_position_embeddings": 512, #一個大於seq_length的參數,用於生成position_embedding "num_attention_heads": 12, #每個隱藏層中的attention head數
 "num_hidden_layers": 12, #隱藏層數
 "type_vocab_size": 2, #segment_ids類別 [0,1] 
"vocab_size": 30522 #詞典中詞數

這裏的輸入參數:input_ids,input_mask,token_type_ids對應上篇文章中輸出的input_ids,input_mask,segment_ids

transformer:

由於 Self-Attention 是每個詞和所有詞都要計算 Attention,所以不管他們中間有多長距離,最大的路徑長度也都只是 1。可以捕獲長距離依賴關係

attention表示成k、q、v的方式:

傳統的attention(sequence2sequence問題):

上下文context表示成如下的方式(h的加權平均):

那麼權重alpha(attention weight)可表示成Q和K的乘積,小h即V(下圖中很清楚的看出,Q是大H,K和V是小h):

上述可以做個變種,就是K和V不想等,但需要一一對應,例如:

  • V=h+x_embedding
  • Q = H
  • k=h

乘法VS加法attention

加法注意力:

還是以傳統的RNN的seq2seq問題爲例子,加性注意力是最經典的注意力機制,它使用了有一個隱藏層的前饋網絡(全連接)來計算注意力分配:

乘法注意力:

就是常見的用乘法來計算attention score:

乘法注意力不用使用一個全連接層,所以空間複雜度佔優;另外由於乘法可以使用優化的矩陣乘法運算,所以計算上也一般佔優。

論文中的乘法注意力除了一個scale factor:

論文中指出當dk比較小的時候,乘法注意力和加法注意力效果差不多;但當d_k比較大的時候,如果不使用scale factor,則加法注意力要好一些,因爲乘法結果會比較大,容易進入softmax函數的“飽和區”,梯度較小。

self-attention

以一般的RNN的S2S爲例子,一般的attention的Q來自Decoder(如下圖中的大H),K和V來自Encoder(如下圖中的小h)。self-attention就是attention的K、Q、V都來自encoder或者decoder,使得每個位置的表示都具有全局的語義信息,有利於建立長依賴關係。

Layer normalization(LN)

batch normalization是對一個每一個節點,針對一個batch,做一次normalization,即縱向的normalization:

layer normalization(LN),是對一個樣本,同一個層網絡的所有神經元做normalization,不涉及到batch的概念,即橫向normalization:

BN適用於不同mini batch數據分佈差異不大的情況,而且BN需要開闢變量存每個節點的均值和方差,空間消耗略大;而且 BN適用於有mini_batch的場景。

LN只需要一個樣本就可以做normalization,可以避免 BN 中受 mini-batch 數據分佈影響的問題,也不需要開闢空間存每個節點的均值和方差。

但是,BN 的轉換是針對單個神經元可訓練的——不同神經元的輸入經過再平移和再縮放後分布在不同的區間,而 LN 對於一整層的神經元訓練得到同一個轉換——所有的輸入都在同一個區間範圍內。如果不同輸入特徵不屬於相似的類別(比如顏色和大小,scale不一樣),那麼 LN 的處理可能會降低模型的表達能力。

encoder:

  • 輸入:和conv s2s類似,詞向量加上了positional embedding,即給位置1,2,3,4...n等編碼(也用一個embedding表示)。然後在編碼的時候可以使用正弦和餘弦函數,使得位置編碼具有週期性,並且有很好的表示相對位置的關係的特性(對於任意的偏移量k,PE[pos+k]可以由PE[pos]表示):

  • 輸入的序列長度是n,embedding維度是d,所以輸入是n*d的矩陣
  • N=6,6個重複一樣的結構,由兩個子層組成:
    • 子層1:
      • Multi-head self-attention
      • 殘餘連接和LN:
        • Output = LN (x+sublayer(x))

    • 子層2:
      • Position-wise fc層(跟卷積很像)
      • 對n*d的矩陣的每一行進行操作(相當於把矩陣每一行鋪平,接一個FC),同一層的不同行FC層用一樣的參數,不同層用不同的參數(對於全連接的節點數目,先從512變大爲2048,再縮小爲512):

  • 整個encoder的輸出也是n*d的矩陣

decoder:

•輸入:假設已經翻譯出k個詞,向量維度還是d

•同樣使用N=6個重複的層,依然使用殘餘連接和LN

•3個子層,比encoder多一個attention層,是Decoder端去attend encoder端的信息的層:

  • Sub-L1:
    self-attention,同encoder,但要Mask掉未來的信息,得到k*d的矩陣

  • Sub-L2:和encoder做attention的層,輸出k*d的矩陣
  • Sub-L3:全連接層,輸出k*d的矩陣,用第k行去預測輸出y

mutli-head attention:

MultiHead可以看成是一種ensemble方式,獲取不同子空間的語義:

獲取每個子任務的Q、K、V:

  • 通過全連接進行線性變換映射成多個Q、K、V,線性映射得到的結果維度可以不變、也可以減少(類似降維)
  • 或者通過Split對Q、K、V進行劃分(分段)

如果採用線性映射的方式,使得維度降低;或者通過split的方式使得維度降低,那麼多個head做attention合併起來的複雜度和原來一個head做attention的複雜度不會差多少,而且多個head之間做attention可以並行。

decoder的輸入:

self.y的輸出

[[1008 3936 1924    4  401 5087 5651    3    0    0]
 [ 141   25    4  101 3180    6  362  552    3    0]
 [  15    4  420   12  845    3    0    0    0    0]
 [  79  243    6    1   15   42 1614  634    3    0]
 [ 527  119   30   85  976    3    0    0    0    0]
 [   1   12  669  193   20  135    1 7288    3    0]
 [   1    1    1    1    3    0    0    0    0    0]
 [  78   58    5 1934  764   87    3    0    0    0]
 [  65   20   44  484  304 1190    3    0    0    0]
 [  92  132    8 3174    5 1286  217    3    0    0]
 [ 534    1   81  979 2304    3    0    0    0    0]
 [  15  334   54   44  505  145    8  222 3052    3]
 [2124 2626 1435   14    3    0    0    0    0    0]
 [ 291  429   39    3    0    0    0    0    0    0]
 [   1    6  438  162    8  178 5506  702    3    0]
 [ 166  106   11   21    5  102 1403   97    3    0]
 [  47  578  116   18  387    3    0    0    0    0]
 [  11  100    5  173   28    1  459    3    0    0]
 [ 193   14   12    3    0    0    0    0    0    0]
 [  80  910   12 1581 1713    3    0    0    0    0]
 [  65   90  807    5   66   56    3    0    0    0]
 [  24   95   44 3973   85  340  327  110    3    0]
 [   1    1    1 1615    1    3    0    0    0    0]
 [  15   14   74  174   31    4 2982  512 1105    3]
 [ 350    3    0    0    0    0    0    0    0    0]
 [  96  246  425    5   54    3    0    0    0    0]
 [3585 1357  117 1105    1   61 5155    3    0    0]
 [  79  159    8    1  898    3    0    0    0    0]
 [  79   33   40  352    3    0    0    0    0    0]
 [  79 3060  569    6  515   25    1   39    3    0]
 [ 120   94  955   10   56 1606    3    0    0    0]
 [  24   95   28   16  139    7  244  199  729    3]]

self.dec:

[[[-2.279155    1.0991251  -1.5886356  ...  1.4294004  -1.5982031
   -1.1408417 ]
  [-1.5231744  -0.05949117 -1.1321675  ... -1.4490161  -0.30499786
   -0.77806205]
  [-1.3187622  -0.15014127 -1.13905    ... -3.1452506  -2.686214
   -0.5845207 ]
  ...
  [-0.41217917  1.4415227  -1.2828536  ... -0.3612737  -0.4510218
   -0.7630521 ]
  [-0.7186153   0.86343354 -1.883712   ... -0.91078717 -0.01014436
    0.07463718]
  [-0.86691934  0.98398304 -2.3404958  ... -0.8095867   0.442111
   -0.04082058]]

 [[-2.631531    0.8046703  -1.8410189  ...  0.98089796 -1.0553441
   -1.0271642 ]
  [-2.527477    0.47748724 -1.5274042  ... -0.9979782  -1.4225875
self.x德語 [[  14   29    1  111  744    3    0    0    0    0]
 [ 344  303    3    0    0    0    0    0    0    0]
 [ 352   68   13   22 2974    1    1    3    0    0]
 [  37   68  459  591    8    9   94    3    0    0]
 [ 100  210   70  737    3    0    0    0    0    0]
 [  34    1    4 3720  492  119    1 2668    3    0]
 [  21    6  660 1234  116   25 4757  539    1    3]
 [ 832   12   46  160  154   32    3    0    0    0]
 [  34    7   20 1067    1    3    0    0    0    0]
 [  25  207   19   18 2361    1   28    3    0    0]
 [  34    1 2921 1123    3    0    0    0    0    0]
 [  34    1 5514   10   56    1    3    0    0    0]
 [  61   14  110   78    8   19  334    3    0    0]
 [  21   15  420  356    3    0    0    0    0    0]
 [  14  171 3183  310 2075 2426 1024    3    0    0]
 [  65  362   13   35    1   10  217 4820    3    0]
 [  25   75    8  557  124 2810 2124    9   29    3]
 [ 774    1    1    1    3    0    0    0    0    0]
 [   1    7 1149 4973    3    0    0    0    0    0]
 [  21   11   99   19 2640  107  215    3    0    0]
 [ 163 1116   29 1335  402    9  197    3    0    0]
 [ 120 2161  918  157   15   23    6  400 1335    3]
 [  21   11  174   89 1260    9   86   71    3    0]
 [  14   48    4 3168  111   54   45  375    3    0]
 [  34  617  225   23  465    3    0    0    0    0]
 [  95  886   12 3115    1    3    0    0    0    0]
 [ 340  269   12   16  426  874 1797  156  117    3]
 [ 204   28    4 3979   30    8 2287   30    1    3]
 [ 477   52   10  405 3787  168  902   51    3    0]
 [ 100   28    4 1241   98 2820    1    3    0    0]
 [  14  589   27   64    1    3    0    0    0    0]
self.y英語 [[   1   27   13  134    4 1534 3332    3    0    0]
 [5502    7  349    8  701 8414  114    3    0    0]
 [ 125  101  444 2134 2960 1767   10  480 2865    3]
 [ 120   21  143 4641 1866    7 1149    3    0    0]
 [ 291   12   33 2686    3    0    0    0    0    0]
 [  92   55    1  955    3    0    0    0    0    0]
 [1540    9    1    7    1    3    0    0    0    0]
 [3050   37    4  462  502   12 8718    3    0    0]
 [  11  100   13    5   29  559    3    0    0    0]
 [ 125  175   48   73   12  169 4592 7722    3    0]
 [5070    7 2667   47    1    3    0    0    0    0]
 [ 231   12   41    1   57 1708    3    0    0    0]
 [   1    6   13   20    1    3    0    0    0    0]
 [  15    4  420   12  111    3    0    0    0    0]
 [ 235    4  314  457  261   21    8  559 3722    3]
 [  15  115 2008    3    0    0    0    0    0    0]
 [  65   94 4087   17  160    3    0    0    0    0]
 [  11 3803  130    1 1661    1    4 4373    3    0]
 [  49    1    9  332  303  110    3    0    0    0]
 [   1  162    6    4  614  181  100   17    3    0]
 [  24   11  182 4885    3    0    0    0    0    0]
 [  96  488    1    1   79  421    3    0    0    0]
 [ 965    8 1126   28   20   13    1    3    0    0]
 [ 120   63  784    5 2063 3565    3    0    0    0]
 [  24   16  132    5  174    9 4755  392    3    0]
 [ 166   44  246  366    3    0    0    0    0    0]
 [  24   28   25  129   22 1411    3    0    0    0]
 [ 737  298 3720  162    3    0    0    0    0    0]
 [  11  100    5   75  477    4 1054    3    0    0]
 [  65  164   25  156    1   46 8431    3    0    0]
 [  80   18  198 2343  624    3    0    0    0    0]
 [ 640  281   37  162    3    0    0    0    0    0]]

deecoder部分:
[[   2   24  334   54  164   25 2337  114    3    0]
 [   2    1   10  423   48   20  115   91   25   17]
 [   2   47  114   37   18  234   10  417 4175   10]
 [   2  166    1  121 3475  409    5    8    1   97]
 [   2   80   12    8 1924  901  159    8 7055  901]
 [   2   96   86    1   79    8    1 2252    3    0]
 [   2 2171   87   11   18    1   23   38  136 4969]
 [   2  120    1 1326 3555    3    0    0    0    0]
 [   2   79  345 7208   19    4 6423    3    0    0]
 [   2  589   16   21  263 5134   53    6  381  267]
 [   2  601   44  265   17   37   22   19  643    3]
 [   2   80   83  104 1242    3    0    0    0    0]
 [   2  141   12    9 2443   77  339  122    5 1571]
 [   2 4358  216  137   61    1   10    3    0    0]
 [   2 3130    1    1    3    0    0    0    0    0]
 [   2   96  181  242    9   89    3    0    0    0]
 [   2   65   20   19    1 4223  127    3    0    0]
 [   2   15  138   51 1303   23   50 1386    3    0]
 [   2   15   13   27  173   56   22  926  460    3]
 [   2   65   21    5 1942    9    3    0    0    0]
 [   2   79    8   51  143  676 4787    3    0    0]
 [   2   15   42  227   19 1225    3    0    0    0]
 [   2   15   16   63    5  286  131    6    8 4174]
 [   2  166    1   23    4    1    3    0    0    0]
 [   2   15   17 4367   12 3481 3562    7  298    1]
 [   2   15   11  100   38  377    5   29   23   13]
 [   2   15  981  112   18    1    8 1169    3    0]
 [   2   15   16 2135  523    1    3    0    0    0]
 [   2 4264   17   12   33   68  208    3    0    0]
 [   2   80   12    4 4979    1    3    0    0    0]
 [   2   11  823   13   51  109    3    0    0    0]
 [   2   79   33 1431  390    3    0    0    0    0]]
 [  25    1  201  526    3    0    0    0    0    0]]
self.decoder_inputs的構成tf.concat((tf.ones_like(self.y[:, :1])*2, self.y[:, :-1]), -1)
tf.ones_like(self.y[:, :1])
[[1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]
 [1]]
tf.ones_like(self.y[:, :1])*2
[[2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]
 [2]]
self.y[:, :-1]
[[  11   74  123  350   95   41 3029    3    0]
 [3510  144 3781    3    0    0    0    0    0]
 [   1   92  738  173    8    1    3    0    0]
 [  96   94  547   29    8 4941 1096    3    0]
 [  49   64  220    3    0    0    0    0    0]
 [ 203   13    3    0    0    0    0    0    0]
 [2917 1523   59    4  204   35  330    3    0]
 [  24   97   44   17   12   41    1 2429   87]
 [  79    4  985 4074 1143  557 3187    3    0]
 [  92   55    4 7987 3611   12    8 1578   37]
 [  24    4  212  806   12  159    1    3    0]
 [1410  336   68 2515    3    0    0    0    0]
 [ 150   28  194   29 2887   17    1    3    0]
 [  15    4  101   37  500   12    1    3    0]
 [  96   18    1 2972   43   11  139   14    3]
 [2629 6333 2802    3    0    0    0    0    0]
 [ 610    4    1   11   90  445    9    3    0]
 [7991  138   10 1481    3    0    0    0    0]
 [  24 5119  122   10    4  360  984    3    0]
 [6026  689    3    0    0    0    0    0    0]
 [1033   34    9 1202    3    0    0    0    0]
 [ 203   13  231   18   14   57    4   89    3]
 [  43   17   12   33   69   11  182    3    0]
 [  11   18  275   93  311    3    0    0    0]
 [2016   22   84    4 1428  899    3    0    0]
 [  24   16  132    5   64 1493   25 1744  289]
 [  98 1298 3170  703  390    3    0    0    0]
 [  24   11  353  232  214   19    8    1    3]
 [  15   28   11  238   72 1850   54    3    0]
 [ 203   13    3    0    0    0    0    0    0]
 [ 231  278    9  489   12   33   53   48    3]
 [ 600   12  878    5 1582    4  572    3    0]] #由原來的32*10變成了32*9
tf.concat((tf.ones_like(self.y[:, :1])*2, self.y[:, :-1]), -1) 
 [[   2  394   33 1133   30   32    3    0    0    0]
 [   2 1675   28   13   64    3    0    0    0    0]
 [   2   15 4777  944    5  102    5    4 1871    3]
 [   2  291   12   33 2686    3    0    0    0    0]
 [   2   11  105 5707    3    0    0    0    0    0]
 [   2   11  119 4231   20    6    4 1577 1135    3]
 [   2  965    4  326   18 4762    3    0    0    0]
 [   2  284  967  111 1484  968 1622    3    0    0]
 [   2   15    6  191 3498    3    0    0    0    0]
 [   2   24   16   21    4    1    1    3    0    0]
 [   2 1160  101   91   20  159    1 3232    3    0]
 [   2   15   42   51  584    3    0    0    0    0]
 [   2  394   33 4681    3    0    0    0    0    0]
 [   2   80   12   41  448 5956   10    1 6524    3]
 [   2   98  416 2000   17   23    4  204    3    0]
 [   2   80   12  301    7 8061    3    0    0    0]
 [   2  284 1579    6    4    1    3    0    0    0]
 [   2  125    4 1385   88 6913 2257   20   25  162]
 [   2 2219 1346    3    0    0    0    0    0    0]
 [   2   67   12  490   28  124  260    5   34    3]
 [   2   47  315  177   12    1    3    0    0    0]
 [   2 6239   12    4  290    6    1    3    0    0]
 [   2   78    3    0    0    0    0    0    0    0]
 [   2   92  103  133  504   14    3    0    0    0]
 [   2   15   28   25    4  668    6 7226 2454    3]
 [   2   24  473  288  543  430  706   48   81    3]
 [   2  689   11  424   21 8420 6647   87    3    0]
 [   2  203   13    3    0    0    0    0    0    0]
 [   2   15   26   71    8  137    6  742    3    0]
 [   2   79   72  803    3    0    0    0    0    0]
 [   2  589   12   41  240    6    4 4257    3    0]
 [   2   15   39 6379    3    0    0    0    0    0]]

encoder的輸入是德語部分,k,q都是德語句子本身,v也是本句,開始self-attention,最後形成的是self.encoder。

decoder的輸入是英語部分,輸入的是英語句子,但是第一個數值都換成了2,其餘的10個數值不變,k,q,都是本身。最後形成self.decoder。同樣是self-attention,

在decoder和encoder鏈接的地方進行普通的attention,q=self.decoder,k=self.encoder,v=self.encoder。 softmax(q*k/d)*v

標籤y的輸入是英語部分。

最後計算概率的一層shape=(32, 10, 8767),8767代表單詞表的大小,最後給出的是對應概率最大的位置,與label對比,也就是和y對比。input_decoder做了特殊的處理:在decoder的輸入中,self-attention的部分要進行對未來信息的屏蔽,用來預防看到未來的信息,起最終的屏蔽後的結果是一個下三角矩陣(10,10)

1   0   0   0   0 0 0 0 0 0
 1   6   0   0   0 0 0 0 0 0
 1   6   10  0   0 0 0 0 0 0
 1   6   10  13  0 0 0 0 0 0
 1   6   10  13  15 0 0 0 0 0

...........................................................其意思是有encoder端的一句10個詞彙構成的語義向量並且配合矩陣第一行的第一個元素,預測出第二行的第二元素。

其實在預測階段是沒有decoder的輸入的部分的,只有根據encode端預測decoder的第一個字符,然後根據encoder的端加上預測出的第一個字符,預測第二個字符,直到預測出第10個字符

import tensorflow as tf
a = tf.constant([[[2, 2, 12,22], [3, 3, 31,23],[3, 4, 5,6], [3, 9, 3,8]]], dtype=tf.float32)
diag_vals = tf.ones_like(a[0, :, :])
tril = tf.linalg.LinearOperatorLowerTriangular(diag_vals).to_dense()

sess = tf.Session()
print(sess.run(tril))


[[1. 0. 0. 0.]
 [1. 1. 0. 0.]
 [1. 1. 1. 0.]
 [1. 1. 1. 1.]]

對於decoder的self.attention部分:數據的變換如上面的程序將(10,10)的矩陣轉換成下三角矩陣。

masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(outputs)[0], 1, 1])通過這條語句將形成(256,10,10)的結構,embedding=512,一句話有10個單詞,mutihead=8,一個批次有32句話。

轉換過程:(32,10,512)--8*(32,10,512/8)----8*(32,10,64)---(256,10,64)------->q*k---------(256,10,64)*(256,10,64)-----(256,10,10)---32句話,每句話有10個單詞,按照將512的空間分解成64的空間,然後進行self.attention的計算得出該句話的每個單詞同這句話的其他單詞的權重如下圖:1--10代表的是一句話中的10個單詞,連線上是權重,是通過softmax函數計算出的。

key的處理部分:32句話,每句話10個人單詞,(32,10,512)----最後一個維度相加得到(32,10),8個attention -----8個32*10,每個都是同樣的。(256,10)----8個一組,最後是(256,10,10),同樣的一句話變成了---------(8*32,10,10)以

a = tf.constant([[2, 2, 12,22],[3, 3, 31,23],[3, 4, 5,6]], dtype=tf.float32)  #3*4
tril = tf.tile(a,[8,1])
[[ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]
 [ 2.  2. 12. 22.]
 [ 3.  3. 31. 23.]
 [ 3.  4.  5.  6.]]
tril = tf.tile(tf.expand_dims(tril, 1), [1, 2, 1]) #(24, 2, 4)
[[[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]

 [[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]

 [[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]

 [[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]

 [[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]

 [[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]

 [[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]
[[ 2.  2. 12. 22.]
  [ 2.  2. 12. 22.]]

 [[ 3.  3. 31. 23.]
  [ 3.  3. 31. 23.]]

 [[ 3.  4.  5.  6.]
  [ 3.  4.  5.  6.]]]

經過計算後變成了(8*32,10,10)其中第二個維度中的10代表了一個句子中的第一個單詞重複10次,其他詞也一樣,然後進行權重與(10,10)的矩陣的相乘權重矩陣同樣爲10*10,代表的意思是第一行爲,第一個單詞同其他所有單詞的權重,第二行爲第二個單詞和其他所有單詞的權重,依次類推......................

[[25 55 95 36 34 88 47 19 35 84]
 [23 82 51 11 79 34 73 90 37 23]
 [47  4  4 21  3 77 72  9 29 26]
 [47 96 34 49 27 71  4 86 73 24]
 [92 99 85 37 44 67 28 67 90 30]
 [73 23 34 47 88 48 33 54 79 77]
 [87 79 45 56 58 25 16 60 77 22]
 [19  8 67 96 84 31 13 21 76 61]
 [49  5 56  7 75  0 12  9 56 93]
 [72 99 56  3  2 79 52 70 17 79]] 

padding

[[[-4.2949673e+09 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [-4.2949673e+09 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [-4.2949673e+09 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [-4.2949673e+09 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]]
.....................................

masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(outputs)[0], 1, 1])主要是爲了形成和shape(256,10,10)一樣形狀的張量,paddings = tf.ones_like(masks)*(-2**32+1),每個元素值都爲-4.2949673e+09爲最小值。outputs = tf.where(tf.equal(masks, 0), paddings, outputs)將(q×k)/(d^1/2),將其中爲0的元素替換成-4.2949673e+09,outputs =tf.nn.softmax(outputs)

然後對query進行mask,

a = tf.constant([[2, 2, 12,-2],[3, 3, 31,0],[3, 4, 0,0]], dtype=tf.float32)  #3*4  batchsize = 3
query_masks = tf.sign(tf.abs(a))
[[1. 1. 1. 1.]
 [1. 1. 1. 0.]
 [1. 1. 0. 0.]]
key_masks維度的擴充是在 1上,query_masks的維度擴充是在-1上,也就是最後一個維度上。

在msak的處理上,形成類似的張量,維度是在

[[[1. 0. 0. 0.]
  [1. 1. 0. 0.]
  [1. 1. 1. 0.]
  [1. 1. 1. 1.]]

 [[1. 0. 0. 0.]
  [1. 1. 0. 0.]
  [1. 1. 1. 0.]
  [1. 1. 1. 1.]]

 [[1. 0. 0. 0.]
  [1. 1. 0. 0.]
  [1. 1. 1. 0.]
  [1. 1. 1. 1.]]

 [[1. 0. 0. 0.]
  [1. 1. 0. 0.]
  [1. 1. 1. 0.]
  [1. 1. 1. 1.]]]

進行屏蔽後q×k/(d^1/2)的值類似於如下的輸出

[[[ 2.0000000e-01 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [ 5.1999998e-01  5.1999998e-01 -4.2949673e+09 -4.2949673e+09]
  [ 1.1200000e-01  1.1200000e-01  1.1200000e-01 -4.2949673e+09]
  [ 0.0000000e+00  0.0000000e+00  0.0000000e+00  0.0000000e+00]]

 [[ 4.3000001e-01 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [ 3.0000001e-01  3.0000001e-01 -4.2949673e+09 -4.2949673e+09]
  [ 0.0000000e+00  0.0000000e+00  0.0000000e+00 -4.2949673e+09]
  [ 0.0000000e+00  0.0000000e+00  0.0000000e+00  0.0000000e+00]]

 [[ 3.0000001e-01 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [ 4.0000001e-01  4.0000001e-01 -4.2949673e+09 -4.2949673e+09]
  [ 5.0000000e-01  5.0000000e-01  5.0000000e-01 -4.2949673e+09]
  [ 6.0000002e-01  6.0000002e-01  6.0000002e-01  6.0000002e-01]]

 [[ 8.9999998e-01 -4.2949673e+09 -4.2949673e+09 -4.2949673e+09]
  [ 4.3000001e-01  4.3000001e-01 -4.2949673e+09 -4.2949673e+09]
  [ 5.0999999e-01  5.0999999e-01  5.0999999e-01 -4.2949673e+09]
  [ 6.2000000e-01  6.2000000e-01  6.2000000e-01  6.2000000e-01]]]

成爲了一個下三角矩陣,也就形成了對未來信息的屏蔽。

函數運行的例子

#-*-coding:utf-8-*-
import tensorflow as tf
# x = [[1,2,3],[4,5,6]]
# y = [[7,8,9],[10,11,12]]
# condition3 = [[True,False,False],
#              [False,True,True]]
# condition4 = [[True,False,False],
#              [True,False,False]]
# with tf.Session() as sess:
#     print(sess.run(tf.where(condition3,x,y)))
#     print(sess.run(tf.where(condition4,x,y)))

#shape=(2, 2, 3)
# a = tf.constant([[[2, 2, 12,22], [3, 3, 31,23]],[[3, 4, 5,6], [3, 9, 3,8]]], dtype=tf.int32)
# b = tf.constant([[1, 7], [2, 9]], dtype=tf.int32)
# c = tf.constant([[1, 11], [2, 12]], dtype=tf.float32)
# oneHot = tf.one_hot(a,depth=35)
# # print(a)
# # print(b)
# # print(c)
# # d = tf.matmul(b,c)
# # Q_ = tf.concat(tf.split(a, 2, axis=2),axis=0) # (h*N, T_q, C/h)
# # print(Q_)
# with tf.Session() as sess:
#     # print(sess.run(tf.split(a, 2, axis=2)))
#     # print(Q_ )
#     # print(sess.run(Q_))
#     print(sess.run(oneHot))#, [3, 3, 31,23],[3, 4, 5,6], [3, 9, 3,8]
a = tf.constant([[2, 2, 12,-2],[3, 3, 31,0],[3, 4, 0,0],[3, 9, 3,8]], dtype=tf.float32)  #3*4  batchsize = 3
q = tf.constant([[2, 2, 12,-2],[3, 3, 31,0],[3, 4, 0,0]], dtype=tf.float32)

diag_vals = tf.ones_like(a)
outputs =  tf.constant([[0.2, 0.52, 0.112,0],[0.43, 0.3, 0,0],[0.3, 0.4, 0.5,0.6],[0.9, 0.43, 0.51,0.62]], dtype=tf.float32)
tril = tf.linalg.LinearOperatorLowerTriangular(diag_vals).to_dense()
masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(tril)[0], 1, 1])
paddings = tf.ones_like(masks)*(-2**32+1)

#tril = tf.expand_dims(a, 0)
outputs = tf.tile(outputs,[1,1])
outputs =  tf.tile(tf.expand_dims(outputs, -1), [1, 1, 4])
# print(tf.Session().run(outputs))
# query_masks = tf.sign(tf.abs(q))
# query_masks = tf.tile(query_masks, [8, 1]) # (h*N, T_q)
# query_masks = tf.tile(tf.expand_dims(query_masks, -1), [1, 1, 4])

# tril = tf.tile(a,[8,1]) #(24,4)===(3*8,4)
# masks = tf.tile(tf.expand_dims(tril, 1), [1, 4, 1]) #(24, 2, 4)
#
# paddings = tf.ones_like(masks)*(-2**32+1)  #-4 294 967 295
#
# outputs = tf.where(tf.equal(masks, 0), paddings, outputs)
sess = tf.Session()
# # outputs = tf.where(tf.equal(masks, 0), paddings, outputs)
# outputs *=query_masks
print(sess.run(tf.where(tf.equal(masks, 0), paddings, outputs)))
# print(sess.run(tf.equal(masks, 0)))
# print("---",sess.run(outputs))

# import numpy as np
#
# num = []
# arr = np.random.random((10,10))
#
# print(arr)

最後的輸出 outputs*v至此整個attention的過程結束。(在傳統的attention中v和k表示的同一個句子,而q表示的是目標句子),在預測未來的目標語句時,對於目標語句中未來的信息,要進行屏蔽,避免影響。

self.target,留下非0 的是1 ,0的位置還是0

[[1. 1. 1. 1.]
 [1. 1. 1. 0.]
 [1. 1. 0. 0.]
 [1. 1. 1. 1.]]

 

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