从零开始理解大模型(七):推理——你按下回车后的这一秒发生了什么

举报
AGENT魔方 发表于 2026/04/20 09:40:00 2026/04/20
【摘要】 本文是「从零开始理解大模型」系列第七篇,本系列共十篇,从 “下一个词预测” 逐步搭建完整的大模型心智模型,每篇均附带可运行代码。本篇聚焦推理过程,带你清晰了解从按下回车到模型输出文字这一秒内,大模型内部完整的运行逻辑。

专栏1.png

欢迎阅读「从零开始理解大模型」系列 —— 十篇文章,从"下一个词预测"到完整的大模型心智模型。每篇配可运行代码。

* 本系列配套运行代码,可在公众号后台回复“大模型”完整获取。

作者:十一

你在 ChatGPT 里输入一个问题,按下回车。等了几秒后,第一个字蹦出来。然后后面的字一个接一个,越来越顺畅地涌出来。

为什么第一个字等得久?后面为什么快了?长文本回复为什么特别慢?

前六篇讲了大模型"是什么"和"怎么训练"。这篇讲推理——训练好的模型,收到你的问题后,实际跑了哪些步骤来生成回答。


一、先说结论

1.png

句话版本:推理 = Prefill(一口气处理全部输入)+ Decode(一个一个蹦出输出),KV Cache 让 Decode 阶段不用重复计算。


二、推理的两个阶段

假设你输入了 "法国的首都是哪里?",模型要生成 "法国的首都是巴黎。"

阶段一:Prefill(预填充)
┌──────────────────────────────────────────┐
│ 输入: "法国的首都是哪里?"(10 个 token)  │
│                                          │
│ 10 个 token 一口气送入模型                 │
│ 跑完 32 层 Transformer                    │
│ 得到概率分布,选出第一个输出 token           │
│                                          │
│ 耗时:几百 ms(时间主要花在这)             │
└──────────────────────────────────────────┘
                    │
                    ▼
阶段二:Decode(逐个生成)
┌──────────────────────────────────────────┐
│ Step 1: 送入 "法" → 模型计算 → 输出 "国"  │
│ Step 2: 送入 "国" → 模型计算 → 输出 "的"  │
│ Step 3: 送入 "的" → 模型计算 → 输出 "首"  │
│ ...                                      │
│ Step 8: 送入 "。" → 模型计算 → 输出 <结束> │
│                                          │
│ 每步耗时:几十 ms                          │
└──────────────────────────────────────────┘

Prefill 为什么慢? 因为要一次性处理所有输入 token。输入有 1000 个 token,Attention 的计算量就是 1000 × 1000 = 100 万次(第四篇讲的 n² 复杂度)。32 层全要跑一遍,跑不了捷径。

Decode 为什么快? 因为每次只处理一个新 token。但这里有个问题——第四篇说过,Attention 要让每个词和所有词算相关度。新蹦出来的第 11 个 token,不也得和前面 10 个 token 做 Attention 吗?

的确要做。但前 10 个 token 的 K 和 V 在 Prefill 阶段已经算过了,不需要重算——把它们缓存起来就行。

这就是 KV Cache


三、KV Cache——推理阶段最关键的优化

3.1 先回忆 Attention 公式

第四篇的核心公式:

Attention(Q, K, V) = softmax(Q · Kᵀ / √d) · V

三个角色

1.png

K 和 V 一旦算出来就不会变。唯一每次不同的是新 token 的 Q。

所以 KV Cache 的思路很直接:把历史 token 的 K 和 V 存起来,新 token 只算自己的 Q,然后拿 Q 去查已有的 KV。

3.2 有没有 Cache,差别有多大

没有 Cache:

生成第  1 个 token: 重算  10 个 token 的 K/V → 计算量 ∝ 10
生成第  2 个 token: 重算  11 个 token 的 K/V → 计算量 ∝ 11
...
生成第100 个 token: 重算 110 个 token 的 K/V → 计算量 ∝ 110

前面的 token 被反复算了几十上百遍,纯浪费。

