【学习笔记】一文读懂 Transformer:从 Attention到LLM 的核心架构(2/35)
一、前言
在上一篇《大模型时代全景图》里,我们梳理了从 2017 年 Transformer 论文到 2026 年大模型生态的完整演进。如果你把那篇文章读完,会发现一个事实:过去八年所有的大模型——GPT、Claude、Llama、Qwen、DeepSeek——本质上都是 Transformer 的变体。
这一篇我们就来彻底搞懂 Transformer。
但这不是一篇"复现论文"的文章。论文级的推导网上已经太多,多数读者读完仍然不知道这玩意儿在生产环境里到底怎么用。本文从工程师视角出发,目标是让你读完之后能回答这些问题:
-
为什么所有大模型推理框架都要专门优化 attention?
-
KV Cache 到底缓存了什么?为什么能加速?
-
上下文从 4K 扩展到 1M 的工程难点在哪里?
-
为什么 Flash Attention、PagedAttention 都围着 attention 转?
-
为什么 Decoder-only 架构吃掉了一切?
如果你能回答以上每一个问题,那么后续推理优化、显存计算、长上下文等等一系列工程话题,你都会有一个稳固的"心智模型"作为参照——这也是为什么把 Transformer 放在系列第 2 篇。
我们开始。
二、为什么 Transformer 是大模型的「地基」
2.1 前 Transformer 时代:RNN 的三个死结
在 2017 年之前,处理"序列"的主流方案是 RNN/LSTM/GRU。这类网络有一个核心结构:当前时刻的输出,依赖上一时刻的隐状态。
h_t = f(h_{t-1}, x_t)
这个看似优雅的递推公式,给工程师埋下了三个炸弹:
炸弹 1:无法并行
序列必须按时间步顺序计算——要算 h_10,必须先算完 h_1, h_2, ..., h_9。这意味着:
-
训练时,单条样本内部完全串行,GPU 闲置严重
-
序列越长,训练越慢,且无法靠堆 GPU 解决
炸弹 2:长程依赖丢失
虽然 LSTM 引入门控机制缓解了梯度消失,但实际效果在序列长度超过 200 后就明显退化。让 RNN 理解一篇 8K Token 的文章?基本不可能。
炸弹 3:状态压缩瓶颈
整个序列的信息被压缩到一个固定维度的隐状态向量里——就像让你用 256 个数字记住一整本《三国演义》。
2.2 2017 年的转折点
2017 年 6 月,Google Brain 团队发表了《Attention is All You Need》。这篇论文做了一件极其大胆的事:把 RNN 整个扔掉。
新架构的核心思想可以用一句话概括:
让序列中任意两个位置直接"对话",不需要经过中间状态。
这个改变看似激进,但带来了三个工程红利:
(1)完全并行:序列内所有位置同时计算,训练效率呈数量级提升
(2)长程依赖直连:第 1 个 token 可以直接"看到"第 8000 个 token
(3)架构高度统一:CV、NLP、语音、多模态最终都收敛到 Transformer
也正因为如此,Transformer 才有可能"做大"——175B 的 GPT-3、671B 的 DeepSeek、千亿级 MoE,本质上都建立在 attention 可大规模并行计算这个基础之上。
2.3 工程师为什么必须懂 Transformer
你可能会问:我又不训模型,懂这个干嘛?
答案是:Transformer 不只是训练框架的事,更是推理框架、显存管理、性能优化的根本依据。
下面这张表列出了"工程问题"和"Transformer 概念"的对应关系:
|
工程问题 |
背后的 Transformer 概念 |
|
为什么单卡跑不动 70B 模型? |
参数量 + KV Cache 显存占用 |
|
为什么上下文一长就 OOM? |
Attention 是 O(n²) 复杂度 |
|
为什么 vLLM 比朴素推理快几十倍? |
PagedAttention 优化 KV Cache 管理 |
|
为什么 Flash Attention 能省显存? |
Attention 计算的 IO 瓶颈 |
|
为什么 RoPE 比绝对位置编码好? |
位置编码的外推能力 |
|
为什么 MoE 能省推理算力? |
FFN 层占了模型大半参数 |
|
投机解码为什么有效? |
Attention 的自回归特性 |
读完本文,你会理解上面每一行的逻辑。
三、核心原理:从 Self-Attention 到完整 Transformer
我们从最基本的 Self-Attention 开始,逐层往外推。
3.1 Self-Attention:让 Token 互相"对话"
问题设定:给定一个序列 [token_1, token_2, ..., token_n],怎么让每个 token 都能"考虑到"其他所有 token?
核心思路:对每个位置 i,计算它和所有其他位置 j 的"相关度"权重,然后用这些权重对所有位置的信息做加权求和。
具体来说,每个 token 会被投影成三个向量:
-
Query (Q):当前位置在"问什么"
-
Key (K):其他位置能"回答什么"
-
Value (V):其他位置实际"携带的信息"




