Plusieurs questions sur Transformer

      Transformer a été proposé dans « Attention Is All You Need » et sa structure est la suivante :

Insérer la description de l'image ici

       Il existe de nombreux articles expliquant Transformer, je ne les répéterai donc pas ici. Vous pouvez vous référer au Document 1 et au Document 2.

      Cet article veut simplement parler de certains des problèmes que j'ai rencontrés lors de la lecture de l'article Transformer et noter ma compréhension de ces problèmes.

Question 1 : Pourquoi diviser par nsp \sqrt{d_k}dk ?

Insérer la description de l'image ici
      quand dk d_kdkLorsqu'il augmente, cela signifie que l'opération du produit scalaire entre q et k augmentera, tant que qiki q_ik_iqjekjeLégèrement plus grandes que les autres valeurs, après softmax, la plupart des valeurs deviendront très petites, proches de 0, ce qui rendra leur gradient très petit.

      Nous supposons que les distributions de q et k obéissent toutes deux à la distribution normale standard, c'est-à-dire que leur moyenne est 0 et la variance est 1. Une fois l'opération de multiplication par points effectuée : la distribution devient une moyenne de 0 et une variance de
Insérer la description de l'image ici
      dk d_kdk[Cela peut être déduit en utilisant les propriétés de moyenne et de variance], lorsque nsp d_kdkLorsqu'elle est très grande, cela signifie que la variance de q*k est très grande et que la distribution aura tendance à être raide (la variance de la distribution est grande et la distribution sera concentrée dans les zones avec de grandes valeurs absolues), ce qui entraînera la polarisation des valeurs après le statut softmax().

      Fais une expérience simple, dk d_kdkEn prenant respectivement 5 et 100, dk d_k est généré aléatoirement.dkq et k obéissent à la distribution normale standard, et après multiplication de points via la fonction softmax, l'image est la suivante :
      Lorsque dk d_kdk=5 :
nsp = 5
      quand nsp d_kdk=100 :
Insérer la description de l'image ici
      quand je mets dk d_kdk=100, q et k sont multipliés par des points et divisés par dk \sqrt{d_k}dk Finalement, l'image devient :
Insérer la description de l'image ici
Question 2 : Le rôle de l'attention multi-têtes

      L'attention mappe la requête et la clé dans le même espace de grande dimension pour calculer la similarité, tandis que l'attention multi-têtes correspondante divise la requête et la clé en h petites séquences et les mappe à différents sous-espaces de l'espace de haute dimension pour calculer la similarité. en effet, lorsque le nombre total de paramètres des deux méthodes reste inchangé, Attention a des distributions différentes dans différents sous-espaces. Concat diversifie les informations de la couche Attention et augmente la capacité d'expression de Attention.
Insérer la description de l'image ici

Troisième question : codage positionnel

      Étant donné que Transformer traite des problèmes de séquence et n'a pas la capacité de capturer les positions des données, des informations de position supplémentaires doivent être ajoutées. Les informations de position ici peuvent être absolues ou relatives, pouvant être entraînées ou non. Informations, dans l'article, l'auteur propose d'utiliser sin et cos pour représenter les informations de position relative de la séquence :
Insérer la description de l'image ici
      où pos est la position et i est la dimension.

      Sin et cos sont des fonctions périodiques, et pour toute valeur x, la valeur de la fonction est déterminée de manière unique. Lorsque la position pos posp os est décalé de k unités (enregistrées sous la formepos + k pos+kpos _+k ),PE pos + k PE_{pos+k}P.E. _po + k _Vous pouvez utiliser PE pos PE_{pos}P.E. _pos _Exprimé sous la forme d'un multiple linéaire de .

Question 4 : couche normale et lot normal

      Supposons que l'entrée soit [batch_size,channels,H,W]
Insérer la description de l'image ici

      Batch Normal, comme le montre la première image, calcule la moyenne et la variance pour chaque canal de tous les échantillons du petit lot (batch_size), puis les normalise. Batch Normal est lié à batch_size. Lorsque batch_size est petit, la normalisation de chaque petit lot ne suffit pas pour représenter la distribution de l'ensemble des données, et la variance moyenne calculée et d'autres informations nécessitent un espace de stockage supplémentaire. Pour une profondeur fixe, DNN et CNN sont plus approprié.

      Layer Normal, comme le montre la deuxième image, normalise la moyenne et la variance de chaque échantillon. Cela ne dépend pas de la taille de batch_size et de la profondeur de la séquence d'entrée, et est plus adapté au RNN. Référence

