Transformer 作为一种强大的序列到序列模型,凭借自注意力机制在诸多领域大放异彩。它能并行处理序列,有效捕捉上下文关系,其架构包含编码器与解码器,各由多层组件构成,涉及自注意力、前馈神经网络、归一化和 Dropout 等关键环节 。下面我们深入探讨其核心要点,并结合代码实现进行详细解读。
一、Transformer 核心公式与机制
(一)自注意力计算
自注意力机制是 Transformer 的核心,其计算基于公式\(Attention(Q, K, V)=softmax\left(\frac{Q K^{T}}{\sqrt{d_{k}}}\right) V\) 。其中,Q、K、V分别是查询、键和值矩阵,由输入X分别乘以对应的权重矩阵\(W_Q\)、\(W_K\)、\(W_V\)得到 。\(d_{k}\)表示键的维度,除以\(\sqrt{d_{k}}\) ,一方面可防止\(QK^{T}\)过大导致 softmax 计算溢出,另一方面能让\(QK^{T}\)结果满足均值为 0、方差为 1 的分布 。\(QK^{T}\)本质上是计算向量间的余弦相似度,反映向量方向上的相似程度。
(二)多头注意力机制
多头注意力机制将输入x拆分为h份,独立计算h组不同的线性投影得到各自的Q、K、V ,然后并行计算注意力,最后拼接h个注意力池化结果,并通过可学习的线性投影产生最终输出。这种设计使每个头能关注输入的不同部分,增强了模型对复杂函数的表示能力。
(三)位置编码
由于 Transformer 没有循环结构,位置编码用于保留序列中的位置信息,确保模型在处理序列时能感知元素的位置。
二、自注意力与多头注意力的实现
(一)自注意力实现
在 PyTorch 中,自注意力模块Self_Attention
的实现如下:
python
import numpy as np
import torch
from torch import nn
class Self_Attention(nn.Module):
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 / np.sqrt(dim_k)
def forward(self, x):
Q = self.q(x)
K = self.k(x)
V = self.v(x)
atten = nn.Softmax(dim=-1)(torch.bmm(Q, K.permute(0, 2, 1))) * self._norm_fact
output = torch.bmm(atten, V)
return output
X = torch.randn(4, 3, 2)
self_atten = Self_Attention(2, 4, 5)
res = self_atten(X)
print(res.shape)
在这段代码中,Self_Attention
类继承自nn.Module
。__init__
方法初始化了线性层q
、k
、v
,并计算了归一化因子_norm_fact
。forward
方法实现了自注意力的计算过程,先通过线性层得到Q、K、V ,然后计算注意力权重atten
,最后得到输出output
。
(二)多头注意力实现
多头注意力模块Self_Attention_Muti_Head
的实现如下:
python
import torch
import torch.nn as nn
class Self_Attention_Muti_Head(nn.Module):
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 / np.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)
atten = nn.Softmax(dim=-1)(torch.matmul(Q,K.permute(0,1,3,2)))
output = torch.matmul(atten,V).reshape(x.shape[0],x.shape[1],-1)
return output
x=torch.rand(1,3,4)
atten=Self_Attention_Muti_Head(4,4,4,2)
y=atten(x)
print(y.shape)
在这个类中,__init__
方法进行了参数校验和模块初始化 。forward
方法将输入x
经过线性层变换后,重塑形状以适应多头计算,接着计算注意力权重并得到输出,最后将多头的结果拼接起来。
三、注意力机制的拓展
(一)视觉注意力机制
视觉注意力机制主要包括空间域、通道域和混合域三种。空间域注意力通过对图片空间域信息进行变换,生成掩码并打分来提取关键信息;通道域注意力为每个通道分配权重,代表模块有 SENet,通过全局池化提取通道权重,进而调整特征图;混合域注意力则结合了空间域和通道域的信息 。
(二)通道域注意力(SENet)实现
SENet 的实现代码如下:
python
class SELayer(nn.Module):
def __init__(self, channel, reduction=16):
super(SELayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
在SELayer
类中,__init__
方法初始化了平均池化层avg_pool
和全连接层序列fc
。forward
方法实现了 SENet 的核心操作,先通过平均池化进行 Squeeze 操作,再经过全连接层进行 Excitation 操作,最后将生成的权重与原特征图相乘,实现对特征图的增强 。
(三)门控注意力机制(GCT)
GCT 是一种能提升卷积网络泛化能力的通道间建模结构。它包含全局上下文嵌入、通道规范化和门控适应三个部分。全局上下文嵌入模块汇聚每个通道的全局上下文信息;通道规范化构建神经元竞争关系;门控适应加入门限机制,促进神经元的协同或竞争关系 。
其实现代码如下:
python
class GCT(nn.Module):
def __init__(self, num_channels, epsilon=1e-5, mode='l2', after_relu=False):
super(GCT, self).__init__()
self.alpha = nn.Parameter(torch.ones(1, num_channels, 1, 1))
self.gamma = nn.Parameter(torch.zeros(1, num_channels, 1, 1))
self.beta = nn.Parameter(torch.zeros(1, num_channels, 1, 1))
self.epsilon = epsilon
self.mode = mode
self.after_relu = after_relu
def forward(self, x):
if self.mode == 'l2':
embedding = (x.pow(2).sum((2, 3), keepdim=True) + self.epsilon).pow(0.5) * self.alpha
norm = self.gamma / ((embedding.pow(2).mean(dim=1, keepdim=True) + self.epsilon).pow(0.5))
elif self.mode == 'l1':
if not self.after_relu:
_x = torch.abs(x)
else:
_x = x
embedding = _x.sum((2, 3), keepdim=True) * self.alpha
norm = self.gamma / (torch.abs(embedding).mean(dim=1, keepdim=True) + self.epsilon)
gate = 1. + torch.tanh(embedding * norm + self.beta)
return x * gate
在GCT
类中,__init__
方法初始化了可训练参数alpha
、gamma
、beta
以及其他超参数 。forward
方法根据不同的模式(l2
或l1
)计算嵌入和归一化结果,最后通过门控机制得到输出 。GCT 通常添加在 Conv 层前,训练时可先冻结原模型训练 GCT,再解冻微调 。