共计 3966 个字符,预计需要花费 10 分钟才能阅读完成。
背景痛点
传统 CNN 通过局部感受野和层次化结构取得了显著成效,但在处理长距离依赖关系时存在天然局限。比如在医学图像分割中,病灶区域可能分散在图像各处,CNN 需要通过多次下采样和上采样才能建立这种全局关联,导致信息损失。另一方面,原始 Transformer 将图像视为像素序列(如 224×224 图像会生成 50176 长度的序列),其自注意力层的 O(n²)复杂度直接导致显存爆炸。
主流架构对比
1. Vision Transformer (ViT)
- 核心创新:将图像划分为 16×16 的 patch(序列长度降至 196),直接套用 NLP Transformer
- 优势:全局注意力建模能力突出,在大型数据集(如 JFT-300M)表现优异
- 局限:固定尺度的 patch 缺乏多粒度特征,且计算量随图像尺寸平方增长
2. Swin Transformer

- 核心创新:
- 层级式金字塔结构(4/8/16/32 倍下采样)
- 滑动窗口注意力(Window-MSA)降低计算复杂度至线性
- 跨窗口连接(Shifted Window)解决窗口间信息隔离
- 计算效率:在 ImageNet-1K 上达到 85.4% 准确率时仅需 4.5G FLOPs
3. Pyramid Vision Transformer (PVT)
- 特色设计:
- 渐进式收缩金字塔保持空间分辨率层级
- 空间缩减注意力(SRA)降低 key/value 分辨率
- 相比 Swin 无需复杂窗口划分,更适合密集预测任务
关键代码实现
可学习 2D 位置编码
class Learnable2DPositionEmbedding(nn.Module):
def __init__(self, dim, grid_size):
super().__init__()
self.pos_emb = nn.Parameter(torch.randn(1, dim, grid_size, grid_size) * 0.02)
def forward(self, x):
# x: [B, C, H, W]
return x + F.interpolate(self.pos_emb, size=x.shape[2:], mode='bilinear')
数学原理:与传统 sin/cos 编码不同,可学习参数通过梯度下降自动适应图像空间关系,公式表示为:
$$\text{Output} = \text{Input} + \text{Interp}(E_{pos}), \quad E_{pos} \in \mathbb{R}^{1×C×S×S}$$
窗口多头注意力实现
def window_partition(x, window_size):
B, H, W, C = x.shape
x = x.view(B, H//window_size, window_size, W//window_size, window_size, C)
windows = x.permute(0,1,3,2,4,5).contiguous().view(-1, window_size, window_size, C)
return windows
class WindowAttention(nn.Module):
def __init__(self, dim, num_heads, window_size, qkv_bias=True):
super().__init__()
self.dim = dim
self.num_heads = num_heads
self.scale = (dim // num_heads) ** -0.5
# 相对位置偏置表 [(2M-1) × (2M-1) ]
self.relative_position_bias_table = nn.Parameter(torch.zeros((2*window_size-1)**2, num_heads))
# 生成相对位置索引
coords = torch.stack(torch.meshgrid([torch.arange(window_size), torch.arange(window_size)]))
coords_flatten = torch.flatten(coords, 1) # [2, M²]
relative_coords = coords_flatten[:,:,None] - coords_flatten[:,None,:]
relative_coords += window_size - 1 # 转换为正数
self.register_buffer("relative_position_index",
relative_coords[0] * (2*window_size-1) + relative_coords[1])
self.qkv = nn.Linear(dim, dim*3, bias=qkv_bias)
self.proj = nn.Linear(dim, dim)
def forward(self, x, mask=None):
B_, N, C = x.shape
qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C//self.num_heads)
q, k, v = qkv.permute(2,0,3,1,4).unbind(0)
attn = (q @ k.transpose(-2,-1)) * self.scale
# 添加相对位置偏置
relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(N, N, -1)
attn = attn + relative_position_bias.permute(2,0,1).unsqueeze(0)
if mask is not None:
attn = attn.view(-1, self.num_heads, N, N)
attn += mask.unsqueeze(1)
attn = attn.view(-1, self.num_heads, N, N)
attn = attn.softmax(dim=-1)
x = (attn @ v).transpose(1,2).reshape(B_, N, C)
return self.proj(x)
Patch Merging 下采样层
class PatchMerging(nn.Module):
"""2×2 降采样,同时通道数翻倍"""
def __init__(self, dim):
super().__init__()
self.norm = nn.LayerNorm(4*dim)
self.reduction = nn.Linear(4*dim, 2*dim, bias=False)
def forward(self, x):
B, H, W, C = x.shape
x = x.view(B, H//2, 2, W//2, 2, C)
x = x.permute(0,1,3,2,4,5).reshape(B, -1, 4*C)
x = self.norm(x)
return self.reduction(x)
性能优化策略
混合精度训练要点
- 在 PyTorch 中使用
torch.cuda.amp.autocast上下文管理器 - 对 LayerNorm 和 Softmax 保持 FP32 计算:
with torch.cuda.amp.autocast(dtype=torch.float16): x = layer_norm(x.float()) # 显式转换 attn = attn_weights.float().softmax(dim=-1) - 梯度缩放防止下溢出:搭配
GradScaler使用
大图像分块处理
- 策略一:重叠切片(Overlap-tile)
- 将 5120×5120 图像切分为 640×640 的带重叠(如 128 像素)区块
- 预测时拼接结果并融合重叠区域
- 策略二:使用 PVT 的空间缩减注意力
- 在 key/value 路径上应用 stride 卷积降低分辨率
- 计算量从 O(H²W²)降至 O(HW(H/p)(W/p)),p 为缩减比例
生产环境避坑指南
问题 1:位置编码微调灾难性遗忘
现象:下游任务微调时,预训练的位置编码参数剧烈变化导致性能下降
解决方案:
– 初始几层位置编码学习率设为 base_lr × 0.1
– 采用分层学习率策略:
param_groups = [{'params': model.pos_embed.parameters(), 'lr': lr*0.1},
{'params': model.blocks.parameters()}
]
optimizer = AdamW(param_groups, lr=lr)
问题 2:小样本过拟合
应对措施:
– 使用 DeiT 的蒸馏策略:在 ViT 中加入蒸馏 token
self.distill_token = nn.Parameter(torch.zeros(1, 1, dim))
x = torch.cat((cls_token, distill_token, patch_embeddings), dim=1)
– 强数据增强:MixUp + CutMix 组合
– 早停法(Early Stopping)监控验证集损失
问题 3:显存不足
优化方案:
– 激活检查点技术(Gradient Checkpointing):
from torch.utils.checkpoint import checkpoint_sequential
def forward(self, x):
return checkpoint_sequential(self.blocks, chunks=4, input=x)
– 采用 Swin 的窗口注意力替代全局注意力
– 使用梯度积累(Gradient Accumulation)模拟更大 batch
延伸思考
- 视频 Transformer 如何平衡时空注意力计算效率?是否需要分离时间 / 空间注意力层?
- 在边缘设备部署时,如何设计硬件友好的稀疏注意力模式?
- 视觉 Transformer 能否与神经辐射场(NeRF)结合,实现 3D 场景理解?
正文完
发表至: 人工智能
近两天内
