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 nnclass 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_factoutput = torch.bmm(atten, V)return outputX = 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 nnclass 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 == 0assert dim_v % nums_head == 0self.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_headself.dim_k = dim_kself.dim_v = dim_vself._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 outputx=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 = epsilonself.mode = modeself.after_relu = after_reludef forward(self, x):if self.mode == 'l2':embedding = (x.pow(2).sum((2, 3), keepdim=True) + self.epsilon).pow(0.5) * self.alphanorm = 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 = xembedding = _x.sum((2, 3), keepdim=True) * self.alphanorm = 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,再解凍微調 。