三矩阵分离是 Attention 的核心设计!
公式如下:

其中,
| 符号 | 含义 | 常见维度(NLP 场景) |
|---|---|---|
|
Q: Query 查询矩阵 |
代表当前要编码的 token,用来 “提问”,找和自己相关的内容 | ![]() |
|
K: Key 键矩阵 |
待匹配的候选 token,和 Q 做相似度匹配 | ![]() |
|
V: Value 值矩阵 |
最终要提取的信息载体,匹配成功后加权取值 | ![]() |
![]() |
矩阵乘法,计算Q 中每个向量和所有 K 向量的点积相似度 | ![]() |
![]() |
缩放因子,Key 向量维度的平方根 | 归一化点积,解决高维下点积方差爆炸问题。防止点积过大导致 softmax 进入梯度消失区。 |
| softmax | 按行归一化,把相似度转为权重分布(总和 = 1) | ![]() |
| 末尾乘V | 用注意力权重对 Value 加权求和,输出融合后的特征 | ![]() |

几何直观:把每个 token 想象成高维空间中的一个向量。Self-Attention 让每个 token 都能"看一眼"所有其他 token,然后把自己变成"和我相关的那些 token 的加权混合体"。
工程视角:整个过程是纯矩阵乘法 + softmax。这意味着:
-
可以完全并行:所有位置同时计算
-
可以高度优化:矩阵乘法是 GPU 的看家本领(CUDA Tensor Core)
-
可以精确缓存:K 和 V 一旦算出可以反复用(这就是 KV Cache 的根基)
3.2 Multi-Head Attention:多个"视角"并行看
单个 attention 头有一个局限:它只能学到一种"相关性模式"。但语言中的关系是多元的——语法关系、语义关系、指代关系、共现关系……一种 attention 学不完。
解决方案:把 Q、K、V 拆成多组(比如 32 头),每组独立做 attention,最后拼起来。
# 伪代码
head_outputs = []
for i in range(num_heads):
Q_i, K_i, V_i = split_per_head(Q, K, V, i)
head_outputs.append(attention(Q_i, K_i, V_i))
output = concat(head_outputs) @ W_o
工程角度的几个关键数字(以 Llama-3-70B 为例):
-
hidden_dim= 8192 -
num_heads= 64 -
head_dim= 128(注意:8192 / 64 = 128)
----------------------------------------
这段描述背后的五个技术要点:
- 结构性约束:hidden_dim = num_heads × head_dim 是多头注意力的硬约束,8192/64=128 说明特征维度被均分。
- 子空间独立计算:每个头在 128 维子空间内独立完成 QK^T → softmax → AV,缩放因子为 √128。
- 硬件对齐:head_dim = 128 是 Tensor Core tile size 的整数倍,是当前 GPU kernel 优化的最优维度。
- GQA 隐藏细节:Llama-3-70B 实际只有 8 个 KV 头(非 64 个),每 8 个 Q 头共享 1 组 KV,描述中省略了这一关键配置。
- 推理效率:GQA 使 KV cache 减少 8 倍,是支撑长上下文推理的核心工程决策,而非单纯的模型质量选择。
-----------------------------------------
这种"切分维度"的设计让多头几乎不增加计算量——总的矩阵规模没变,只是 reshape 了一下。
单头注意力的计算量为 O(L² × head_dim),其中 L 是序列长度。64 个头的总计算量为 O(L² × hidden_dim) = O(L² × 8192),与头数无关——这是 MHA/GQA/MQA 三者的共同计算量。
GQA 节省的是 KV 投影的权重量和 KV cache 的显存量,而非注意力计算本身的 FLOPs。
💡 2024 年后的演进:GQA / MQA
Llama 2 之后大量模型用 Grouped Query Attention:让多个 query head 共享同一组 K/V head。
例如 Llama-3-70B 实际有 64 个 Q head 但只有 8 个 KV head。
这个改动直接减少了 KV Cache 显存占用 8 倍——是长上下文部署的关键优化。
3.3 Positional Encoding:注入"顺序"信息
Self-Attention 有一个隐藏问题:它对位置完全不敏感。
把"我打你"和"你打我"输入进去,由于是对所有位置做加权求和,结果几乎一样——这显然不对。
解决方案:在输入向量上加一个表示位置的编码。

