📚 本文是《Transformer 由浅入深》系列第 10 篇 · 阶段四「走向现代 LLM」(完结)

走到最后一篇了。前九篇我们从 RNN 的痛点出发,一路搭到完整的 Transformer 和现代 LLM。但原版 Transformer 离"能高效跑起来的大模型"还有距离——它有两个明显的硬伤。这一篇,我们看现代 LLM 用哪些利器把它变得更快、更省、更长、更大,并为整个系列收尾。

打个比方:前九篇我们造出了一台原理完美的发动机,但要把它装进一辆能上路、能跑长途、还省油的车里,还得加上变速箱、涡轮、油电混动这些"工程优化"。本篇讲的就是这些让 Transformer 真正能商用、能服务上亿用户的关键工程。它们大多不改变"注意力"这个核心思想,而是围绕它,把"跑得起来"做到极致。

本篇目标

读完你应该能:

  • 说清 Transformer 的两个核心效率瓶颈;
  • 理解 KV Cache、FlashAttention、MQA/GQA、MoE 各自解决什么问题;
  • 在脑中建立一张"现代 LLM 优化地图"。

阅读前提

最好读过全系列前 9 篇,尤其第 3 篇(\( O(n^2) \) 复杂度)和第 9 篇(自回归生成)。

1. 两个瓶颈回顾

把前面埋的两个伏笔收回来:

  1. 注意力是 \( O(n^2) \) 的(第 3 篇)。那张 \( n \times n \) 的注意力分数矩阵,随上下文长度 \( n \) 平方级增长。\( n \) 翻 10 倍,显存和算力涨 100 倍——这是长文本的拦路虎。
  2. 自回归生成有大量重复计算(第 9 篇)。每生成一个新词,朴素做法要把整个序列重跑一遍,而其中绝大部分计算和上一步完全一样。

为什么 \( O(n^2) \) 这么可怕?想象一个会议室里要让每个人都和其他所有人握一次手:10 个人是 45 次握手,100 个人就是约 5000 次,1000 个人则接近 50 万次。人数(序列长度)线性增长,握手次数(注意力计算)却平方级暴涨。注意力的本质正是"每个词都要和每个词算一次相关度",所以序列一长,代价就失控。

第二个瓶颈则更像"健忘的抄写员":他每抄一个新字,都要把前面已经抄好的整段重新读一遍、重新誊一遍,哪怕前面那些字一个标点都没改。明明可以直接接着写,他却每次从头来过——这正是朴素自回归生成在干的傻事。生成一篇一千字的文章,他得把前面写过的内容平均重读五百遍,绝大多数都是无用功。

这两个瓶颈的性质其实不太一样,值得分清:第一个是算法复杂度问题——再快的硬件也扛不住平方级的增长,长度一上去总会撞墙;第二个是重复劳动问题——计算本身没毛病,只是被反复做了很多遍。性质不同,药方也不同:前者要么换更省的算法、要么干脆只算一部分;后者只要把算过的结果存下来复用就行。下面的技术,基本都是冲着这两个瓶颈、对症下药来的。

2. KV Cache:推理提速的基石

先解决第二个瓶颈。观察一个事实:自回归生成时,已经生成的那些词,它们的 K、V 向量在后续每一步里都是不变的——既然不变,何必每步重算?

KV Cache 就是把历史 token 的 K、V 缓存起来。每生成一个新词,只需:为这一个新词算出它的 Q、K、V,把新的 K、V 追加进缓存,然后让新 Q 和缓存里的全部 K、V 做注意力即可。

这就像开会时记笔记:前面同事说过的话,你记在本子上,后面要用直接翻笔记,而不是让每个人把之前的发言再原原本本复述一遍。KV Cache 缓存的就是这本"会议纪要",新发言人(新 token)只管说自己的新内容,需要参考历史时直接查本子。