有 Cache:

Prefill:  处理 10 个 token,缓存它们的 K 和 V
Decode 1: 只算 1 个新 token 的 Q/K/V,用 Q 查缓存的 K/V,把新 K/V 追加到缓存
Decode 2: 同上,还是只算 1 个
...
Decode 100: 还是只算 1 个

每步恒定只处理 1 个 token,不管已经生成了多长。

 inference.py[1] 可以实测对比。核心代码只差一个参数:

# 无 Cache:每次送入全部 token
outputs = model(all_tokens)

# 有 Cache:只送新 token + 传入缓存
outputs = model(new_token, past_key_values=kv_cache, use_cache=True)
kv_cache = outputs.past_key_values   # 更新缓存

实测效果:

无 Cache: Step 1 = 15ms, Step 20 = 28ms, Step 30 = 35ms  ← 越来越慢
有 Cache: Step 1 = 18ms (Prefill), Step 2~30 = 4ms       ← Decode 恒定

这就是你在 ChatGPT 里感受到的"第一个字慢、后面快"的技术原因。

完整对比代码见附件 inference.py[1]

* 本系列配套运行代码,可在公众号后台回复“大模型”完整获取。

3.3 KV Cache 的代价:吃显存

Cache 用空间换时间。要存多少?以 LLaMA 7B 为例:

每个 token 的 KV Cache 大小
= 层数 × 头数 × 每头维度 × 2(K和V) × 2字节(float16)
= 32 × 32 × 128 × 2 × 2
= 524,288 字节 ≈ 0.5 MB / token

如果上下文有 4096 个 token:

KV Cache = 0.5 MB × 4096 = 2 GB

模型参数本身 14 GB,KV Cache 再加 2 GB。如果上下文扩到 128K token,KV Cache 就要 64 GB——可能比模型本身还大。

这就是为什么长上下文对显存要求那么高:不光模型要占地方,KV Cache 也要占,而且和上下文长度成正比。


四、采样策略——模型怎么"选词"

模型输出的是 50257 个概率。怎么从中选一个 token?不同的选法,直接影响回答的风格。

4.1 Greedy(贪心)

永远选概率最高的。输出稳定、确定,但容易重复、无聊。

4.2 Temperature

第一篇演示过的参数。原理很简单——在 Softmax 之前,先把 logits 除以一个数:

P(token_i) = softmax(logit_i / T)

1.png

小了,模型更"保守";T 大了,模型更"奔放"。

4.3 Top-k

只从概率最高的 k 个 token 里采样,其他全部屏蔽。

问题是 k 是固定的。模型很确定的时候(概率集中在 2-3 个词),k=50 就太松;模型不确定的时候(概率分散),k=50 又太紧。

4.4 Top-p(Nucleus Sampling)

更聪明的做法:从概率最高的 token 开始往下累加,加到总和超过 p(比如 0.9)就停,只在这些 token 里采样。

概率排序:  0.30  0.20  0.15  0.10  0.08  0.05  0.04 ...
累加:      0.30  0.50  0.65  0.75  0.83  0.88  0.92 ← 超过 0.9,到这截断

Top-p 是自适应的——模型确定时只选几个词,不确定时自动选更多。实际使用中 Top-p = 0.9 ~ 0.95 最常见。

4.5 实际用法:组合起来

API 调用时通常同时设多个参数:

logits = logits / temperature           # 先调 Temperature
logits = top_p_filter(logits, p=0.9)    # 再做 Top-p 过滤
probs = softmax(logits)                 # 转成概率
next_token = sample(probs)              # 按概率随机选一个


五、批处理——同时服务几百万人

你用 ChatGPT 的时候不是一个人在用。同一秒可能有几百万请求。GPU 一次只服务一个人太浪费了。

解决办法:把多个用户的请求打包成一个 batch,一起送入模型。GPU 擅长并行计算,处理 8 个请求的时间几乎和 1 个一样。

但不同用户的生成长度不同——有人 5 步就说完了,有人要说 200 步。传统做法要等最慢的那个结束才能处理下一批。

