Transformer

Transformer

自注意力机制

自注意力机制核心就是计算句子在编码过程中每个位置上的注意力权重,然后再以权重和的方式计算整个句子的隐含向量表示

attention核心?

self-attention 核心公式:

Attention(Q,K,V)=softmax(QKTdk)V

  • 其中,QKV分别表示查询(Query)、键(Key)和值(Value)的矩阵,dk是每个注意力头的维度。
  • 首先将query与key的转置做点积,然后将结果除以sqrt(dk),再进行softmax计算,最后将结果与value做矩阵乘法得到output,dk表示词向量维度,除以sqrt(dk)是为了防止QKT过大导致softmax计算溢出,其次可以将QKT结果满足均值为0,方差为1的分布。
  • QKT计算相当于计算Q和V中每个向量的点积,本质上是余弦相似度,可以表示两个向量在方向上的相似度,结果越大越相似。

总而言之,在Self-Attention中,通过计算查询和键之间的相似性得到注意力分布,然后将该分布与值相乘以获取加权的值表示。最后,对所有头的结果进行拼接或平均操作,得到最终的输出。

Scaled Dot Product Attention

Q、K、V是什么?

q,k,v分别代表query,key,value对应为查询,键、值

QKV

Q、K、V都是对X进行矩阵W后的线性变换

实现

import numpy as np 
from math import sqrt
import torch 
from torch import nn

class Self_Attention(nn.Module):
    # input: batch_size * seq_len * input_dim
    # Q: batch_size * seq_len * dim_k
    # K: batch_size * seq_len * dim_k
    # V: batch_size * seq_len * dim_v
    def __init__(self,input_dim,dim_k,dim_v):
        super(Self_Attention,self).__init__()
        self.q = nn.Linear(input_dim, dim_k)
        self.k = nn.Linear(input_dim, dim_k)
        self.v = nn.Linear(input_dim, dim_v)
        self._norm_fact = 1 / sqrt(dim_k)
    
    def forward(self,x):
        Q = self.q(x)  # Q: batch_size * seq_len * dim_k
        K = self.k(x)  # K: batch_size * seq_len * dim_k
        V = self.v(x)  # V: batch_size * seq_len * dim_v
        
        # Q * K.T()  batch_size * seq_len * seq_len 
        atten = nn.Softmax(dim=-1)(torch.bmm(Q,K.permute(0,2,1))) * self._norm_fact
        # Q * k.T() * V batch_size * seq_len * dim_v
        output = torch.bmm(atten,V)
        
        return output
    
X = torch.randn(4,3,2)
print(X)
self_atten = Self_Attention(2, 4, 5)
res = self_atten(X)
print(res.shape)


多头注意力机制

不同于只使用一个注意力池化,可以将输入x拆分为h份,然后独立计算h组不同的线性投影来得到各自的QKV,然后将变换后的h组QKV并行计算注意力,最后将h个注意力池化拼接起来并通过另一个可以学习的线性投影进行变换以产生输出,这种设计叫做多头注意力。

多头注意力

在多头注意力机制中,每个头可能会关注输入的不同部分,可以表示比简单加权平均值更加复杂的函数。

加权平均

实现

# Muti-head Attention 机制的实现
from math import sqrt
import torch
import torch.nn as nn


class Self_Attention_Muti_Head(nn.Module):
    # input: batch_size * seq_len *input_dim 
    # q: bath_size * seq_len * dim_k
    # k: bath_size * seq_len * dim_k
    # v: bath_size * seq_len * dim_v
    def __init__(self,input_dim,dim_k,dim_v,nums_head):
        super(Self_Attention_Muti_Head, self).__init__()
        assert(dim_k % nums_head ==0)
        assert(dim_v % nums_head ==0)
        self.q = nn.Linear(input_dim, dim_k)
        self.k = nn.Linear(input_dim, dim_k)
        self.v = nn.Linear(input_dim, dim_v)

        self.nums_head = nums_head
        self.dim_k = dim_k
        self.dim_v = dim_v
        self._norm_fact = 1 / sqrt(dim_k)

        
    def forward(self,x):
   
        Q = self.q(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.nums_head)
        K = self.k(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.nums_head)
        V = self.v(x).reshape(-1,x.shape[0],x.shape[1],self.dim_v // self.nums_head)
        
        print(x.shape)
        print(Q.size())

        atten = nn.Softmax(dim=-1)(torch.matmul(Q,K.permute(0,1,3,2))) # Q * K.T() ---> batch_size * seq_len * seq_len
        output = torch.matmul(atten,V).reshape(x.shape[0],x.shape[1],-1) # Q * K.T() * V ---> batch_size * seq_len * dim_v
        
        return output

x = torch.rand(1,3,4)
print(x)

atten = Self_Attention_Muti_Head(4, 4, 4, 2)
y = atten(x)
print(y.shape)

作者:野哥李
微信公众号:AI算法学习社
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
本文章不做任何商业用途,仅作为自学所用,文章后面会有参考链接,我可能会复制原作者的话,如果介意,我会修改或者删除。

posted @   野哥李  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示

目录导航