无缓存:第 t 步,重算前 t 个词的 K/V         → 每步 O(t·d) 重复劳动
KV Cache:第 t 步,只算第 t 个词的 K/V       → 每步只 O(d),历史直接取缓存

值得强调的是,KV Cache 只在**推理(生成)**阶段才有意义。训练时整段文本是一次性、并行喂进去的(第 9 篇讲过的"teacher forcing"),根本不存在"一个词一个词往外吐"的过程,自然也无所谓缓存;而推理时模型必须一步一步生成,前一步的输出是后一步的输入,这种"串行"特性才让"把历史存下来复用"变得既必要又划算。所以你会看到,很多优化都是分训练、推理两套账来算的。

这是今天所有 LLM 推理的标配,提速巨大。但它有代价:缓存大小随序列长度线性增长,长上下文时 KV Cache 会吃掉大量显存——这又催生了下面的优化。换句话说,KV Cache 做的是一笔典型的"空间换时间“交易:用显存里那本越记越厚的笔记本,换来每一步生成不再重复劳动。笔记本越厚(上下文越长),占的桌面(显存)也越大,所以接下来我们要想办法把这本笔记记得更精简。

3. 省显存的注意力:MQA / GQA

KV Cache 的显存,正比于"头数 × 序列长度”。既然多头注意力每个头都存一份 K、V,能不能让多个头共享 K、V?

  • MQA(Multi-Query Attention):所有 query 头共享同一组 K、V。KV Cache 直接缩小到原来的 \( 1/h \),省得极致,但有时略损质量。
  • GQA(Grouped-Query Attention):折中方案——把 query 头分成几组,每组共享一组 K、V。在"省显存"和"保质量"之间取得平衡。

可以这样想:多个 query 头就像一个项目组里的几位同事,每人负责从不同角度提问(这是第 4 篇讲的多头价值,不能省)。但他们查阅的资料(K、V)不一定要人手一份——原版 MHA 是每人复印一整套,占地方;MQA 是全组共用一份资料,最省纸但有时不够用;GQA 则是按小组各配一份,几个人共用一份资料,既不浪费又够用。提问的多样性保留了,存资料的开销却降了下来。

MHA(原版):  每个 Q 头配自己的 K/V      ←  质量高,KV Cache 大
GQA:         每组 Q 头共享一份 K/V       ←  折中(现代主流)
MQA:         所有 Q 头共享一份 K/V       ←  最省,质量略降

今天的主流大模型(LLaMA-2/3、Qwen 等)大多采用 GQA。它的妙处在于:省的是"存 K、V"的显存,动的不是"提问角度"的数量,所以质量损失通常很小,却能让 KV Cache 缩小好几倍——长上下文场景下立竿见影。

顺带一提,MQA/GQA 和 KV Cache 是天生的搭档:正因为 KV Cache 的开销正比于"头数 × 序列长度",而长上下文又让序列长度这一项失控,人们才格外想从"头数"这一项里抠出节省。两者合在一起,等于把那本越记越厚的"会议纪要"再做了一次精简归档——既保留够用的信息,又不让它撑爆显存。这也是为什么现代模型几乎总是把它们成套使用。

4. FlashAttention:把注意力算得更快

回到第一个瓶颈。FlashAttention 的厉害之处在于:它不改变注意力的数学结果,却能算得又快又省显存。

它的洞察是:GPU 的瓶颈往往不是算得慢,而是数据在显存和高速缓存之间搬来搬去太慢(IO 瓶颈)。朴素实现会把那个巨大的 \( n \times n \) 分数矩阵完整地写进显存、再读出来,IO 开销巨大。

这就好比一个厨师做菜,刀工火候(计算)其实很快,真正费时间的是反复跑去冰箱(显存)取一样食材、用完再放回、再跑去取下一样。GPU 的"灶台"(片上高速缓存)很小很快,“冰箱”(显存)很大但远,来回搬运才是隐形的时间杀手。