该方法已被淘汰。




演进路线:
|
方法 |
提出时间 |
思路 |
当下使用 |
|
Sinusoidal |
2017 |
用 sin/cos 表示绝对位置 |
已淘汰 |
|
Learned PE |
2018 |
给每个位置学习一个向量 |
BERT、早期 GPT |
|
ALiBi |
2021 |
在 attention 分数上加位置偏置 |
部分模型 |
| RoPE |
2021 |
用旋转矩阵编码相对位置 |
当下主流
(Llama / Qwen / DeepSeek) |
RoPE(Rotary Position Embedding)为什么赢了:
-
相对位置:天然反映 token 之间的距离关系
-
可外推:训练 4K,推理时通过 YaRN 等技术可以扩展到 32K、128K、1M
-
计算高效:只在 Q 和 K 上做旋转,不增加参数
👉 关于长上下文外推,详见 系列第 15 篇:YaRN、Ring Attention 详解
3.4 完整的 Transformer Block
把 attention包装成可堆叠的block,再加几个组件,就得到了完整的 Transformer Block:
输入 x
├─→ LayerNorm
│ ↓
│ Self-Attention
│ ↓
└─── ⊕ (残差连接)
↓
├─→ LayerNorm
│ ↓
│ FFN (前馈网络)
│ ↓
└─── ⊕ (残差连接)
↓
输出
几个工程上的关键设计:
① 残差连接(Residual Connection)
输入直接"短路"加到输出上:x + f(x)。这让深层网络可以训练——没有残差,超过几十层就训不动了。Llama-3-70B 有 80 层 Transformer Block,全靠残差扛着。
② LayerNorm vs RMSNorm
LayerNorm 把每层激活做归一化。2023 年后主流大模型基本都换成了 RMSNorm(Llama 系列首推),相比 LayerNorm 减少了一次均值计算,推理速度更快、精度几乎无损。
③ FFN(Feed-Forward Network)
每个 Transformer Block 里都有一个 FFN,结构是:
FFN(x) = W_2 · activation(W_1 · x)
W_1 把维度放大 4 倍,激活函数过滤一下,W_2 再缩回去。这是模型大部分参数的"住所"——以 Llama-3-70B 为例,FFN 占了总参数的约 70%。
💡 这就是 MoE 优化的入口
既然 FFN 参数最多但每次推理只用一部分,那不如把它做成"专家库"——每个 token 只激活几个专家。
这就是 DeepSeek V3 (671B 总参数,37B 激活) 背后的核心机制。
详见 系列第 31 篇:MoE 架构深度解析。
④ Pre-LN vs Post-LN
原始论文用 Post-LN(先做完 attention 再 LayerNorm),但训练大模型时不稳定。2020 年后所有大模型都改成了 Pre-LN(先 LayerNorm 再 attention)。这是一个不起眼但影响巨大的工程改进。
3.5 Decoder-only:为什么这个架构吃掉了一切
Transformer 原论文是为机器翻译设计的,包含 Encoder 和 Decoder 两部分。但实际演化中,三种架构同台竞技:
|
架构 |
代表模型 |
特点 |
现状 |
|
Encoder-only |
BERT |
双向注意力,擅长理解任务 |
仍在用,但非主流 |
|
Encoder-Decoder |
T5 / BART |
编码 + 解码,适合翻译 |
渐渐淡出 |
| Decoder-only |
GPT / Llama / Claude |
自回归生成 |
绝对主流 |
为什么 Decoder-only 赢了?
从工程视角看,原因有三:
-
统一性:所有 NLP 任务都可以转化为"给定前缀,预测下一个 token"——分类、抽取、对话、翻译……一个范式打遍天下。
-
Scaling 友好:训练目标极其简单(下一个 token 预测),数据极易获取(任何文本都能用),适合 Scaling Law。
-
In-Context Learning 涌现:达到足够规模后,模型仅凭 prompt 中的例子就能学会新任务,无需微调。
Decoder-only 的核心机制:Causal Mask
为了保证"自回归生成"(生成第 t 个 token 时只能看到前 t-1 个),attention 需要加一个三角掩码:
Mask matrix (n=4):
[0 -inf -inf -inf]
[0 0 -inf -inf]
[0 0 0 -inf]
[0 0 0 0 ]
加在 QK^T 后、softmax 前。-inf 经过 softmax 变成 0,等于"看不到未来"。
这个看似简单的设计,直接催生了 KV Cache 这一推理优化的基石——我们下一节会看到。
四、实战拆解:用 100 行 PyTorch 写一个 Mini-GPT
光看公式记不住,写一遍代码就懂了。以下是一个可运行的极简 Transformer Decoder:
import torch
import torch.nn as nn
import torch.nn.functional as F
from dataclasses import dataclass
@dataclass
class Config:
"""模型超参数配置类,使用 dataclass 简化定义。"""
vocab_size: int = 32000 # 词表大小,即可用的 token 数量
hidden_dim: int = 256 # 隐藏维度(模型宽度),所有子层的特征维度
num_heads: int = 8 # 多头注意力的头数
num_layers: int = 4 # Transformer Block 的层数
max_seq_len: int = 512 # 支持的最大序列长度
class CausalSelfAttention(nn.Module):
"""因果多头自注意力层,带下三角掩码,用于自回归语言建模。"""
def __init__(self, cfg):
super().__init__()
assert cfg.hidden_dim % cfg.num_heads == 0 # 确保 hidden_dim 能被头数整除
self.num_heads = cfg.num_heads # 记录注意力头数
self.head_dim = cfg.hidden_dim // cfg.num_heads # 每个头的维度 = hidden_dim / num_heads
# 一次性投影出 Q、K、V(拼接后维度为 3 * hidden_dim)
self.qkv = nn.Linear(cfg.hidden_dim, 3 * cfg.hidden_dim, bias=False)
# 输出投影,将多头拼接后的结果映射回 hidden_dim
self.proj = nn.Linear(cfg.hidden_dim, cfg.hidden_dim, bias=False)
# 因果掩码:下三角矩阵为 1,上三角为 0,防止"看到未来"
mask = torch.tril(torch.ones(cfg.max_seq_len, cfg.max_seq_len))
# 注册为 buffer(随模型一起移动到 GPU,但不参与梯度更新)
self.register_buffer("mask", mask.view(1, 1, cfg.max_seq_len, cfg.max_seq_len))
def forward(self, x):
B, T, C = x.shape # B=批次大小, T=序列长度, C=隐藏维度
# 1. 投影成 Q, K, V
qkv = self.qkv(x) # [B, T, 3C],一次线性变换得到拼接的 QKV
q, k, v = qkv.split(C, dim=-1) # 在最后一维上拆分成三个 [B, T, C]
# 2. reshape 成多头形式
q = q.view(B, T, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, T, D]
k = k.view(B, T, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, T, D]
v = v.view(B, T, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, T, D]
# 3. 计算 attention 分数: Q @ K^T / sqrt(d_k)
scores = (q @ k.transpose(-2, -1)) / (self.head_dim ** 0.5) # [B, H, T, T]
# 4. 因果掩码:将上三角(未来位置)的分数置为 -inf
scores = scores.masked_fill(self.mask[:, :, :T, :T] == 0, float('-inf'))
# 5. softmax 得到注意力权重,再与 V 做加权求和
attn = F.softmax(scores, dim=-1) # 沿最后一维归一化 → [B, H, T, T]
out = attn @ v # 加权求和 → [B, H, T, D]
# 6. 合并多头:把 head 维度折叠回特征维度
out = out.transpose(1, 2).contiguous().view(B, T, C) # [B, T, C]
return self.proj(out) # 输出投影 → [B, T, C]
class FFN(nn.Module):
"""前馈网络:两层线性变换 + GELU 激活,中间扩展 4 倍维度。"""
def __init__(self, cfg):
super().__init__()
# 第一层:hidden_dim → 4 * hidden_dim
self.w1 = nn.Linear(cfg.hidden_dim, 4 * cfg.hidden_dim, bias=False)
# 第二层:4 * hidden_dim → hidden_dim
self.w2 = nn.Linear(4 * cfg.hidden_dim, cfg.hidden_dim, bias=False)
def forward(self, x):
return self.w2(F.gelu(self.w1(x))) # w1 → GELU → w2
class Block(nn.Module):
"""一个 Transformer Block:Pre-LN 结构的自注意力 + FFN,各带残差连接。"""
def __init__(self, cfg):
super().__init__()
self.ln1 = nn.LayerNorm(cfg.hidden_dim) # 第一个 LayerNorm(注意力前)
self.attn = CausalSelfAttention(cfg) # 因果自注意力子层
self.ln2 = nn.LayerNorm(cfg.hidden_dim) # 第二个 LayerNorm(FFN 前)
self.ffn = FFN(cfg) # 前馈网络子层
def forward(self, x):
# Pre-LN + 残差:先归一化再进子层,结果与原始输入相加
x = x + self.attn(self.ln1(x)) # 自注意力子层的残差连接
x = x + self.ffn(self.ln2(x)) # FFN 子层的残差连接
return x
class MiniGPT(nn.Module):
"""完整的迷你 GPT 模型:token 嵌入 + 位置嵌入 → N 层 Block → LayerNorm → 输出投影。"""
def __init__(self, cfg):
super().__init__()
self.cfg = cfg # 保存配置
self.tok_emb = nn.Embedding(cfg.vocab_size, cfg.hidden_dim) # token 嵌入表 [vocab, dim]
self.pos_emb = nn.Embedding(cfg.max_seq_len, cfg.hidden_dim) # 位置嵌入表 [max_len, dim]
self.blocks = nn.ModuleList([Block(cfg) for _ in range(cfg.num_layers)]) # 堆叠 N 个 Block
self.ln_f = nn.LayerNorm(cfg.hidden_dim) # 最终的 LayerNorm
self.head = nn.Linear(cfg.hidden_dim, cfg.vocab_size, bias=False) # 输出头:映射回词表维度
def forward(self, idx):
B, T = idx.shape # B=批次大小, T=序列长度
pos = torch.arange(T, device=idx.device).unsqueeze(0) # 位置索引 [0, 1, ..., T-1] → [1, T]
x = self.tok_emb(idx) + self.pos_emb(pos) # token 嵌入 + 位置嵌入 → [B, T, C]
for block in self.blocks: # 依次通过每个 Transformer Block
x = block(x)
x = self.ln_f(x) # 最终 LayerNorm
return self.head(x) # 输出 logits → [B, T, vocab_size]
# 跑一个 forward 测试
if __name__ == "__main__":
cfg = Config() # 使用默认配置实例化
model = MiniGPT(cfg) # 构建模型
x = torch.randint(0, cfg.vocab_size, (2, 64)) # 随机生成输入 token: batch=2, seq=64
logits = model(x) # 前向传播,得到 logits
print(logits.shape) # torch.Size([2, 64, 32000])
print(f"参数量: {sum(p.numel() for p in model.parameters()) / 1e6:.2f}M") # 打印参数量(百万)
跑一下输出:
torch.Size([2, 64, 32000])
参数量: 11.95M

