01 - 推理服务的基本概念
这一篇是整个专题的地基。下面三个问题你要是都答得上来,可以直接跳到 02 篇:
- KV cache 为什么会越用越多?
- 预填充和解码有什么区别?
- 批开大了,为什么总吞吐涨、但每个人反而变慢?
不知道的话,这一篇必须先看 —— 后面十几篇里每一个「为什么会慢」的解释,最后都会落回这六个概念上。
只假设你知道一件事:大模型的回答是一个字一个字冒出来的,像有人在实时打字。
从这一件事出发,剩下的全都能推出来。
一、为什么是一 个字一个字
先问个很笨的问题:模型为什么不能一次把整句话输出完?
因为它压根不知道整句话。模型能做的只有一件事:给它一段文字,它告诉你下一个字最可能是什么。就这一个能力,没有别的。
所以要生成一整句,只能这么循环:
记住这一条,后面到处都要用
自回归的成本由「要生成多少字」决定,不由「输入多长」决定。
一个 5 万字的文档摘要成 50 个字,跟一句话扩写成 50 个字,生成阶段的步数是一样的 —— 都是 50 步。
二、KV cache:不想每次都重算
看上面那张图,你应该会立刻起疑:第 4 次的输入包含了前 3 次已经算过的全部内容,难道要整个重算一遍?
如果真的重算,代价高得离谱。生成第 100 个字要处理 100 多个字的输入,生成第 200 个字要处理 200 多个。算下来总计算量是步数的平方 —— 生成翻倍,计算量翻四倍。
好在不用。模型处理一段文字时,会给每个字算出两个中间结果(习惯上叫 K 和 V)。关键性质是:一个字的 K 和 V 只取决于它自己和它前面的字,跟后面还没生成的字无关。
既然跟后面无关,那算过一次就不会变,存下来下次直接用就行。
KV cache 是会涨的,而且只涨不落
一个请求生成得越久,它占的显存越多,中途不会释放。
所以推理服务有个纯文本时代就存在的经典问题:同时跑着的请求越多、生成得越长,显存越紧张。紧张到装不下时,框架只能把某个请求踢出去(这个动作叫抢占),等有空间了再从头重算。
三、预填充与解码:一次请求里的两种计算
回到第一张图。第 1 次和后面几次,其实不是同一种活。
- 第 1 次:输入是用户完整的那句话,比如 12 个字。这 12 个字之前从没算过,得全部算一遍。但它们可以同时算 —— 因为每个字的 K、V 只依赖它前面的字,12 个字的依赖关系一次就能理清。
- 第 2 次以后:只有 1 个新字要算,其余全在缓存里。
于是同一个请求被劈成了性质截然不同的两段: