Transformer 基础解析
你可能不训练模型,但大概率已经在用它:接一个大模型 API、做一个知识库问答、算一次 token 账单、给上下文长度设一个上限。这些决策背后站着同一个东西——Transformer。
这篇文章不推导公式,也不追求把论文讲全。它只回答一个问题:一个主要写 Java 后端的人,理解 Transformer 的哪些部分,能让自己在用大模型时少踩坑、少花冤枉钱、少做无效优化。
先用一句话把结论摆出来:Transformer 的本质是把「按顺序一步步算」换成了「一次性把所有位置两两算一遍」。它用一次矩阵乘法,把序列中任意两点的距离压缩成 1 步,代价是计算量随序列长度平方增长、推理时显存随长度线性增长。你后面会遇到的所有「贵、慢、上下文不够」的问题,几乎都能从这句话里找到根因。
一、它到底解决了什么问题
1.1 RNN 时代的三个结构性缺陷
在 2017 年之前,处理序列(句子、日志、时间序列)的标准解法是 RNN 及其改进版 LSTM。它们的共同点是一步一步递推:算完第 1 个词,才能算第 2 个词。
| 缺陷 | 本质原因 | 直观表现 | 后果 |
|---|---|---|---|
| 无法并行 | 第 t 步必须等第 t-1 步算完 | 一个句子里 512 个词,就得排 512 次队 | GPU 再强也吃不满,训练慢到无法规模化成今天的样子 |
| 长依赖难学 | 信息要穿过几百次递推才能从开头传到结尾 | 像传话游戏,传的人越多,原意丢得越多 | 长句、长文档里远距离的关联学不到 |
| 信息瓶颈 | 整句话被压进一个固定长度的向量 | 不管原文多长,解码时只能看同一个摘要 | 长文本翻译质量断崖式下降 |
一个类比:RNN 像一个人在会议室里从头读到尾,边读边记笔记,但笔记本只有一页,写满了就得覆盖。 位置越靠前的信息,被覆盖得越彻底。
1.2 一个容易被忽略的前史
注意力机制不是 Transformer 发明的。2015 年 Bahdanau 等人为了缓解「信息瓶颈」,让 RNN 的解码器每一步都能回头看一眼编码器的全部状态——这就是最早的注意力。
Transformer 的历史贡献不是发明注意力,而是做了一个极端决定:既然注意力能直接建模任意两个位置的关系,那 RNN 是不是可以整个删掉?
删掉递推之后,剩下的计算全是矩阵乘法。矩阵乘法是 GPU 最擅长的事情。这就是标题《Attention Is All You Need》的字面意思,也是后面八年一切变化的起点。
对你的意义:你不必理解 RNN,但要知道「串行」和「并行」的区别决定了今天模型的能力边界。凡是需要严格按顺序、无法并行的计算,在大规模场景下都会被淘汰。
二、真正需要理解的几个机制
这一节只讲「理解了会对你有用」的部分。不讲梯度推导,不讲初始化理论。
2.1 Q/K/V:把它当成一次「带权重的搜索」
Transformer 里最容易被讲玄乎的就是 Q、K、V。其实它就是一个可微的检索过程,用图书馆找书来类比最清楚:
| 符号 | 图书馆类比 | 在做什么 |
|---|---|---|
| Query(查询) | 你心里的检索需求 | 当前这个 token「想问什么」 |
| Key(键) | 每本书封面上的标签、索引卡 | 每个 token「能被怎么找到」 |
| Value(值) | 书里的实际内容 | 每个 token「实际提供什么信息」 |
| 匹配分数 | 需求与标签的相似度 | 谁跟谁相关 |
| 注意力权重 | 按相似度分配注意力 | 归一化成 0 到 1 的权重 |
| 输出 | 按权重把所有书的内容混合起来 | 加权求和的结果 |
关键区别在于:它不是「命中一条返回一条」的硬检索,而是「按相似度给所有内容分配权重再混合」的软检索。 硬检索不可导、没法训练;软检索完全可导,所以模型能自己学会「该怎么提问、该怎么贴标签」。
对你的意义:这就是 RAG 排障时最有用的一个直觉。模型检索到的内容和它「真正注意到」的内容不是一回事——检索给你证据,注意力告诉你模型看没看那些证据。当你遇到「文档明明检索到了,模型却答错」时,问题往往出在注意力被别的内容分散了,而不是检索失败。
2.2 注意力:一行公式,四步操作
整个自注意力的核心就是下面这一行:
1 | Attention(Q, K, V) = softmax( Q · Kᵀ / √d_k ) · V |
拆成四步,每一步都有明确的工程含义:
| 步骤 | 做什么 | 要记住的点 |
|---|---|---|
| ① 打分 | Q 乘以 K 的转置 | 算出「每对位置之间有多相关」,得到一个 n×n 的矩阵 |
| ② 缩放 | 除以 √d_k | 数值稳定性修正,见下 |
| ③ 归一化 | 沿最后一维做 softmax | 每行和为 1,即「这个位置把注意力怎么分配出去」 |
| ④ 聚合 | 权重乘以 V | 得到加权混合后的新表示 |
第 ② 步为什么必须缩放? 不用记推导,记结论就够:向量维度越大,点积的数值就越大;数值一大,softmax 就会输出接近「非 0 即 1」的结果——注意力全都压在一个位置上,其余全是 0。这样一来模型就没法「综合考虑多个信息源」,训练也会因为梯度接近 0 而停滞。除以 √d_k 就是把数值拉回正常范围。
一个实用的副产品:注意力权重是一个 n×n 的矩阵,天然记录了「每个位置看了哪些位置」。这是大模型可解释性最主要的抓手——想知道模型凭什么这么答,看它的注意力分布比看参数有用得多。
2.3 多头注意力:同时用多套视角看同一句话
单个注意力头只能产出一套「平均化」的关联关系,会把不同的模式互相抹平。多头的做法是并行跑好几套独立的注意力,最后拼起来。
| 单头 | 多头(一般 8 到 128 个头) | |
|---|---|---|
| 视角 | 一套 | 多套,每个头在不同子空间里关注不同模式 |
| 计算量 | n² × 维度 | 基本相同(每个头维度按比例缩小) |
| 参数量 | 相同量级 | 相同量级 |
| 实际观察 | — | 有的头盯前一个词、有的盯句法结构、有的负责代词指代 |
注意那个「基本相同」:多头不是「多花几倍算力买效果」,它买到的是表示能力,成本几乎不变。这是 Transformer 设计里性价比最高的一处。
2.4 位置编码:顺序必须专门喂进去
自注意力有一个反直觉的性质:它对位置是置换等变的。把一句话的词序打乱,输出只是跟着打乱,数值完全不变。因为它只关心「谁和谁相关」,不关心「谁在前」。
所以不加位置信息,Transformer 就是一个词袋模型。 位置必须显式注入。方案演进如下:
| 方案 | 做法 | 现状 |
|---|---|---|
| 正弦固定编码(2017 原论文) | 用三角函数生成绝对位置,加到词向量上 | 论文实测与可学习编码几乎无差别 |
| 可学习绝对位置 | 每个位置一个可训练向量 | 有最大长度上限,超出就无定义 |
| 相对位置偏置 | 在注意力分数上加与距离有关的偏置 | T5 等使用 |
| RoPE 旋转位置编码 | 让注意力内积只依赖两个位置的相对距离 | 当今大模型的主流(Llama 3 等) |
| ALiBi | 按距离对注意力分数线性衰减 | 外推性好,但采用度较低 |
对你的意义:上下文长度不是「把参数调大」就能变长的能力。它靠位置编码方案 + 训练时的长度扩展共同决定——这也解释了为什么很多模型要专门做「长上下文版本」,以及为什么超出训练长度的部分表现会明显退化。
2.5 残差连接与归一化:为什么能堆到 100 多层
每个子层外面都套了一层「残差 + 归一化」。两句话说明白:
- 残差连接(把输入直接加到输出上):给梯度留了一条直通车道。没有它,网络一深梯度就衰减,6 层都难训。
- 归一化:把每层的数值范围拉回稳定区间,防止层数一多数值就漂移。
几个需要知道的名词:
| 名词 | 一句话解释 | 现状 |
|---|---|---|
| LayerNorm | 沿特征维归一化,与 batch 大小无关 | 原论文用的是它 |
| BatchNorm | 沿 batch 维归一化 | 不适合序列:变长序列与 padding 会污染统计量 |
| Pre-LN | 先归一化再进子层 | 现代主流,训练更稳,可以不用预热 |
| Post-LN | 先算子层再归一化 | 原论文方案,训练更脆弱,需要预热 |
| RMSNorm | LayerNorm 的简化版,去掉去均值步骤 | 现代主流,更快,质量相当 |
对你的意义:这些细节不会影响你怎么调 API,但它们解释了「为什么模型能堆到 100 多层、几千亿参数」。稳定性工程做得好,规模才上得去。
2.6 FFN:参数和「知识」的真正大头
每个位置还会独立过一个两层的小型全连接网络(FFN),中间维度会扩张 3 到 4 倍。这里有一个多数人不知道的事实:
| 模型 | 注意力占参数比例 | FFN 占参数比例 |
|---|---|---|
| 原论文 base(d=512) | 33.3% | 66.7% |
| Llama 3 8B | 19.2% | 80.8% |
| Llama 3 405B | 17.9% | 82.1% |
(这两行不是估算,是我按官方配置逐项算出来的,见 5.3 节。)
有研究把 FFN 解释成一层可微的键值记忆:前半部分负责「识别出什么模式」,后半部分负责「识别到之后往输出里写什么」。模型中存储事实性知识的主要位置就是 FFN,这也解释了为什么知识编辑技术针对的都是 FFN 层。
对你的意义:模型的参数量主要买的是知识容量,而不是「推理能力」。这个结论直接支撑了后面 5.7 节的决策——补充知识优先用 RAG(便宜、可更新、可溯源),改变说话方式和输出格式才考虑微调。
三、和 RNN 的对比:为什么它赢了,代价是什么
3.1 三个维度看差异
原论文用三个维度对比了几种层类型,这张表是理解所有取舍的钥匙:
| 维度 | RNN | Transformer |
|---|---|---|
| 每层的计算量 | 随序列长度线性增长,但系数大 | 随序列长度平方增长 |
| 必须串行的步骤 | 与序列长度同阶(一步一递推) | 常数级(整层一次矩阵乘法) |
| 信息传递路径 | 从开头传到结尾要经过整条链 | 任意两点都是 1 步直达 |
三句话解读:
- 路径短意味着长距离依赖好学——这是 Transformer 质量更高的根本原因。
- 串行步骤少意味着能并行——这是它能被训练到几千亿参数的根本原因。
- 计算量是平方的意味着序列一长就贵——这是今天所有长上下文问题的根源。
3.2 平方复杂度:什么时候开始变贵
以 d=512 为例,把两者的计算量算出来(这里只列数量级,理解趋势即可):
| 序列长度 | 谁更省 | 差距 |
|---|---|---|
| 128 | 注意力更省 | 约 4 倍 |
| 256 | 注意力更省 | 约 2 倍 |
| 512 | 打平 | 转折点 |
| 1024 | RNN 更省 | 约 2 倍 |
| 2048 | RNN 更省 | 约 4 倍 |
| 8192 | RNN 更省 | 约 16 倍 |
| 131072 | RNN 更省 | 约 256 倍 |
规律:序列长度每翻一倍,注意力的成本翻 4 倍。 这就是为什么 8K 上下文和 128K 上下文的成本差距不是 16 倍,而是 256 倍(算力维度)。
所以在 512 个 token 以内(大多数句子、段落),注意力在算力上并不亏;一旦超过,账就开始难看。今天所有的长上下文优化——分块计算、稀疏注意力、滑窗注意力、状态空间模型——本质都是在跟这个平方项讨价还价。
3.3 推理阶段的账要重算:KV Cache
训练时 Transformer 完胜,但推理时的成本结构完全反转,这段是工程上最要命的:
| 维度 | RNN 推理 | Transformer 推理 |
|---|---|---|
| 需要保留的状态 | 常数:一个固定长度的隐状态 | KV Cache,随序列长度线性增长 |
| 每步计算 | 矩阵乘向量 | 读取全部权重 + 全部缓存 |
| 显存压力 | 固定 | 随上下文长度和并发数线性增长 |
| 瓶颈 | 参数带宽 | 参数带宽 + 缓存带宽 |
KV Cache 是什么?自回归生成是一步一个 token 往外吐的。如果不做缓存,每生成一个新 token 都要把前面所有 token 的中间结果重算一遍,复杂度会从平方恶化到立方。KV Cache 就是把已经算过的键和值存下来复用——代价是显存占用随上下文长度线性增长。
用 Llama 3 8B 的配置实算一遍(fp16):
| 配置 | 每 token | 8K 上下文 | 128K 上下文 |
|---|---|---|---|
| 假设不做优化(每个注意力头各存一份) | 512 KiB | 4 GiB | 64 GiB |
| 实际做法(分组共享,下节解释) | 128 KiB | 1 GiB | 16 GiB |
| 再加 8 路并发 | 128 KiB | 8 GiB | 128 GiB |
这张表能读出三个直接可用的结论:
- 128K 上下文下,单个请求的 KV Cache 就有 16 GiB,和 8B 模型本身的权重差不多大。 「上下文从 8K 扩到 128K」在显存账单上不是多备一点,而是要多备一整套同量级的显存。
- 8 路并发就是 128 GiB,已经超出一张 H100(80 GB)。 也就是说,长上下文服务的并发上限,是被显存卡住的,不是被算力卡住的。
- 这解释了一个常见现象:为什么有些服务「短问题很快,长文档就很慢甚至排队」——不是模型变笨了,是显存不够、只能排队。
3.4 那 RNN 系什么时候仍然是对的
| 场景 | 更适合 | 原因 |
|---|---|---|
| 流式/实时(音频、传感器、边读边处理) | RNN / 状态空间模型 | 状态固定,来一个 token 更新一次,不需要重算 |
| 超长序列(数十万 token) | 状态空间模型 / 线性注意力 | 线性复杂度,绕开平方项 |
| 边缘设备、极低显存 | RNN 或极小模型 | 没有 KV Cache,显存不随上下文增长 |
| 通用语言建模、大规模预训练 | Transformer 及其混合变体 | 并行性 + 短路径 + 可扩展,三条无替代 |
结论:Transformer 不是「全面更优」,而是「在大规模通用场景下综合最优」。 它把长依赖从「优化难题」变成了「成本问题」——而成本问题通常比数学问题好解决。
四、三种编解码范式:决定你该调哪种模型
4.1 差别只在「谁能看谁」
Transformer 有三种用法,本质上是三种注意力可见性:
flowchart LR
subgraph E["Encoder-only(理解)"]
E1["每个位置<br/>可见全部位置"] --> E2["双向表示<br/>输出向量"]
end
subgraph D["Decoder-only(生成)"]
D1["每个位置<br/>只能看自己和左边"] --> D2["因果表示<br/>可逐 token 生成"]
end
subgraph ED["Encoder-Decoder(转换)"]
ED1["编码器:双向"] --> ED2["解码器:因果 + 交叉注意力"]
end
| 注意力类型 | 谁能被看到 | 用在哪 |
|---|---|---|
| 编码器自注意力 | 全序列,双向 | 理解类任务 |
| 解码器自注意力 | 只能是已生成的部分(因果掩码) | 生成类任务 |
| 交叉注意力 | 查询来自解码器,键值来自编码器 | 编码器和解码器之间的桥 |
三者共用同一个注意力函数,区别只在「谁被遮住」。所谓因果掩码,就是把每个位置右边的分数全部设为负无穷,确保模型在预测第 t 个词时看不到第 t 个词之后的内容——这是自回归生成能成立的前提。
4.2 三种范式对照
| 维度 | Encoder-only | Decoder-only | Encoder-Decoder |
|---|---|---|---|
| 擅长 | 理解、编码成向量 | 生成、续写 | 输入输出长度差异大的转换 |
| 训练目标 | 遮词还原 | 预测下一个 token | 去噪还原 |
| 代表模型 | BERT、各类 embedding 模型 | GPT、Llama、DeepSeek | 原始 Transformer、T5 |
| 典型用途 | 检索、分类、聚类、语义匹配 | 对话、写作、代码、Agent | 翻译、摘要 |
| 你会怎么用到 | 搭 RAG 时的向量模型 | 你调的大模型 API | 相对少见 |
这张表里最实用的一行是最后一行:
- 你搭 RAG 用的 embedding 模型,几乎都是 Encoder-only。 它的产物是向量,不是文字,所以「让 embedding 模型回答问题」是方向性错误。
- 你调的大模型 API,几乎都是 Decoder-only。 它天生是「给前缀、续写下去」,所有任务都要被你改写成「续写」的形式——这就是提示词工程存在的根本原因。
4.3 为什么大模型最终都收敛到 Decoder-only
原始 Transformer 是编码器-解码器结构,但今天几乎所有通用大模型都是 Decoder-only。这不是理论最优,而是工程上最省事:
| 理由 | 说明 |
|---|---|
| 目标统一 | 任何任务都能改写成「给前缀、续写」。翻译、分类、抽取共用一套目标函数,不用为每个任务设计结构 |
| 结构最简 | 只有一种注意力模式,没有编码器/解码器两套堆叠和交叉注意力,代码路径少、并行策略简单 |
| 推理最省 | 只有一套 KV Cache,且可以单调追加、天然支持前缀复用(系统提示词只算一次) |
| 扩展最顺 | 加深、加宽、加数据就能按规律涨点,不需要额外设计 |
| 涌现了上下文学习 | 规模足够大时,模型能靠提示词里的几个示例学会新任务,不需要更新参数 |
一句话:Decoder-only 赢在工程,不是赢在理论。 它牺牲了双向注意力带来的表示效率,换来了极致的可扩展性。
五、大模型是怎么跑起来的
5.1 一次请求的完整流水线
flowchart TB Q["用户提问"] --> TOK["分词器<br/>文本转 token id"] TOK --> PRE["Prefill 阶段<br/>整个 prompt 并行前向"] PRE --> KVC["写入 KV Cache<br/>每层存 K 和 V"] KVC --> D1["Decode 每一步<br/>只算新 token 的 Q"] D1 --> LOG["最后一层输出 logits<br/>投影到词表维度"] LOG --> SAMP["采样<br/>temperature / top-p"] SAMP --> APP["追加新 token"] APP -->|"未结束:回到 Decode"| D1 APP -->|"遇到结束符"| DETOK["反分词<br/>token id 转文本"] DETOK --> ANS["返回回答"] KVC -.->|"每步复用已缓存的 K, V"| D1
下表是每一步「在干什么」和「对你的影响」:
| 步骤 | 在做什么 | 对你的影响 |
|---|---|---|
| ① 分词 | 把文本切成 token(子词),映射成数字 | 决定计费和上下文长度,见 5.2 |
| ② Prefill | 一次性并行处理整个输入 | 输入越长,首字延迟(TTFT)越大 |
| ③ 写 KV Cache | 把中间结果缓存下来 | 直接决定显存占用和并发上限,见 5.5 |
| ④ Decode | 一个一个往外吐 token | 决定吐字速度(TPOT),受显存带宽限制 |
| ⑤ 输出 logits | 算出每个候选 token 的分数 | 词表越大,这一步越占显存 |
| ⑥ 采样 | 按策略挑出下一个 token | 决定输出的稳定性和多样性,见 5.6 |
| ⑦ 循环 | 把新 token 追加后回到 ④ | 整个链路是串行的,无法并行加速 |
| ⑧ 反分词 | 把 token 拼回文本 | 流式返回时要注意半个词被截断的问题 |
记住这个结构,后面调优就有方向了:「首字慢」是 prefill 的问题(输入太长),「吐字慢」是 decode 的问题(模型太大 / 显存带宽不够),「并发上不去」是 KV Cache 的问题。
5.2 token 与成本:为什么中文更贵
模型不认识「字」,只认识 token(子词)。不同模型的分词方案不同,中文、代码、JSON 的「字符数转 token 数」比例差异很大:
| 模型 | 词表大小 |
|---|---|
| 原论文(英德翻译) | 约 37,000 |
| BERT | 30,522 |
| GPT-3 | 约 50,257 |
| Llama 3 | 论文称 128K |
| DeepSeek-V3 | 129,280 |
实际影响:同一个中文段落,在不同模型上可能相差 30% 以上的 token 数。这意味着
- 按 token 计费时,换一个模型可能直接改变成本,不能只看单价;
- 「上下文 128K」这个数字要换算成实际能放多少中文——按经验大约是 10 万汉字量级,但不同模型差异明显,上线前必须用真实业务文本实测;
- 存会话历史时按 token 而不是按字符来设阈值。
5.3 参数量与显存:自己算一遍就懂了
一个 Transformer 的参数量可以用几个简单规则算出来(现代大模型的配置):
1 | 每个注意力层 ≈ 2 × d² + 2 × d × (KV 头数 × 每头维度) |
用这套规则去反推 Llama 3 的官方参数量,能精确到小数点后一位:
| Llama 3 8B | Llama 3 405B | |
|---|---|---|
| 每层注意力 | 41,943,040(19.2%) | 570,425,344(17.9%) |
| 每层 FFN | 176,160,768(80.8%) | 2,617,245,696(82.1%) |
| 每层合计 | 218,103,808 | 3,187,671,040 |
| 乘以层数 | × 32 = 6.98 B | × 126 = 401.6 B |
| 加嵌入与输出头 | 1.05 B | 4.19 B |
| 算出来 | 8.03 B | 405.8 B |
| 官方公布 | 8.03 B | 405 B |
对上了。这个练习的价值在于:你以后看到一个模型的配置表,就能自己估出它的部署成本,不用等别人告诉你。
部署一个模型需要多少显存?三部分相加:
| 部分 | 大小 | 说明 |
|---|---|---|
| 模型权重 | 参数量 × 每参数字节数 | fp16 约 2 字节,int8 约 1 字节,int4 约 0.5 字节 |
| KV Cache | 见 5.5 节 | 随上下文长度和并发数线性增长,常常是真正的瓶颈 |
| 中间激活 | 与 batch 和序列长度有关 | 推理时通常占比最小 |
以 Llama 3 8B 为例:
| 精度 | 权重占用 | 能否放进单张 24 GB 消费级显卡 |
|---|---|---|
| fp16 | 约 16 GB | 勉强,但几乎没空间留给 KV Cache |
| int8 | 约 8 GB | 可以,能留出上下文和并发的余量 |
| int4 | 约 4 GB | 宽裕 |
这也是量化技术存在的全部理由:它用很小的质量代价,把权重体积压到 1/2 到 1/4,从而让同样的显卡能跑更大的模型或更长的上下文。
5.4 Prefill 与 Decode:两种完全不同的性能特征
推理不是一个动作,而是两个特征相反阶段:
| Prefill(处理输入) | Decode(生成输出) | |
|---|---|---|
| 处理对象 | 整个提示词,可能几千 token | 每次一个新 token |
| 并行度 | 全并行 | 完全串行 |
| 瓶颈 | 算力 | 显存带宽 |
| 影响指标 | 首字延迟 TTFT | 吐字速度 TPOT |
| 优化方向 | 减少输入长度、分块预填充 | 量化、批处理、减少 KV Cache |
Decode 为什么受带宽限制? 因为它每生成一个 token,都要把全部模型权重从显存里读一遍,却只做了极少的计算。实测推演:
| 模型(fp16) | 权重体积 | 单 token 理论下限 | 单请求吞吐上限 |
|---|---|---|---|
| Llama 3 8B | 16.1 GB | 约 4.8 ms | 约 209 token/s |
| Llama 3 70B | 141.2 GB | 约 42 ms | 约 24 token/s |
(按 H100 的 3.35 TB/s 显存带宽估算,这是理论上限,实际还要打折。)
这张表说明三件事:
- 单请求的生成速度上限由显存带宽决定,和算力无关。 8B 模型在 H100 上的理论上限就是 200 token/s 左右,换更贵的卡也不会更快。
- 70B 的权重有 141 GB,单张 H100(80 GB)根本放不下,必须多卡切分或者量化到 int8/int4。
- 提高吞吐的唯一出路是批处理:权重读一次,服务多个请求。但批一开大,KV Cache 立刻成为新瓶颈。
对你的意义:如果一个服务的「吐字速度」达不到要求,加机器不如换更小的模型或量化;如果「首字延迟」达不到要求,先砍输入长度。
5.5 KV Cache:并发能力的真正上限
KV Cache 的大小可以用一句话概括:层数 × 缓存的键值头数 × 每头维度 × 序列长度 × 并发数 × 2(键和值各一份)。
每一项都能优化,于是有了四代方案:
| 方案 | 做法 | 缓存规模 | 代表 | 代价 |
|---|---|---|---|---|
| MHA | 每个查询头各存一份键值 | 最大 | 原论文、BERT | 显存压力最大 |
| MQA | 所有头共享一份 | 最小 | PaLM、Falcon | 质量有轻微下降 |
| GQA | 分组共享,折中 | 中等(常见 8 组) | Llama 2/3 全系 | 基本无损 |
| MLA | 把键值压缩成低秩向量再缓存 | 最小(数量级下降) | DeepSeek-V2/V3 | 实现复杂 |
GQA 的效果很直观:Llama 3 的查询头有 128 个,但只保留 8 组键值缓存——缓存直接压到 1/16,而质量几乎无损。这就是「为什么大家都用 GQA」的答案。
给你的三个可操作结论:
- 评估长上下文能力时,不要只看「支持 128K」,还要问「在这个长度下能跑多少并发」——很多服务标称支持,实际并发只有个位数。
- 省显存的最有效手段通常是缩短输入,而不是换量化。因为 KV Cache 是按上下文长度和并发数线性叠加的,砍掉一半输入长度,等于白赚一倍并发。
- 前缀缓存(prompt caching)是性价比最高的优化之一:如果多个请求共享相同的系统提示词或长文档前缀,这部分只需计算一次,后续请求可以复用缓存。设计提示词时把固定的内容放前面、变化的内容放后面,能直接省掉重复的 prefill 成本。
5.6 采样参数:怎么调才不玄学
模型输出的是整个词表的概率分布,采样策略决定怎么从里面挑一个:
| 参数 | 作用 | 什么时候调 |
|---|---|---|
| temperature | 除以它再 softmax;越小越确定,越大越随机 | 抽取/分类类任务调到 0 到 0.3;创作类 0.7 到 1.0 |
| top-p(核采样) | 只在累积概率达到 p 的最小集合里采样 | 比 top-k 更自适应,通常比 top-k 更好用 |
| top-k | 只在概率最高的 k 个里采样 | 截断长尾,防止出现离谱词 |
| 重复惩罚 | 降低已出现 token 的分数 | 处理复读,但调太大会让输出变得不自然 |
最实用的经验:做结构化抽取、分类、写代码时,temperature 直接设 0 或接近 0。 你要的是稳定复现,不是创造力。很多「模型输出格式不稳定」的问题,就是这么解决的。
5.7 补充知识该用哪一招
这是后端开发最常遇到的决策。三个选项不是互相替代,而是各有边界:
| 改提示词 | RAG(检索增强) | 微调 | |
|---|---|---|---|
| 解决什么 | 输出格式、任务指令 | 补充知识、要求可溯源 | 固定风格、领域语感、特殊格式 |
| 知识更新 | 改文本即生效 | 改知识库即生效,分钟级 | 需重新训练,天级 |
| 成本 | 极低 | 中(存储 + 检索) | 高(数据 + GPU) |
| 可溯源 | 无 | 强,能给出引用原文 | 弱,知识混在参数里 |
| 适合 | 所有场景的第一步 | 知识密集、频繁更新的问答 | 风格与格式的一致性 |
决策顺序建议:先改提示词 → 不够就上 RAG → 还是不够(且问题是「说得不像」「格式总是跑偏」)才考虑微调。
一句总结:微调改变的是「怎么说」,RAG 补充的是「说什么」。 加上 2.6 节的结论(参数主要买的是知识容量),绝大多数企业知识问答场景,RAG 的性价比都远高于微调。
5.8 常见名词速查
看模型介绍时你会遇到一堆缩写,这张表一次说清:
| 名词 | 是什么 | 对你的影响 |
|---|---|---|
| RMSNorm | 简化版归一化,更快 | 只影响速度,不影响你怎么用 |
| SwiGLU | FFN 的门控激活方式 | 提升效果,是「现代模型标配」之一 |
| RoPE | 旋转位置编码,支持长上下文 | 决定长上下文能力上限 |
| GQA / MQA / MLA | 三种压缩 KV Cache 的方案 | 直接决定长上下文的并发和成本 |
| FlashAttention | 一种省显存、更快的注意力计算方式 | 不是近似算法,结果与标准算法等价 |
| MoE(混合专家) | 参数量很大但每次只激活一部分 | 总参数不等于部署成本,要看激活参数 |
| Pre-LN | 归一化的摆放位置 | 只影响训练稳定性 |
| 上下文长度 | 一次能处理多少 token(输入 + 输出) | 直接决定成本、延迟和并发 |
关于 MoE 多说一句,因为它最容易误导预算评估:DeepSeek-V3 总参数 671B,但每个 token 只激活 37B。 部署时权重仍然要全部加载(671B),但计算量按 37B 算——所以它「能力接近超大模型,速度接近中等模型」,代价是显存占用高。
六、接进 Java 后端要注意什么
这一节是纯应用经验的归纳,和模型内部结构无关,但和前面所有机制都有关联。
| 关注点 | 建议做法 | 背后的原因 |
|---|---|---|
| 流式返回 | 用 SSE 逐 token 推给前端,不要等全文生成完 | Decode 是串行的,等全文会让首字延迟等于总时长 |
| 超时设置 | 按「输出 token 数 × 单 token 时间」估算,不要沿用普通 HTTP 的 3 到 5 秒 | 生成是逐个 token 累积的,长回答天然耗时 |
| 限流维度 | 按 token 限流,不要只按请求数 | 一个 8 万 token 的请求和一个 50 token 的请求,成本差 3 个数量级 |
| 输入瘦身 | 会话历史做摘要裁剪,长文档做检索而不是全文塞 | 输入长度同时影响首字延迟、成本、并发三项 |
| 前缀复用 | 把固定内容(系统提示词、长文档)放前面,变化内容放后面 | 命中前缀缓存可以直接省掉重复的 prefill |
| 并发控制 | 给长上下文请求单独排队,限制最大并发 | KV Cache 是显存里的硬约束,超了就是 OOM 或排队 |
| 降级策略 | 准备好小模型/规则兜底,模型不可用时能降级 | 外部依赖 + 长耗时 = 必须考虑失败路径 |
| 可观测性 | 记录 token 数、首字延迟、吐字速度、重试次数 | 没有这些指标,成本和体验问题都无法定位 |
| 缓存边界 | 语义缓存要谨慎:相似问题可能答案不同 | 缓存命中错误比不缓存更伤用户体验 |
| 该不该用模型 | 能用规则、正则、检索解决的,不要上模型 | 大模型是最贵、最慢、最不确定的那个选项 |
一句总结:把它当成一个「慢、贵、不确定,但很聪明」的远程依赖来设计架构。 所有你对远程依赖做过的防护——超时、重试、熔断、降级、限流、缓存、监控——都需要再来一遍,而且阈值要重算。
七、总结
| 维度 | RNN | Transformer |
|---|---|---|
| 核心操作 | 一步步递推 | 一次矩阵乘法,所有位置两两交互 |
| 能否并行 | 不能 | 能,这是它能做大的根本原因 |
| 长距离依赖 | 难学 | 好学,但从「优化难题」变成了「成本问题」 |
| 计算量 | 随长度线性增长 | 随长度平方增长 |
| 推理显存 | 固定 | 随上下文和并发线性增长 |
| 位置信息 | 结构自带 | 必须显式注入(现代用 RoPE) |
六条可以直接拿去用的结论:
- 注意力就是一次带权重的软检索。 它同时解释了模型为什么有效,以及为什么 RAG 排障要看「模型注意到什么」而不只是「检索到什么」。
- 模型参数的大头在 FFN,主要在存知识。 所以补知识用 RAG,改风格才用微调。
- 三种范式对应三种用法:Encoder-only 出向量(你的向量模型)、Decoder-only 出文字(你调的大模型)、Encoder-Decoder 做长度差异大的转换。
- 首字慢、吐字慢、并发低是三件不同的事,分别对应输入长度、模型大小与显存带宽、KV Cache 占用,别混在一起优化。
- 显存账 = 权重 + KV Cache + 激活,其中 KV Cache 随上下文和并发线性增长,往往是真正的瓶颈。省显存先砍输入长度,其次才是量化。
- 温度调低能解决大部分「输出不稳定」的问题,在结构化抽取和代码场景尤其明显。
最后回到开头那句话:Transformer 把「按顺序一步步算」换成了「一次性两两算一遍」。 理解了这笔交易的收益(并行、可扩展、短路径)和代价(平方算力、线性显存),你在选模型、定上下文长度、估成本、排性能问题时,就有了判断依据,而不是只凭经验和传言。
参考
- Vaswani et al. Attention Is All You Need, NeurIPS 2017, arXiv:1706.03762
- Bahdanau et al. Neural Machine Translation by Jointly Learning to Align and Translate, 2015, arXiv:1409.0473
- Shazeer. Fast Transformer Decoding: One Write-Head is All You Need(MQA), 2019, arXiv:1911.02150
- Ainslie et al. GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints, EMNLP 2023, arXiv:2305.13245
- Su et al. RoFormer: Enhanced Transformer with Rotary Position Embedding(RoPE), 2021, arXiv:2104.09864
- Zhang & Sennrich. Root Mean Square Layer Normalization(RMSNorm), NeurIPS 2019, arXiv:1910.07467
- Shazeer. GLU Variants Improve Transformer(SwiGLU), 2020, arXiv:2002.05202
- Dao et al. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness, NeurIPS 2022, arXiv:2205.14135
- Geva et al. Transformer Feed-Forward Layers Are Key-Value Memories, EMNLP 2021, arXiv:2012.14913
- Devlin et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding, 2018, arXiv:1810.04805
- Brown et al. Language Models are Few-Shot Learners(GPT-3), 2020, arXiv:2005.14165
- Grattafiori et al. The Llama 3 Herd of Models, 2024, arXiv:2407.21783
- DeepSeek-AI. DeepSeek-V3 Technical Report, 2024, arXiv:2412.19437
- DeepSeek-V3 官方推理配置
- PyTorch 官方文档 — scaled_dot_product_attention