Continuous Batching(连续批处理)解决了这个问题:谁先说完谁先走,腾出来的位置立刻给新请求。不用等。

所以当你觉得 ChatGPT"变慢了",通常不是你的问题太难,而是太多人在用,GPU 被挤满了,你在排队。


六、推理优化:让模型跑得更快

KV Cache 只是起点。生产环境还有更多优化。

6.1 量化——用更少的位数存参数

模型参数默认 float16(每个数字 2 字节)。量化把精度降到 int8(1 字节)甚至 int4(0.5 字节):

LLaMA 7B 显存占用:
  float16:  14 GB
  int8:      7 GB
  int4:    3.5 GB   ← 一张消费级显卡就能跑

精度会有一点点损失,但通常可以接受。你能在自己电脑上跑开源大模型,靠的就是量化。

6.2 和 Agent 的关系

Agent 场景的推理成本特别高。一个典型的 Agent 任务:

用户输入 → LLM 推理(决定用什么工具)
         → 工具执行
         → LLM 推理(分析工具结果)
         → 工具执行
         → LLM 推理(生成最终回答)

每次 "LLM 推理" 都是一轮完整的 Prefill + Decode。一个 Agent 任务可能调 5-10 次 LLM,每次上下文窗口里塞着之前所有的消息历史和工具返回——Prefill 越来越重。

这就是为什么 Agent 场景格外在意推理效率——KV Cache、上下文压缩、用小模型做简单决策,都是实际工程中的重要优化。


七、完整的推理时间线

把所有概念串成你按下回车后的一条时间线:

你按下回车
    │
    ▼ 输入发送到服务器
    │
    ▼ 分词:文字 → token IDs(第二篇,几乎不花时间)
    │
    ▼ ─── Prefill ───
    │  所有输入 token 一口气送入模型
    │  跑完 32 层 Transformer
    │  算出所有 token 的 K/V,存入 KV Cache
    │  得到概率分布,选出第一个输出 token
    │  耗时:几百 ms
    │
    ▼ ─── 第一个字出现 ───
    │
    ▼ ─── Decode(循环)───
    │  只送入上一步的新 token
    │  用新 Q 查 KV Cache 做 Attention(不重算旧 token)
    │  新 token 的 K/V 追加到 Cache
    │  Temperature + Top-p 采样,选出下一个 token
    │  耗时:每步几十 ms
    │  重复直到输出 <结束> 或达到最大长度
    │
    ▼ ─── 回答完成 ───
    │
    ▼ token IDs → 文字(反分词,几乎不花时间)
    │
    ▼ 显示在你屏幕上


八、结语

推理看着简单——字一个一个蹦出来而已。但后面的工程优化决定了你等多久、花多少钱。

KV Cache 是核心中的核心:把 Decode 从"每步重算全部"变成"每步只算一个"。没有它,大模型推理在工程上根本不可用。

推理的本质矛盾:模型越大越聪明,但也越慢越贵。KV Cache、量化、批处理——所有优化都在"聪明"和"快速"之间找平衡。

下一篇,我们讲上下文窗口——为什么模型不能记住无限长的文本?128K 的窗口到底什么意思?位置编码怎么让模型分得清第 1 个词和第 10 万个词?


本文配套代码:inference.py[1](有无 KV Cache 的推理速度实测对比)。需要 Python 3.8+、transformers、torch。



容器模仿.png

扫码回复“大模型

获取本系列文章完整配套代码


「从零开始理解大模型」是「从零开始理解 Agent」的姊妹系列。Agent 系列讲"四肢",本系列讲"大脑"。建议对照阅读 专栏入口

相关链接

[1] inference.py: https://github.com/GitHubxsy/nanoAgent/blob/claude/organize-teaching-materials-4hnRP/llm-from-scratch/inference.py

   今日推荐  

关注AGENT魔方公众号,回复大模型

领取「从零开始理解大模型」实操配套代码

加速入门和掌握Agent:

0大模型配图.jpg

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。