为什么6年后还要谈Transformer?
2017年,《Attention is All You Need》横空出世,Transformer架构彻底颠覆了NLP领域。6年后的今天,它不仅没有被取代,反而成为了AI时代最重要的基础设施之一——所有你能叫出名字的大模型:GPT系列、Claude、Gemini、Llama、Qwen……内核都是Transformer的变体。
但是,2026年的Transformer和2017年的原始版本已经不是同一个东西了。在注意力机制、位置编码、归一化方式、激活函数等每一个维度上,都经历了多轮迭代和进化。
理解这些演变,不只是学术兴趣——它直接决定了你怎么选模型、怎么做微调、怎么优化推理性能。
第一部分:注意力机制的演化
原始多头注意力(Multi-Head Attention)
原始Transformer的注意力公式是:
Attention(Q, K, V) = softmax(QK^T / √d_k) × V
其中Q(查询)、K(键)、V(值)来自同一序列(自注意力)或不同序列(交叉注意力)。多头注意力将这个操作并行执行h次,让模型在不同的"子空间"里关注不同类型的关系。
核心问题:计算复杂度是O(n²),n是序列长度。对于长文本(如128K上下文),这变成了天文数字。
分组查询注意力(Grouped Query Attention,GQA)
2023年后的主流改进。原始注意力中,每个注意力头都有独立的K、V矩阵;GQA将多个头共享同一组K、V:
# 标准MHA:8个头,每头有独立K/V
# GQA示例:8个Q头,共用2组K/V
class GroupedQueryAttention(nn.Module):
def __init__(self, d_model, n_heads, n_kv_heads):
super().__init__()
self.n_heads = n_heads
self.n_kv_heads = n_kv_heads
self.head_dim = d_model // n_heads
self.q_proj = nn.Linear(d_model, n_heads * self.head_dim)
self.k_proj = nn.Linear(d_model, n_kv_heads * self.head_dim)
self.v_proj = nn.Linear(d_model, n_kv_heads * self.head_dim)
self.out_proj = nn.Linear(n_heads * self.head_dim, d_model)
def forward(self, x):
B, T, C = x.shape
q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim)
k = self.k_proj(x).view(B, T, self.n_kv_heads, self.head_dim)
v = self.v_proj(x).view(B, T, self.n_kv_heads, self.head_dim)
# 将K/V扩展到与Q相同的头数
k = k.repeat_interleave(self.n_heads // self.n_kv_heads, dim=2)
v = v.repeat_interleave(self.n_heads // self.n_kv_heads, dim=2)
# 标准注意力计算
...
效果:KV Cache大小减少4-8倍,推理内存占用显著下降,速度提升明显。Llama 3、Qwen3等主流模型都采用GQA。
Flash Attention:硬件感知的注意力实现
Flash Attention不改变数学结果,但从根本上改变了计算方式:通过分块计算(tiling)避免将完整的注意力矩阵写入HBM(显存),大幅减少内存读写次数。
传统注意力内存访问:O(n²)
Flash Attention内存访问:O(n)(相对于序列长度)
在实践中,Flash Attention v2将注意力层的速度提升了2-4倍,并将可处理的上下文长度从几千token扩展到数十万token。这是现代LLM能够支持128K+上下文的关键技术支撑之一。
第二部分:位置编码的革命
从绝对位置到旋转位置编码(RoPE)
原始Transformer用的是固定的正弦/余弦位置编码——每个位置有一个唯一的向量标识。这种方式有个致命缺陷:无法外推到训练时未见过的更长序列。
2021年提出的RoPE(Rotary Position Embedding)用旋转矩阵编码相对位置信息,彻底解决了这个问题:
def apply_rotary_emb(xq, xk, freqs_cos, freqs_sin):
"""应用旋转位置编码"""
# 将向量拆分为实部和虚部
xq_r, xq_i = xq.float().reshape(*xq.shape[:-1], -1, 2).unbind(-1)
xk_r, xk_i = xk.float().reshape(*xk.shape[:-1], -1, 2).unbind(-1)
# 旋转操作(复数乘法)
xq_out_r = xq_r * freqs_cos - xq_i * freqs_sin
xq_out_i = xq_r * freqs_sin + xq_i * freqs_cos
xk_out_r = xk_r * freqs_cos - xk_i * freqs_sin
xk_out_i = xk_r * freqs_sin + xk_i * freqs_cos
return torch.stack([xq_out_r, xq_out_i], dim=-1).flatten(3), \
torch.stack([xk_out_r, xk_out_i], dim=-1).flatten(3)
RoPE的核心优势:注意力分数只依赖于位置差,而不是绝对位置,天然支持相对位置泛化。所有2024年后的主流模型(Llama 3、Qwen3、Gemma等)都采用RoPE或其变体。
YaRN:不重新训练就能扩展上下文
YaRN(Yet Another RoPE extensioN)通过对RoPE频率进行缩放,让模型在不重新训练的情况下处理比训练时更长的序列:
# YaRN的核心:按频率分组缩放
def yarn_get_mscale(scale: float) -> float:
if scale <= 1:
return 1.0
return 0.1 * math.log(scale) + 1.0
# 通过修改rope_scaling参数启用
model_config = {
"rope_scaling": {
"type": "yarn",
"factor": 4.0, # 上下文扩展倍数
"original_max_position_embeddings": 8192
}
}
第三部分:归一化与激活函数的现代选择
Pre-Norm vs Post-Norm
原始Transformer用的是Post-Norm(残差连接后做归一化)。实践发现,深层网络用Post-Norm训练不稳定,现代模型普遍改用Pre-Norm:
# Post-Norm(原始,训练不稳定)
x = LayerNorm(x + Attention(x))
# Pre-Norm(现代主流,训练更稳定)
x = x + Attention(LayerNorm(x))
RMSNorm:更快的归一化
LayerNorm需要计算均值和方差,RMSNorm只需计算均方根(RMS),计算量减少约40%,效果相当:
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
# 只计算RMS,不减去均值
rms = x.pow(2).mean(-1, keepdim=True).add(self.eps).rsqrt()
return x * rms * self.weight
Llama、Qwen、Mistral等模型都用RMSNorm替代了LayerNorm。
SwiGLU:FFN层的现代激活函数
原始Transformer的FFN层用ReLU。SwiGLU是2020年后的主流替代:
class SwiGLU(nn.Module):
def __init__(self, dim, hidden_dim):
super().__init__()
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
self.w3 = nn.Linear(dim, hidden_dim, bias=False)
def forward(self, x):
# 门控机制:SiLU(xW1) * xW3
return self.w2(F.silu(self.w1(x)) * self.w3(x))
SwiGLU的门控机制让FFN具备了选择性激活的能力,在相同参数量下,比ReLU FFN表现更好。
第四部分:现代LLM的完整架构图
综合以上改进,一个2026年主流的Transformer Decoder Block长这样:
class ModernTransformerBlock(nn.Module):
def __init__(self, config):
super().__init__()
self.attention_norm = RMSNorm(config.dim)
self.attention = GroupedQueryAttention(
d_model=config.dim,
n_heads=config.n_heads,
n_kv_heads=config.n_kv_heads # GQA
)
self.ffn_norm = RMSNorm(config.dim)
self.feed_forward = SwiGLU(config.dim, config.ffn_dim)
def forward(self, x, freqs_cos, freqs_sin, mask=None):
# Pre-Norm + 残差
h = x + self.attention(
self.attention_norm(x),
freqs_cos, freqs_sin, mask
)
out = h + self.feed_forward(self.ffn_norm(h))
return out
| 组件 | 原始Transformer | 2026年主流实现 |
|---|---|---|
| 位置编码 | 正弦/余弦(绝对) | RoPE(相对旋转) |
| 注意力 | MHA | GQA + Flash Attention |
| 归一化位置 | Post-Norm | Pre-Norm |
| 归一化方法 | LayerNorm | RMSNorm |
| FFN激活函数 | ReLU | SwiGLU / GeGLU |
| 偏置项 | 有 | 通常省略(无bias) |
第五部分:对工程师的实际意义
选模型时的架构参数解读
当你看到一个模型的技术报告,这些架构参数直接影响你的工程选型:
# Llama 3.1 70B部分架构参数
hidden_size: 8192 # 模型维度
num_attention_heads: 64 # Q头数量
num_key_value_heads: 8 # KV头数量(GQA:64/8=8:1压缩比)
intermediate_size: 28672 # FFN维度
max_position_embeddings: 131072 # 最大上下文(128K)
rope_theta: 500000 # RoPE基础频率(影响长文本外推能力)
num_key_value_heads越小,KV Cache越省内存;rope_theta越大,长上下文处理越稳定。
推理优化的选择
了解架构后,你能更准确地选择量化和优化策略:
- GQA模型的KV Cache已经很小,可以重点量化权重(W4A16)
- 非GQA模型(如早期GPT-2)更需要KV Cache量化(KV Int8)
- Flash Attention已内置于vLLM/TGI,直接收益,无需额外配置
总结
理解Transformer架构的演化,不是为了复现论文,而是为了:
- 看懂模型报告:知道每个参数意味着什么
- 做出正确的工程决策:从硬件配置到量化策略
- 预判技术方向:了解架构限制在哪,下一步改进会在哪
从原始注意力到GQA、Flash Attention,从绝对位置编码到RoPE,从Post-Norm到Pre-Norm+RMSNorm——每一步改进都有其工程动机。掌握这些,你对大模型的理解会从"会调用"跃升为"真理解"。