图4.1 整体前向传播架构
说明:展示从输入 token [B, T] 到输出 logits [B, T, vocab_size] 的完整数据流:
- Token 嵌入与 Position 嵌入相加 → Transformer Block × N → LayerNorm → Linear 输出头

图 4.2 CausalSelfAttention 内部计算流程
说明:展示注意力的八步计算过程,每步标注张量形状变化:
- QKV 投影 → 拆分 Q/K/V → 多头 reshape
[B, H, T, D]→ 缩放点积Q@K^T/√d_k→ 因果掩码(上三角→-inf)→ Softmax →attn@V→ 合并多头 → 输出投影

图 4.3 Transformer Block 残差结构
说明: 展示 Pre-LN 架构的核心设计——两次残差连接:
- 第一条虚线旁路(residual)跳过
LN1 → CausalSelfAttention,在 ⊕ 处与主路径相加 - 第二条虚线旁路跳过
LN2 → FFN,在 ⊕ 处与主路径相加 - FFN 内部结构:
w1 (4× 扩展) → GELU → w2 (恢复)
关键设计要点:Pre-LN 在子层之前做归一化(而非之后),残差连接让梯度可以直通底层,因果掩码确保自回归特性——每个位置只能看到自身及之前的位置。

12M 参数的 mini-GPT,结构上和 Llama-3-70B 是完全一样的——只是层数、宽度、词表大小不同。
💡 真实工业级实现的差异
把
LayerNorm换成RMSNorm把绝对位置编码换成
RoPE把 FFN 的 GELU 换成 SwiGLU
Attention 改成 GQA(多个 Q 共享 K/V)
实际用
torch.nn.functional.scaled_dot_product_attention直接调底层 Flash Attention但核心骨架不变。后续大模型的所有"创新",都是这个骨架上的"换部件"。
五、工程意义:从架构到推理优化
这部分是本文最重要的一节。我们要把"架构理解"转化为"工程认知"。
5.1 KV Cache:自回归生成的最大优化
回看 Decoder-only 的生成过程:模型每次只生成一个新 token,然后把它拼回输入再生成下一个。
朴素做法(无 KV Cache):
生成第 1 个 token:处理 [prompt] → 算所有 K, V → 输出
生成第 2 个 token:处理 [prompt, t1] → 重新算所有 K, V → 输出
生成第 3 个 token:处理 [prompt, t1, t2] → 重新算所有 K, V → 输出
...
注意到了吗?前面 token 的 K 和 V 每次都在被重复计算。
KV Cache 做法:
生成第 1 个 token:处理 [prompt] → 算 K, V → 缓存 → 输出
生成第 2 个 token:只处理 [t1] → 算 t1 的 K, V → 拼到缓存 → 输出
生成第 3 个 token:只处理 [t2] → 算 t2 的 K, V → 拼到缓存 → 输出
...
每生成一个新 token,只需计算这一个新 token 的 K/V,然后拼到历史缓存上做 attention。
性能差异有多大? 对于一个 prompt 1000 token、生成 100 token 的请求:
-
无 KV Cache:总计算量 ≈ Σ(1000+i)² ≈ 1.1 × 10⁸
-
有 KV Cache:总计算量 ≈ 1000² + 100 × 1000 ≈ 1.1 × 10⁶
差 100 倍。这就是为什么任何工业级推理框架都必须支持 KV Cache。
5.2 KV Cache 的代价:显存
KV Cache 不是免费午餐——它会吃掉大量显存。
KV Cache 显存公式:
KV_cache_bytes = 2 × batch × seq_len × num_layers × num_kv_heads × head_dim × dtype_size
以 Llama-3-70B 为例:
-
num_layers= 80 -
num_kv_heads= 8(GQA 后) -
head_dim= 128 -
dtype_size= 2(FP16)
一个请求处理 32K 上下文:
2 × 1 × 32768 × 80 × 8 × 128 × 2 = ~10.7 GB
注意:这只是单个请求的 KV Cache。如果你想并发 10 个请求,KV Cache 总占用是 107 GB——比模型权重本身(140 GB)的占用还要可观。
这就是为什么 vLLM 的 PagedAttention 横空出世:把 KV Cache 当作"虚拟内存"管理,避免碎片化和过度预分配。详见 系列第 11 篇:推理加速三板斧。
5.3 上下文长度的代价:O(n²) 的诅咒
Attention 的核心计算 QK^T 是一个 n × n 的矩阵。这意味着:
-
上下文翻倍,attention 计算量变成 4 倍
-
上下文 10 倍,attention 计算量变成 100 倍
这就是为什么大家拼命要"扩上下文",但每多一个数量级都极其困难。
几个数字感受一下(同样是 Llama-3-70B,FP16):
|
上下文长度 |
KV Cache 显存 |
Attention 计算量(相对) |
|
4K |
1.3 GB |
1× |
|
32K |
10.7 GB |
64× |
|
128K |
43 GB |
1024× |
|
1M |
335 GB |
65536× |
1M 上下文不是"扩个参数"就能做到,它需要:
-
Flash Attention 优化 IO(系列第 13 篇)
-
Ring Attention 分布式注意力(系列第 15 篇)
-
YaRN 等位置编码外推(系列第 15 篇)
-
极其精细的显存管理
5.4 显存的三巨头:模型权重 + KV Cache + 激活值
实际部署一个大模型,显存被三部分吃掉:
显存 = 模型权重 + KV Cache + 激活值 + 临时 Buffer
以 A100 80G 部署 Llama-3-70B FP16 为例:
|
项 |
占用 |
说明 |
|
模型权重 |
~140 GB |
不可省,需 2 卡 |
|
KV Cache (32K, batch=4) |
~43 GB |
可压缩(量化、PagedAttention) |
|
激活值 |
~10 GB |
推理时可优化 |
|
Buffer 等 |
~5 GB |
框架开销 |
一张 A100 80G 跑不动——所以工业部署必须考虑:
-
量化压缩模型权重(INT8 / INT4,系列第 12 篇)
-
KV Cache 量化(INT8)
-
张量并行(TP)跨卡分割模型(系列第 20 篇)
六、变体与演进:Transformer 还能怎么变
理解了原版 Transformer,再来看主流变体就轻松了。
6.1 三大架构的胜负已分
Encoder-only ──── 适合理解(BERT),渐弱
Encoder-Decoder ── 适合翻译(T5),淡出
Decoder-only ──── 适合生成(GPT/Llama/Claude),主流
未来 5 年,主流大模型几乎全是 Decoder-only。
6.2 部件级的演进
主流大模型相对原版 Transformer 的"换件清单":
|
部件 |
原版 (2017) |
当下主流 (2026) |
|
归一化 |
LayerNorm |
RMSNorm |
|
位置编码 |
Sinusoidal |
RoPE |
|
激活函数 |
ReLU/GELU |
SwiGLU |
|
Attention |
MHA |
GQA / MQA |
|
LN 位置 |
Post-LN |
Pre-LN |
|
FFN 结构 |
单分支 |
MoE
(部分) |
每个改动看似微小,但叠加起来让模型效率提升数倍。
6.3 替代架构:Mamba、Linear Attention 等
近两年也有一些"挑战 Transformer"的架构:
-
Mamba / S4:状态空间模型,O(n) 复杂度
-
Linear Attention:把 attention 改造成线性复杂度
-
RWKV:RNN 风格但能并行训练
现状判断:这些架构在特定场景(长序列、低算力)有优势,但目前仍未撼动 Transformer 的主导地位。原因是:
-
工程生态(CUDA kernel、推理框架)全部围绕 Transformer 建立
-
Scaling Law 在 Transformer 上经过充分验证
-
模型能力上限尚未看到瓶颈
对工程师的建议:当下深入 Transformer,关注但不押注替代架构。
七、结语:地基打稳了,后面就好办了
Transformer 是大模型时代最重要的"地基"。它本身不复杂——核心就是几个矩阵乘法 + 残差 + 归一化——但它支撑的整个工程栈极其丰富。
读完本文,你应该已经能回答开头那几个问题了:
-
KV Cache 缓存了什么? 历史 token 的 K 和 V 矩阵,避免重复计算。
-
上下文为什么贵? Attention 是 O(n²),从 4K 到 1M 计算量增长 65536 倍。
-
为什么所有推理框架都围着 attention 转? 因为 attention 是计算 + 显存的双重瓶颈。
-
为什么 Decoder-only 赢? 任务统一、Scaling 友好、In-Context Learning 涌现。
如果你对某个部分还有疑问,强烈建议把第三节的 100 行代码亲手敲一遍跑一下。架构这种东西,看十遍不如手写一遍。
-------------------------------------------------
参考文献:
更多推荐











所有评论(0)