FlashAttention 用分块(tiling) 的技巧:把计算切成小块,在 GPU 的高速片上缓存里边算边累加,始终不把完整的 \( n \times n \) 矩阵落地到显存。结果是相同的数学输出,却大幅减少 IO,显存占用从 \( O(n^2) \) 降到 \( O(n) \),速度还更快。它已经成为现代训练和推理的事实标准。延续上面的比方:它的策略是一次从冰箱端来一小批食材摆在灶台边,就地全部处理完再换下一批,少跑腿、不囤大盘,菜的味道(结果)分毫不差,出菜却快得多。

注意区分:FlashAttention 是精确的(结果不变,只是算法更聪明);而下面的稀疏/线性注意力是近似的(用精度换更低的复杂度)。

这个"精确优化"和"近似优化"的区别很关键,值得多说一句。精确优化像是换了条更聪明的路线去同一个目的地——路变了,终点一模一样,你不会损失任何东西,所以能放心大胆地用;近似优化则像是抄了条近道,省了时间,但可能会错过沿途某些风景——它真的会改变结果,只是赌"改变得不多、换来的速度很值"。理解了这层差别,你就知道为什么 FlashAttention 能成为人人都用的默认项,而稀疏/线性注意力则要看任务、需要权衡。

还有一点容易混淆:FlashAttention 并没有降低注意力的算法复杂度——它本质上还是 \( O(n^2) \) 次乘加,该算的乘法一次没少。它优化的是怎么算、怎么搬数据,而不是算多少。所以严格说,它解决的是"常数项太大、硬件跑不满"的工程问题,而不是第 5 节那些动算法复杂度的路线要解决的"渐进增长太快"的理论问题。两类优化层次不同,常常叠加在一起用:先用 FlashAttention 把每一步算得又快又省,再用稀疏或外推手段去够更长的距离。

5. 长上下文之路

要让模型读得更"长"(几万、几十万 token),需要多管齐下:

  • 位置外推:让训练时只见过短序列的模型,在更长序列上仍能工作。常见手段是对 RoPE(第 5 篇)做插值或 NTK 缩放,把没见过的远距离"压"进见过的范围。
  • 稀疏 / 滑动窗口注意力:不让每个词都看全部词,而是只看邻近一个窗口或少数关键位置,把 \( O(n^2) \) 降下来。
  • 线性注意力:用数学近似把注意力的复杂度从 \( O(n^2) \) 降到 \( O(n) \),代价是损失一些精度。

这几条路线各有性格,不妨这样理解:位置外推像是教一个只在小城市开过车的司机去跑高速——路本身没变,只是想办法让他适应更远的距离;滑动窗口注意力则承认"人不可能同时盯着所有人",于是规定每个词只重点看身边一圈邻居,远处的信息靠一层层传递间接获得,就像读小说时你主要记得最近几页、更早的剧情靠模糊印象;线性注意力则是干脆用一套数学近似,把"每个词对每个词"的全连接换成更省的等价估计,复杂度从平方降到线性,代价是精度打点折扣。

为什么上下文越长,代价越大?根子还在第 1 节那张平方级增长的握手表,再叠加上 KV Cache 随长度线性变厚的显存。两者一起,使得"读得更长"几乎总要在速度、显存、精度之间做取舍。这也解释了一个常见现象:同样一个模型,你喂给它的资料越长,它响应越慢、越占资源——你并不是在做一道"长度乘个常数"的加法题,而是在爬一条越来越陡的坡。这些思路各有取舍,长上下文至今仍是活跃的研究前沿,业界至今没有一个"既精确、又便宜、还无限长"的银弹,更多是针对具体场景挑选合适的组合。

6. 把模型做大又不做慢:MoE

最后一个方向是扩规模。通常模型越大越强,但也越慢——因为每个 token 都要过全部参数。MoE(Mixture of Experts,混合专家) 打破了这个绑定。

