共计 2703 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点
在传统的计算机视觉任务中,Self-Attention 机制在 Vision Transformer(ViT)中表现出色,但当我们需要处理多模态数据(如视觉 - 文本联合任务)时,它就显得力不从心了。

- 模态差异问题 :图像和文本数据具有完全不同的特征分布和结构,直接使用 Self-Attention 难以有效对齐不同模态的特征。
- 计算效率低下 :传统的多模态处理方法通常需要分别处理不同模态的数据,然后再进行融合,这种分步操作增加了计算开销。
- 信息交互不足 :Self-Attention 只能在同模态内进行特征交互,缺乏跨模态的深层特征融合能力。
技术对比:Cross Attention vs Self-Attention
Cross Attention(简称 cat)是 Self-Attention 的扩展,专门用于处理不同模态间的特征交互。
- 架构差异
- Self-Attention:Q、K、V 都来自同一个输入序列
-
Cross Attention:Q 来自一个模态,K、V 来自另一个模态
-
计算复杂度
- Self-Attention:O(n²)(n 是序列长度)
-
Cross Attention:O(nm)(n 是 Q 序列长度,m 是 K / V 序列长度)
-
内存占用
- Cross Attention 通常比 Self-Attention 更节省内存,特别是当两个模态的序列长度差异较大时
核心实现:PyTorch 代码详解
下面我们实现一个完整的 Cross Attention 层,包含多头注意力和必要的正则化操作。
import torch
import torch.nn as nn
import torch.nn.functional as F
class CrossAttention(nn.Module):
def __init__(self, embed_dim, num_heads):
super().__init__()
self.embed_dim = embed_dim
self.num_heads = num_heads
self.head_dim = embed_dim // num_heads
# 定义 Q、K、V 的投影矩阵
self.q_proj = nn.Linear(embed_dim, embed_dim)
self.k_proj = nn.Linear(embed_dim, embed_dim)
self.v_proj = nn.Linear(embed_dim, embed_dim)
# 输出投影
self.out_proj = nn.Linear(embed_dim, embed_dim)
def forward(self, x, context, mask=None):
"""
参数:
x: 查询序列 [batch_size, seq_len_q, embed_dim]
context: 上下文序列 [batch_size, seq_len_kv, embed_dim]
mask: 可选注意力掩码 [batch_size, seq_len_q, seq_len_kv]
"""
batch_size = x.size(0)
# 1. 投影计算
Q = self.q_proj(x) # [B, L_q, D]
K = self.k_proj(context) # [B, L_kv, D]
V = self.v_proj(context) # [B, L_kv, D]
# 2. 多头拆分
Q = Q.view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, L_q, D/H]
K = K.view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, L_kv, D/H]
V = V.view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, L_kv, D/H]
# 3. 缩放点积注意力
attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.head_dim ** 0.5) # [B, H, L_q, L_kv]
if mask is not None:
attn_scores = attn_scores.masked_fill(mask == 0, float('-inf'))
attn_weights = F.softmax(attn_scores, dim=-1)
attn_output = torch.matmul(attn_weights, V) # [B, H, L_q, D/H]
# 4. 多头拼接
attn_output = attn_output.transpose(1, 2).contiguous() # [B, L_q, H, D/H]
attn_output = attn_output.view(batch_size, -1, self.embed_dim) # [B, L_q, D]
# 5. 输出投影
output = self.out_proj(attn_output)
return output
性能考量
- 头数选择
- 通常情况下,4- 8 个头效果较好
- 头数过多可能导致模型过拟合
-
头数过少会降低模型容量
-
Flash Attention 加速
- Flash Attention 可以显著减少内存访问次数
- 对于长序列效果尤为明显
- 实现时需要检查 CUDA 版本和硬件兼容性
避坑指南
- 维度不匹配 :确保 Q、K、V 的嵌入维度能被头数整除
- 注意力掩码误用 :注意 softmax 前的 mask 值应为负无穷而非 0
- 初始化策略 :建议使用 Xavier 初始化投影矩阵
- 梯度检查点 :对于大模型,可以使用梯度检查点技术节省显存
延伸思考
- 多模态预训练应用
- 可以用于图像 - 文本对齐任务
-
在 CLIP 等模型中表现优异
-
与 CNN 混合设计
- 用 CNN 提取局部特征,用 Cross Attention 进行全局交互
- 这种混合架构在计算效率和性能间取得平衡
计算公式
- FLOPS 计算
- 投影计算:3 × batch_size × seq_len × embed_dim²
-
注意力计算:batch_size × num_heads × seq_len_q × seq_len_kv × head_dim
-
内存占用
- 主要来自注意力矩阵:batch_size × num_heads × seq_len_q × seq_len_kv
实验改进方向
- 尝试不同的头数(4,8,16)比较模型性能
- 实现 Flash Attention 版本并对比速度提升
- 在不同模态组合(如图像 - 音频)上测试 Cross Attention 的效果
通过本文的介绍,相信你已经对 Vision Transformer 中的 Cross Attention 机制有了深入理解。这种强大的特征交互机制正在越来越多的多模态任务中发挥关键作用,值得我们在实践中不断探索和创新。
正文完
发表至: 人工智能
近两天内
