Attention注意力机制综述(二)--多头自注意力机制(含代码)Multi-head Self-Attention Machanism

3. 多头自注意力机制(Multi-head Self-Attention Machanism)

多头注意力机制是在自注意力机制的基础上发展起来的,是自注意力机制的变体,旨在增强模型的表达能力和泛化能力。它通过使用多个独立的注意力头,分别计算注意力权重,并将它们的结果进行拼接或加权求和,从而获得更丰富的表示。

在自注意力机制中,每个单词或者字都仅仅只有一个 q、k、v与其对应,如下图所示;

在这里插入图片描述

为什么需要多个head?

在做self-attention的时候是用q去找相关的k,但是相关有很多不同形式和定义,不同q负责不同种类的相关性。

多头注意力机制则是在 ai 乘以一个q,k,v后,会再分配多个 q,k,v,这里以2个 q,k,v 为例,如下图所示:
在这里插入图片描述

3.1q、k操作

在多头注意力机制中, ai 会先乘 q矩阵,*qi=Wqai ;

其次,会为其多分配两个head,以 q为例,包括: qi,1,qi,2 ;

在这里插入图片描述

同样地,k和 v 也是一样的操作。

那么,下面就是 q和 k 的点乘操作了,在多头注意力机制中,有多个q 和 k,究竟应该选择哪个进行操作呢?

其实很简单,就是看下标,如下图所示。 qi,1,会和 ki,1和kj,1>进行点乘,再进行softmax操作

在这里插入图片描述

3.2v操作

将对应的attention分数乘对应的v, 比如head1的qi,1和 ki,1和kj,1进行点乘(dot_product)后,得到相应的注意力分数,归一化后分别和 vi,1和vj,1相乘,再求和得到注意力输出,head2也是同时这样进行的,得到bi,1和bi,2.

3.3注意力输出

将head1和head2得到的输出拼接起来,进行transpose就得到了注意力输出bi

在这里插入图片描述

3.4single-head与multi-head对比

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的维度

在这里插入图片描述

3.5流程总结

在这里插入图片描述

3.6代码实现
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>)
  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等待整个冬天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值