它把 FFN(第 6 篇说过,这是参数大头)换成许多个并列的"专家"FFN,再加一个路由器(router)。每个 token 进来,路由器只挑少数几个专家(比如 8 选 2)来处理:

          ┌─► 专家1
token ─► 路由器 ─► 专家2  ✓(被选中)
          ├─► 专家3  ✓(被选中)
          └─► 专家4 …(本次不激活)

这就像医院的专家会诊:医院里养着一大批各科专家(总参数量巨大,知识储备深厚),但你挂号看病时,分诊台(路由器)不会把所有专家都叫来围着你转,而是根据你的症状只请相关的一两位(比如心内科和影像科)。其余专家照常待命,这次不打扰。于是医院的"总实力"可以很强,而你单次看病的"成本"却很低。

效果是:模型的总参数量可以做得极大(知识容量大),但每个 token 实际只激活一小部分(计算量小)。这让"参数规模"和"计算成本"解耦——以前这两者是死死绑在一起的,参数翻倍计算就翻倍;MoE 把这根绳子剪断了,你可以只为"会诊请来的那几位专家"付计算费,却享受"整个专家库"的知识广度。这是当前超大模型的热门路线。代价是路由带来的工程复杂度和负载均衡问题:得保证别让少数几个"网红专家"被挤爆、其他专家闲置,否则又快不起来。

这里还藏着一个容易被忽略的细节:不同的专家在训练中会逐渐分化、各有所长,有的更擅长代码、有的更擅长某种语言或某类常识。路由器要学会的,正是"看一眼这个 token 的上下文,就把它派给最对口的专家"。这有点像一家大公司随着规模扩张自然分出了不同部门,而前台要练就一双慧眼,把每个来访者准确引到该去的窗口。分工越合理,整体效率越高;反之若路由学得不好,会诊就成了"乱点鸳鸯谱",再多专家也发挥不出实力。正因如此,MoE 虽然理念优雅,真正训练好却相当讲究工程功力。

7. 一张「现代 LLM 优化地图」

把本篇技术按目标归位,方便你建立全局观:

目标代表技术
提速度(推理)KV Cache、FlashAttention
省显存GQA / MQA、FlashAttention
扩长度(长上下文)RoPE 外推、稀疏 / 滑动窗口 / 线性注意力
扩规模(更大模型)MoE

几乎每一个现代大模型,都是"原版 Transformer + 这张表里若干技术"的组合。回到开头的比方:原版 Transformer 是那台完美发动机,而这张表就是变速箱、涡轮、混动系统的清单——把它们按需装上,才有了今天你用到的那些又快又强又能读长文的大模型。

值得留意的是,这四个目标之间并不总是"既要又要"地皆大欢喜,有时还会互相拉扯。比如扩长度往往加重显存压力,于是省显存的手段就更不可或缺;扩规模(MoE)虽然省了计算,却把更多参数压进显存,又得靠别的办法腾地方。所以真正落地一个大模型,从来不是"把好技术全堆上去"那么简单,而是在速度、显存、长度、规模这四个维度之间反复权衡、找到适配自己业务场景的那个平衡点。看懂了这张地图,你再去读任何一篇新模型的技术报告,就能迅速对号入座:它在每个维度上分别下了什么注、做了什么取舍。

常见疑问与易错点

问:用了 KV Cache,生成质量会不会下降? 答:不会。KV Cache 缓存的是历史 token 那些本来就不会变的 K、V 向量,算出来的结果和不缓存时完全一致,只是省去了重复计算。它是纯粹的"提速不改味",你完全不必担心质量。

问:FlashAttention 会改变模型的输出吗? 答:不会(在数值精度范围内可视为不变)。它是精确优化——换了更聪明的计算顺序和搬运方式,数学上算的还是同一个 \( \text{softmax}(QK^\top/\sqrt{d_k})V \)。这正是它能被所有人当默认项用的原因。要小心的是另一类近似方法(稀疏/线性注意力),那些是真的会改变结果、需要权衡的。

