你有没有注意到这个现象:每次打开ChatGPT或Claude,输入问题后,总要等上一小会儿,屏幕上才蹦出第一个字。但紧接着,后面的文字就像开了闸一样哗哗往外涌,速度快得几乎跟不上。
这不是网络卡顿,也不是服务器抽风。背后是一个叫KV缓存的工程决策,专门用来让大模型推理变快。
![]()
要理解它为什么有效,得先从Transformer生成文本的基本原理说起。
模型是怎么"一个字一个字"往外蹦的
Transformer处理输入时,会给每个词都算出一个隐藏状态。这些隐藏状态再被投影到词汇表空间,变成一组分数——每个词一个分,叫logits。但模型只关心最后一个词的分数,从中采样出下一个词,把它拼回输入末尾,再重复整个过程。
关键洞察就在这里:要生成下一个词,其实只需要最新那个词的隐藏状态。其他所有隐藏状态都是中间产物,用一次就扔。
注意力机制到底在算什么
在Transformer的每一层里,每个词都会被拆成三个向量:查询向量Q、键向量K、值向量V。注意力机制用Q去点乘所有的K,得到注意力分数,再用这些分数给所有的V加权求和。
现在只看最后一个词。它的注意力计算需要:它自己的Q向量,以及序列中所有词的K和V向量。最终输出的隐藏状态,用的也是同样的配方——最新Q,加上全部K和V。
这意味着,要算出我们唯一需要的那个隐藏状态,每一层注意力都只需要:最新token的Q,以及所有历史token的K和V。
没有缓存时,算力是怎么被浪费的
生成第50个token时,模型需要token 1到50的K和V。生成第51个token时,需要token 1到51的K和V。问题在于,token 1到49的K和V早就算过了,输入没变,输出也不会变,但模型每次都从头重算一遍。
这是每步O(n)的冗余计算,整段生成下来就是O(n²)的浪费。
KV缓存的做法很简单:算过的K和V存起来,别扔。下次直接用新的Q去查全表,只算新增的那一个K和一个V,其余从内存里取。
注意力计算本身还是要遍历整个序列长度,但昂贵的K、V投影操作每个token只做一次,而不是每步都重做。
为什么第一个字特别慢
现在你能理解那个初始延迟了。当你发送提示词,模型要在一轮前向传播里处理完整输入,为每个token计算并缓存K和V。这叫预填充阶段,是整个请求中最吃算力的部分。
缓存 warmed up 之后,每个后续token只需要单token的单轮前向传播。那个让人抓狂的等待时间,就叫首token时间(TTFT)。提示词越长,预填充越久,等得越久。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.