从输入到输出:大模型推理过程与缓存计费详解
🔔 核心误解速查表(来自真实提问)
| ❌ 常见误解 | ✅ 正确理解 |
|---|---|
| 模型会把中文翻译成英文后再分token | 模型处理的是原始UTF-8字节或直接含中文的词表,不会自动翻译 |
| “指令”是独立于提示词的一个步骤 | 指令只是提示词的一部分,模型不区分对待 |
| Token化就是传统分词 | Token化可能切出子词(如”学习”分成”学””习”),比分词更细 |
| Key/Value向量就是嵌入向量 | 嵌入向量是原料,K/V是从中加工出来的专用向量 |
| 预填充结果可能是随机的 | 预填充纯矩阵运算,完全确定 |
| 缓存命中允许微小变化(如空格) | 必须从第一个Token开始完全一致,任何差异都会导致失效 |
一、大模型推理的正确流程(总览)
许多人对流程的理解存在偏差。下图是标准自回归模型(如GPT)的完整步骤:
1 | 输入提示词 → 分词 → 嵌入 → 预填充(构建KV缓存) → 自回归生成(逐Token采样) → 输出 |
- 分词与嵌入:将原始文本拆分为Token,并映射为稠密向量。
- 预填充:一次性计算所有输入Token的Key、Value向量并缓存。此阶段计算密集,但只做一次。
- 自回归生成:逐个产生新Token,每步仅计算新Token的K/V并复用缓存。根据采样策略,输出可能确定或随机。
以下各章将按此顺序逐一详解。
二、分词与嵌入:从文字到向量
2.1 什么是Token?
- 模型不直接认识文字,需要先将输入文本拆分为最小处理单元——Token。
- 对于中文,一个Token可以是字、词或子词。例如”机器学习”可能分成
["机器", "学", "习"]。
❗ 常见误解:Token化就是传统的中文分词(如jieba)。
✅ 正确理解:Token化会切得更细,常常把完整词拆成常见的片段(如”学习”拆为”学”和”习”),目的是控制词汇表大小并覆盖所有字符。
2.2 模型会把中文翻译成英文再处理吗?
❗ 常见误解:听说某些模型不支持中文,会把中文先翻译成英文再分token。
✅ 正确理解:绝对不会。模型只处理你输入的原始文本的UTF-8字节或直接包含中文词表。如果词表没有中文,每个汉字会变成3个字节Token(如”中” →<0xE4> <0xB8> <0xAD>),这是字节级编码,不是翻译。
2.3 嵌入向量:赋予Token数学含义
- 每个Token通过查一个可学习的嵌入表,得到一个固定维度的稠密向量(如768维),称为嵌入向量。
- 相同Token永远映射到相同嵌入向量(不考虑位置编码时),因此是静态的。
- 嵌入向量是模型的原始输入特征,携带该Token的基础语义信息。
三、预填充阶段:构建KV缓存
当整个输入序列的嵌入向量准备好后,模型进入预填充(Prefill)阶段。这一步会一次性计算所有Token的Key、Value向量并缓存,供后续生成复用。
3.1 从嵌入向量到Key/Value
- 每个Token的嵌入向量
X分别乘以三个可训练的权重矩阵:1
Q = X · W_Q, K = X · W_K, V = X · W_V
- 其中
W_K,W_V将嵌入”加工”为专门用于注意力机制的Key向量和Value向量。
❗ 常见误解:Key/Value向量就是嵌入向量,只是换个名字。
✅ 正确理解:嵌入向量是原料,Key/Value是从原料加工出来的专用工具。嵌入用于表示Token本身,Key用于匹配注意力权重,Value用于提供加权内容。三者完全不同。
- Key用于匹配相关度(与其他Token的Query计算注意力权重)。
- Value用于提供内容(根据权重加权求和,得到输出)。
通俗类比:
| 向量类型 | 类比 |
|---|---|
| 嵌入向量 | 员工原始简历 |
| Key | 简历中提取的”技能标签”(用来匹配岗位需求) |
| Value | 简历中提取的”实际项目经验”(被选中后贡献的内容) |
3.2 KV缓存:为什么能省计算?
- 在自注意力中,每个Token都需要所有Token的Key和Value。如果每次生成新Token都重新计算整个序列的K/V,成本极高。
- 预填充阶段一次性算好所有Token的K/V,并存入KV缓存。之后每生成一个新Token,只需计算该Token自己的K/V,追加到缓存中即可。
- 相同输入的前缀,其缓存可直接复用——这就是缓存命中的基础。
3.3 确定性说明
❗ 常见误解:因为模型有随机性,所以预填充的结果也可能每次不同。
✅ 正确理解:在推理模式下(关闭Dropout等随机层),相同的输入序列 → 相同的嵌入 → 相同的K/V → 预填充结果完全确定。模型的随机性只出现在后续的生成采样阶段。
四、自回归生成阶段:逐Token采样与输出
预填充完成后,模型进入自回归生成阶段,逐个产生新的Token。
4.1 每一步做什么?
- 利用已有的KV缓存(包含输入序列及之前已生成的所有Token),计算下一个Token的概率分布。
- 根据采样策略(如温度采样、Top-K、Top-P)随机或确定地选出一个Token。
- 将该Token追加到序列末尾,计算其K/V并加入缓存。
- 重复直到遇到结束符或达到最大长度。
4.2 随机性的来源
- 如果使用贪心搜索(每次选概率最高的Token),输出完全固定。
- 如果使用随机采样(多数API默认),则相同输入可能产生不同输出——这正是常见”模型不听话”的真正原因。
❗ 常见误解:相同输入得到不同输出,是因为模型内部有”随机种子”在预填充阶段就起作用。
✅ 正确理解:随机性只发生在生成阶段的采样。预填充阶段没有任何随机操作。你可以通过设置temperature=0或使用贪婪解码来获得完全确定性的输出。
4.3 输出
- 将生成的Token序列通过解码器转换回文本,返回给用户。
4.4 思考型模型:思考过程在哪里?
❓ 常见问题:像 DeepSeek-R1、OpenAI o1 等模型,它们展示的”思考过程”属于流程中的哪个阶段?
✅ 简明答案:思考过程完全属于自回归生成阶段,不是独立阶段。
- 模型在生成最终答案之前,先通过逐Token采样生成一连串表示”思考”的文本(例如”首先,我们需要…”、”因为…所以…”)。
- 这些思考Token和普通输出一样,是利用KV缓存逐步生成的,每一步只计算新Token的K/V。
- 思考过程也具有随机性(如果使用采样)或确定性(如果
temperature=0)。
换句话说:思考过程 = 自回归生成早期产生的一批特殊Token,它既不是预填充,也不是嵌入,而是生成的一部分。
五、缓存命中:省钱的秘密
5.1 什么是KV缓存命中?
- 当你发送新请求时,API服务会检查你的提示词前缀是否与之前某个请求的前缀Token序列完全一致。若是,则直接复用已缓存的K/V,无需重新计算预填充。
- 计费上:缓存命中的输入Token价格通常仅为未命中的10%~20%。
5.2 命中的硬性条件
- 前缀必须从第一个Token开始连续完全相同。哪怕多一个空格、一个换行,或改变顺序,都会导致缓存失效。
❗ 常见误解:只要提示词大致相同,比如改了一个字或加了一个空格,应该还能命中大部分缓存吧?
✅ 正确理解:绝对不能。缓存匹配是严格的字节/Token级别相等。一个空格、一个标点、甚至繁体与简体的差异,都会导致从差异位置开始后续全部无法命中。
示例:
- 请求A:
System: 你是个助手 User: 北京天气 - 请求B:
System: 你是个助手 User: 上海天气 - 命中
System: 你是个助手 User:这一连续前缀(前提是长度足够触发折扣)。
5.3 缓存为什么会失效?
- 时间过期(TTL):每个缓存块有存活时间,通常5~60分钟。每次命中间隔不超过TTL则持续有效,否则被清除。
- 空间淘汰(LRU):缓存容量有限,长期未命中的旧缓存会被新请求挤掉。
- 内容变更:任何前缀修改(哪怕是一个字、一个标点)都会使原缓存不可用。
5.4 最佳实践:静态在前,动态在后
- 错误的模式:
[动态][静态][动态]→ 第一个Token不同,后续静态完全无法命中。 - 正确的模式:将所有静态内容连续放在最开头,所有动态内容放在末尾。
❗ 常见误解:可以静态和动态交替放置,只要后面有连续静态就能命中那一段。
✅ 正确理解:不能。因为缓存匹配必须从第一个Token连续一致。一旦第一个位置是动态,整个前缀就断裂了,后面的任何静态都无法被命中。只有把所有静态放在最前面形成连续前缀才能缓存。
示例:
- ❌ 错误:
[用户问题] [系统提示词] [随机ID] - ✅ 正确:
[系统提示词] [历史对话] [当前用户问题]
黄金法则:不变的靠前放,多变的放末尾。
六、确定性与随机性总结
| 阶段 | 是否确定 | 原因 |
|---|---|---|
| 预填充 | 确定 | 无随机操作,纯矩阵运算 |
| 生成(贪心) | 确定 | 每次选最高概率Token |
| 生成(采样) | 随机 | 按概率分布随机抽取 |
若需要完全可复现的结果,可以设置随机种子或使用贪心解码。
结语
理解大模型内部的推理流程,不仅能帮你写出更高效的代码,还能在日常API调用中大幅降低成本。记住两条核心要点:
- 静态内容永远放在最前面,利用缓存命中节省80%+输入费用。
- 生成随机性源自采样,并非模型”出错”。