赞
踩
多头注意力机制是在自注意力机制的基础上发展起来的,是自注意力机制的变体,旨在增强模型的表达能力和泛化能力。它通过使用多个独立的注意力头,分别计算注意力权重,并将它们的结果进行拼接或加权求和,从而获得更丰富的表示。
在自注意力机制中,每个单词或者字都仅仅只有一个 q、k、v与其对应,如下图所示;
为什么需要多个head?
在做self-attention的时候是用q去找相关的k,但是相关有很多不同形式和定义,不同q负责不同种类的相关性。
多头注意力机制则是在 ai 乘以一个q,k,v后,会再分配多个 q,k,v,这里以2个 q,k,v 为例,如下图所示:
在多头注意力机制中, ai 会先乘 q矩阵,*qi=Wqai ;
其次,会为其多分配两个head,以 q为例,包括: qi,1,qi,2 ;
同样地,k和 v 也是一样的操作。
那么,下面就是 q和 k 的点乘操作了,在多头注意力机制中,有多个q 和 k,究竟应该选择哪个进行操作呢?
其实很简单,就是看下标,如下图所示。 qi,1,会和 ki,1和kj,1>进行点乘,再进行softmax操作
将对应的attention分数乘对应的v, 比如head1的qi,1和 ki,1和kj,1进行点乘(dot_product)后,得到相应的注意力分数,归一化后分别和 vi,1和vj,1相乘,再求和得到注意力输出,head2也是同时这样进行的,得到bi,1和bi,2.
将head1和head2得到的输出拼接起来,进行transpose就得到了注意力输出bi
single-head-attention中,从每个a中提取出q、k、v,他们的维度为d-model(每个token的维度)
multi-head-attention,与singlehead一样,先得出q、k、v,不同的是,每个q、k、v还要分出多个head
将多头得到的bii先按照列再按行con-cat,可以得到m x d_model的矩阵,m为token个数,d_model为每个token的维度
import torch from torch import nn as nn #dim_in:输入中每个token的维度,也就是输入x的最后一个维度 #d_model:single-head-attention情况下q、k、v总的向量长度 #num_heads:head个数 class MultiHead_SelfAttention(nn.Module): def __init__(self,input_dim,num_heads): super().__init__() self.num_heads=num_heads self.head_dim=input_dim//num_heads#head的维度为输入维度除以head个数,方便后面拼接 assert input_dim%num_heads==0 ,"Input dimension must be divisible by the number of heads." # Linear layers for the query, key, and value projections for each head self.query=nn.Linear(input_dim,input_dim) self.key=nn.Linear(input_dim,input_dim) self.value=nn.Linear(input_dim,input_dim) self.output_linear=nn.Linear(input_dim,input_dim) def forward(self,x): batch_size,seq_len,input_dim=x.size() #输入数据shape=[batch_size,token个数,token长度] #将输出向量经过矩阵乘法后拆分为多头 query=self.query(x).view(batch_size,seq_len,self.num_heads,self.head_dim) #输入数据shape=[batch_size,token个数,head数,head维度] key=self.query(x).view(batch_size, seq_len, self.num_heads, self.head_dim) value=self.query(x).view(batch_size, seq_len, self.num_heads, self.head_dim) #对调序列的长度和head个数(batch_size, seq_len, num_heads, head_dim) to (batch_size, num_heads, seq_len, head_dim) #方便后续矩阵乘法和不同头部的注意力计算 query=query.transpose(1,2)#(batch_size, num_heads, seq_len, head_dim) key=key.transpose(1,2) value=value.transpose(1,2) #计算注意力分数和权重matmul:最后两个维度做矩阵乘法 attention_scores=torch.matmul(query,key.transpose(-2,-1))/torch.sqrt(torch.tensor(self.head_dim,dtype=torch.float)) #query:(batch_size, num_heads, seq_len, head_dim) * key(batch_size,num_heads,head_dim,seq_len) #attention_scores:(batch_size, num_heads, seq_len, seq_len) attention_weights=torch.softmax(attention_scores,dim=-1) #注意力加权求和 attention=torch.matmul(attention_weights,value) # attention_scores:(batch_size, num_heads, seq_len, seq_len)* value(batch_size, num_heads, seq_len, head_dim) #attention:(batch_size, num_heads, seq_len, head_dim) #连接和线性变换 attention=attention.transpose(1,2).contiguous().view(batch_size,seq_len,input_dim)#contiguos深拷贝,不改变原数据 #(batch_size,num_heads, seq_len , head_dim) to (batch_size, seq_len, num_heads, head_dim) to (batch_size,seq_len,input_dim) output=self.output_linear(attention) return output #定义多头自注意力机制模型 class MultiHead_SelfAttention_Classifier(nn.Module): def __init__(self,input_dim,num_heads,hidden_dim,num_classes): super().__init__() self.attention=MultiHead_SelfAttention(input_dim,num_heads) self.fc1=nn.Linear(input_dim,hidden_dim) self.fc2=nn.Linear(hidden_dim,num_classes) self.relu=nn.ReLU() def forward(self,x): x=self.attention(x) x=x.mean(dim=1)#(batch_size, seq_len, input_dim) to (batch_size, input_dim) x=self.fc1(x) x=self.relu(x) x=self.fc2(x) return x if __name__=='__main__': model=MultiHead_SelfAttention_Classifier(input_dim=4,num_heads=2,hidden_dim=32,num_classes=2) x=torch.randn((2,4,4))#batch,4个token,每个token长度=input_dim output=model(x) print(output)
输出
tensor([[-0.0598, -0.0333],
[-0.2237, -0.0573]], grad_fn=<AddmmBackward0>)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。