18/05/2023 Quelques enregistrements de code lors de l'apprentissage à nouveau de Transformer.
La référence de base pour cette partie du code et du texte est https://b23.tv/Vw6IYII

tenseur de masque

Le tenseur du masque se compose généralement de 0 et 1, ce qui indique si la position actuelle est masquée ou non. Dans Transformer, le tenseur de masque est principalement utilisé pour l'attention. Les calculs dans certains tenseurs d'attention générés peuvent être obtenus en connaissant les informations futures. Les informations futures sont vues car l'intégralité du résultat de sortie est traitée en même temps lors de l'intégration, mais généralement le tenseur de masque est utilisé pour l'attention. Le décodeur ne peut pas produire le résultat final en une seule fois, mais doit être synthétisé à partir du résultat précédent encore et encore. Par conséquent, les informations futures pourront être utilisées à l’avance. Par conséquent, un tenseur de masque est nécessaire pour masquer les informations futures.

# 构建掩码张量函数
def subsequent_mask(size):
    atten_shape=(1,size,size)
    subsequent_mask=np.triu(np.ones(atten_shape),k=1).astype('uint8')
    return torch.from_numpy(1-subsequent_mask)
    
plt.figure(figsize=(5,5))
plt.imshow(subsequent_mask(20)[0])
plt.show()

Insérer la description de l'image ici
L'image ci-dessus est une illustration du tenseur du masque. Le jaune représente la partie occultée, le violet représente la partie visible, l'abscisse est la position du mot cible et l'ordonnée est la position visible du mot. Par exemple, lorsque x=5, y=4, cela signifie qu'à la position du cinquième mot, seuls les 4 premiers mots peuvent être visualisés.

code d'attention

# 注意力机制
def attention(query,key,value,mask=None,dropout=None):
    '''
    args:
        query
        key
        value
        mask:是否使用掩码
        dropout:dropout函数
    '''
    d_k=query.size(-1)  # 词向量维度,一般设为512
    scores=torch.matmul(query,key.transpose(-2,-1))/math.sqrt(d_k)
    
    if mask is not None:
        # mask维度和scores一样,用于遮掩满足一定条件的scores位置的值,用于掩码注意力机制中
        # mask一般为上三角矩阵,这里的意思是将mask中为0的位置对应的scores位置的值用1e-9进行填充
        scores=scores.masked_fill(mask==0,1e-9)
        
    p_attn=F.softmax(scores)
    if dropout is not None:
        p_attn=dropout(p_attn)
        
    return torch.matmul(p_attn,value),p_attn

Explication d'image de Q, K et V :

假设给出一段文本,需要使用一些关键词对它进行描述:
key表示提前给的提示信息,value是第一次看到后脑中猜测的信息,qurey表示给出的这段文本。
一般key和value值相同,这种为一般的注意力机制。当key=value=query时,成为自注意力机制。

mécanisme d'attention multi-têtes

def clone(module,nums):
    return nn.ModuleList([module for _ in range(nums)])


class MultiHeadedAttention(nn.Module):
    def __init__(self,head,embedding_dim,dropout=0.1):
        super(MultiHeadedAttention,self).__init__()
        '''   
        head:多头数目
        embedding_dim:词嵌入维度
        '''
        assert embedding_dim%head == 0 #可以整除
        
        self.d_k=embedding_dim/head  # 每个头分配的词嵌入维度
        self.head=head
        
        self.linears=clone(nn.Linear(embedding_dim,embedding_dim),4)
        self.attn=None
        self.dropout=nn.Dropout(p=dropout)
        
    
    def forward(self,query,key,value,mask=None):
        if mask is not None:
            mask=mask.unsqueeze(1) # 使用unsqueeze扩展维度,表示多头中的第头
        
        bach_size=query.size(0)  #表示有多少样本
        
        # 多头注意力机制
        query,key,value=[model(x).view(bach_size,-1,self.head,self.d_k).transpose(1,2) 
                         for model,x in zip(self.linears,(query,key,value))]
        
        # 传入attention函数
        x,self.attn=attention(query,key,value,mask,self.dropout)
        
        x=x.transpose(1,2).contiguous().view(bach_size,-1,self.head*self.d_k)
        
        return self.linears[-1](x)

      Ce qui précède sont les problèmes que j'ai rencontrés lors de la lecture de l'article de Transformer et certaines de mes propres réflexions. S'il y a quelque chose d'écrit de manière incorrecte ou si l'expression n'est pas claire, veuillez me donner quelques conseils.

Je suppose que tu aimes

Origine blog.csdn.net/qq_44846512/article/details/114364559
conseillé
Classement