问:MoE 模型参数那么大,是不是就一定更慢、更费显存? 答:计算上不一定更慢。每个 token 只激活少数专家,所以实际算力接近一个小得多的稠密模型——这正是 MoE 的卖点。但要注意:虽然算得不多,全部专家的参数仍要加载进显存待命,所以它"省的是计算,不省存储"。换句话说,MoE 省的是单次推理的算力账,显存账反而可能更大。

问:为什么上下文越长,又慢又贵? 答:两个原因叠加。一是注意力本身是 \( O(n^2) \),长度翻倍计算涨四倍(回顾第 1 节的握手比方);二是 KV Cache 随长度线性变厚,越长越吃显存。所以长上下文几乎总要在速度、显存、精度之间做取舍,这也是它至今仍是研究前沿的原因。

问:GQA/MQA 让多个头共享 K、V,会不会削弱多头注意力的好处? 答:影响通常很小。多头的核心价值是"从不同角度提问"(不同的 Q),而 GQA/MQA 动的只是被共享的"参考资料"(K、V),并没有减少提问的角度数量。所以它在大幅压缩 KV Cache 的同时,质量损失往往可以接受——这也是 GQA 成为现代主流的原因。

问:这些优化能不能同时用? 答:能,而且现代大模型基本都是组合拳。比如一个典型的开源大模型,可能同时用了 GQA(省显存)+ FlashAttention(精确提速)+ RoPE 外推(扩长度),MoE 模型还会再叠一层混合专家。它们针对的瓶颈不同,彼此正交,叠加使用正是第 7 节那张"优化地图"的现实写照。

8. 系列总结

十篇走下来,我们完整地走过了这条主线:

  1. 痛点:RNN 的长距离依赖与无法并行(第 1 篇);
  2. 核心思想:用注意力让每个词直接环顾全场(第 2 篇);
  3. 机制:Scaled Dot-Product Attention 的数学(第 3 篇)、多头(第 4 篇)、位置编码(第 5 篇);
  4. 完整模型:Transformer Block(第 6 篇)与 Encoder-Decoder 架构(第 7 篇);
  5. 走向 LLM:三大流派(第 8 篇)、训练与生成(第 9 篇)、效率与变体(本篇)。

如果让我用一句话总结 Transformer 为什么能成:它用"注意力"这一个简单而强大的机制,既缩短了信息的传播距离,又彻底释放了并行计算——剩下的,交给规模。

想继续深入?

  • 📄 原始论文:《Attention Is All You Need》(2017)——现在回头读,你会发现它出奇地好懂;
  • 📝 图解经典:Jay Alammar 的《The Illustrated Transformer》;
  • 🛠️ 动手实现:Andrej Karpathy 的 nanoGPT / “Let’s build GPT” 视频,从零写一个 GPT;
  • 📚 关键模型论文:GPT 系列、BERT、T5、LLaMA、以及 FlashAttention 原论文。

最好的巩固方式,是自己动手写一遍——哪怕只是几十行的 toy 版。当你亲手让那个 softmax(QKᵀ/√dₖ)V 跑起来、看着 loss 下降、模型开始吐出像样的句子时,这十篇文章里所有的概念,都会真正变成你自己的。

感谢你读完这个系列,祝你在大模型的世界里玩得开心。 🎉


📖 系列目录(全 10 篇 · 完结)

  1. 为什么需要 Transformer?
  2. 注意力到底在「注意」什么?
  3. Self-Attention 的数学
  4. 多头注意力
  5. 位置信息从哪来?位置编码
  6. 一个 Transformer Block 的全貌
  7. Encoder-Decoder 完整架构
  8. 从 Transformer 到 GPT 与 BERT
  9. 训练与生成
  10. 效率与现代变体(本篇)