预训练、RLHF 与采样(KV Cache)
第 3 章拼出了完整的架构,但一台随机初始化的模型只会输出乱码——把它变成能对话、能推理、能调用工具的助手,要经过一条两级流水线:预训练(学知识)+ 后训练(学做人)。这是模型的"前半生"。"后半生"则是每次你按下回车后…
第 3 章拼出了完整的架构,但一台随机初始化的模型只会输出乱码——把它变成能对话、能推理、能调用工具的助手,要经过一条两级流水线:预训练(学知识)+ 后训练(学做人)。这是模型的"前半生"。"后半生"则是每次你按下回车后发生的事:参数冻结,进入推理阶段,模型一个 token 一个 token 地把答案"吐"出来。本章把这两段一口气讲完——训练侧的预训练、SFT、RLHF/DPO、思维链、幻觉,推理侧的自回归循环、采样策略(温度到底在调什么)、K-V 缓存(为什么有"首 token 延迟"、为什么响应是逐字蹦出来的)。理解了这条完整链路,你就能解释日常开发中的一堆现象:为什么模型会编造事实、为什么换个问法结果天差地别、为什么"思维链"能提升效果。这是第一章「它是什么」的收官——原理的最后一块拼图,从下一章起,我们正式动手。
1. 预训练:读遍互联网
预训练的目标函数在第 1 章已经剧透过:预测下一个词。工程规模是这样的:
| 项 | 典型规模 |
|---|---|
| 数据 | 数万亿 token:网页、书籍、代码、论文 |
| 算力 | 数千张 GPU 跑数月 |
| 方法 | 第 2 章的梯度下降(工业级优化器 + 稳定技巧) |
训练完成的产物叫基座模型(Base Model)。它的行为很"原始":你输入"中国的首都是",它接得很准;但你问"中国的首都",它可能接着补全"是哪个国家的问题吗?"——因为在语料里,这更像一个对话的开头。
基座模型的行为模式 = 互联网文本的续写统计
关键认知:预训练耗资巨大,但只做一次。 你每天用的各种模型,绝大多数共享少数几个开源基座(Qwen、Llama、DeepSeek 等)——差异主要来自下一步的后训练。这就是为什么"同一个基座的不同产品,性格迥异"。
一个常被问到的问题:预训练数据截止于某个时间点(knowledge cutoff),之后的事模型一无所知。模型没有"现在"的概念——这是 Agent 必须给模型配搜索工具的根本原因(第 9 章实战会用到)。
2. 后训练:从补全机器到助手
后训练(Post-training)让模型学会两件新事情:听懂指令 和 符合人类偏好。主流流程是三步走:
基座模型 ──SFT──→ 会聊天 ──RLHF/DPO──→ 乖巧有用 ──(可选)专项──→ Agent/推理模型
2.1 SFT:监督微调,照着示范学
SFT(Supervised Fine-Tuning)就是监督学习(第 1 章三大范式的第一员):
- 数据:人工编写/审核的
(指令, 理想回答)对,量级通常几万~百万条 - 训练:和预训练完全相同的"预测下一个词",但只在"理想回答"部分计算损失
训练样本示例:
用户: 总结这段文章
助手: <人类写的标准总结> ← 只在这里算损失,让模型学会扮演"助手"
效果立竿见影:模型学会了"问-答"的行为模式。SFT 后的模型已经可以对话,但答案质量参差——因为"好的标准"很难用有限样本覆盖。
2.2 RLHF:从"模仿"到"偏好对齐"
RLHF(Reinforcement Learning from Human Feedback)用上了第三大范式——强化学习(第 1 章):
- 让模型对同一问题生成多个回答
- 人类标注员对回答排序(排序比打分容易,也更可靠)
- 用排序数据训练一个奖励模型(Reward Model),学会给回答打分
- 用强化学习(如 PPO)微调语言模型,使其输出向奖励模型的高分方向靠拢
直觉上,SFT 是"临摹字帖",RLHF 是"教练纠偏"。它解决的是"什么是好回答"中难以明说的部分:语气、分寸、诚实度、安全性。
近年的轻量替代是 DPO(直接偏好优化):跳过奖励模型和强化学习的复杂工程,直接从"偏好对"数据优化,效果接近、实现简单。还有基于 AI 反馈的 RLAIF/宪法 AI(用另一个模型代替人类标注员做排序),把对齐成本进一步压低。
2.3 对齐税
对齐是有代价的:过度对齐的模型会变得过于谨慎(拒答边缘问题)、过于啰嗦(讨好性)、风格趋同(多样性下降),这被称为对齐税(alignment tax)。你在开发中遇到的"模型太啰嗦""怎么问都说不知道"等现象,很多时候不是能力问题,而是对齐策略的副作用——这也是为什么要发展系统提示词(第 5 章)这类"运行时调控"手段。
3. 思维链:后训练教会模型"想再答"
思维链(Chain-of-Thought,CoT)是后训练阶段最重要的能力注入之一,值得单独一节。
现象(2022 年发现):在提示词里加一句"让我们一步步思考",复杂推理任务的正确率显著上升。
原理:模型是逐 token 生成的(本章第 6 节详解),每生成一个 token,它都成为后续 token 的上下文。直接输出答案时,模型只有一次"开枪"的机会;而先写推理过程,相当于把中间结论写进上下文,后续步骤可以基于这些"脚手架"继续思考——用生成换计算。
无 CoT: 问题 → 答案 (一步到位,容易错)
有 CoT: 问题 → 推理过程 → 答案 (中间结论成为后续的输入)
这也解释了两个日常现象:
- 为什么推理模型(o1/R1 类)喜欢长篇分析——它们在后训练中被大量强化了"先充分思考"的模式
- 为什么让模型"别思考直接答"复杂问题质量会下降——你亲手拆掉了它的脚手架
Agent 开发与 CoT 的关系比想象中深:ReAct(第 8 章)就是把思维链中的"思考"替换成"思考 + 行动(调用工具)",让推理过程可以接入真实世界的信息。
4. 一个模型的一生:训练流水线全景
万亿 token 语料 万人写的示范对 人类偏好排序
│ │ │
▼ ▼ ▼
┌─────────┐ SFT ┌──────────┐ RLHF/DPO ┌──────────┐
│ 随机初始化 │ ───────→ │ 基座模型 │ ────────→ │ 对话模型 │
└─────────┘ └──────────┘ └──────────┘
会续写乱码 会接话 有分寸地帮忙
│
专项后训练 ──┤── Agent 模型(工具调用强化)
└── 推理模型(长思维链强化)
你在 Ollama 里 pull 一个模型时看到的命名差异,对应的就是这条流水线的不同位置:qwen3 系列的 base 版(纯基座)和 instruct 版(后训练完成)行为完全不同。日常开发永远选 instruct/chat 版,base 版只用于继续训练或研究。
5. 幻觉:流水线的必然副产品
幻觉(hallucination,一本正经地编造事实)不是 bug,是训练方式的必然副产品:
- 预训练的目标是"预测下一个词",不是"预测真词"。语料本身就有错误、虚构、过时内容;语法通顺的错误答案和正确答案,在损失函数上可能只差一点
- 模型的全部倾向是生成统计上合理的文本。"合理"和"真实"在多数时候重合,但模型没有内在机制区分二者
- 对齐训练教会它"诚实",但没有教它"知道自己不知道"——校准(calibration)至今是开放难题
工程对策(Agent 视角,第 13、15 章还会深化):
| 手段 | 原理 |
|---|---|
| RAG / 搜索工具 | 把"回忆"变成"查阅",事实来源外置(第 9 章实战) |
| 要求引用来源 | 让输出可验证,倒逼生成时"有据" |
| 结构化输出(第 6 章) | 收窄输出空间,减少自由发挥 |
| 温度调低(本章第 8 节) | 降低采样随机性,倾向高概率答案 |
6. 自回归:一次生成一个 token
模型训练完毕、参数冻结,就进入推理(inference)阶段——每次你按下回车后发生的事。推理的核心是一个循环,称为自回归(autoregressive)生成:
上下文: "今天天气真"
│ ① 整段送入模型,对最后一个位置做预测
▼
概率: 好(0.60) 不错(0.20) 差(0.15) 凉(0.03) ...
│ ② 选出一个 token(怎么选?见第 8 节)
▼
上下文: "今天天气真好"
│ ③ 把新 token 拼回上下文,回到 ①
▼
上下文: "今天天气真好," → ...
(循环,直到选出特殊的结束符 <eos> 或达到长度上限)
两个重要推论:
- 输出长度不可精确预知:结束与否由模型自己在循环中决定。这就是 max_tokens 参数的本质——给循环设个上限
- 每一步都有完整上下文参与计算:第 N 个 token 的生成依赖前 N-1 个。这个特性带来了"前缀重复计算"问题,下一节的 K-V 缓存就是它的解药
用伪代码总结整个生成过程(第 5 章会换成真实 API):
def generate(model, prompt_ids, max_tokens=512):
ids = list(prompt_ids)
for _ in range(max_tokens):
logits = model(ids)[-1] # 只关心最后位置的词表分布
next_id = sample(logits) # ← 采样策略(第 8 节)
if next_id == EOS:
break
ids.append(next_id)
return ids
7. K-V 缓存:重复计算的大消除
7.1 问题:前缀的重复计算
朴素的实现有个严重浪费:生成第 1000 个 token 时,要把前 999 个 token 全部重新跑一遍前向传播(第 3 章的完整流水线)——而其中 998 个位置的计算结果和上一轮一模一样。
7.2 解药:缓存历史位置的 K、V
注意第 3 章的细节:注意力计算需要每个位置的 Q、K、V 三个向量。其中 Q 只属于当前最新位置(查询发自"当下"),而 K、V 是所有历史位置的属性。既然历史位置的 K、V 不会变,就把它们缓存下来:
生成第 N 个 token 时:
- 计算:只有第 N 个位置的完整前向(产出它的 Q/K/V)
- 读取:缓存里前 N-1 个位置的 K、V
- 更新:把第 N 个位置的 K、V 写入缓存
这就是 K-V Cache。效果:每步生成从 O(N²) 的重复计算降到 O(N),代价是显存里持续存放历史 K/V(这也是长上下文吃显存的另一个来源——上下文越长,缓存越大)。
7.3 推论:预填充与解码的两阶段节奏
由此推出两个你在开发中天天遇到的现象的原理:
- 首 token 延迟(TTFT):第一个 token 之前必须处理完整输入(这个阶段叫 prefill,预填充),输入越长等得越久。系统提示词一长,"开口"就慢
- 生成速度恒定:后续每个 token(decode,解码阶段)只需增量计算,通常稳定在几十 tokens/s
7.4 流式输出:不省总耗时,只改善体感
顺带解释流式输出(streaming):API 所谓的"流式",就是把 decode 循环每步的产物即时推给你,而不是攒齐了再发。流式不影响总耗时,只改善体感。
8. 采样策略:温度到底在调什么
模型每步输出的是整个词表上的概率分布,怎么从中选出下一个 token?这一步就是采样策略,也是 temperature、top_p 这些参数的属地。
8.1 贪心 vs 随机
最简单的两种极端:
- 贪心(greedy):永远选概率最高的。问题:输出确定但呆板,长文容易陷入重复循环("的的的")
- 纯随机:按概率抽签。问题:偶尔抽到离谱低概率 token,文章跑偏
生产实践在两者之间:先截断再重调再采样。
8.2 截断:Top-k 与 Top-p
- Top-k:只在概率最高的 k 个候选里抽(如 k=50)
- Top-p(核采样):从高到低累加概率,累计到 p(如 0.9)为止,只在这批里抽。比 top-k 聪明的地方:分布尖锐时候选少(谨慎),分布平缓时候选多(开放)
分布尖锐: 好(0.85) 差(0.1) ... → top-p=0.9 几乎只有"好"可选
分布平缓: 红(0.2) 橙(0.2) 黄(0.2) … → top-p=0.9 会保留一大串候选
8.3 温度:分布的锐度旋钮
温度直接改写概率分布——用 logits 除以 T 再 softmax:
$$ P_i = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}} $$
直觉:T 调低 → 分布更尖 → 更确定、更保守;T 调高 → 分布更平 → 更随机、更有创造性。
用 numpy 直接感受:
import numpy as np
logits = np.array([5.0, 3.0, 1.0, 0.5, 0.1]) # 5 个候选的原始分
def softmax(z, T=1.0):
z = z / T
e = np.exp(z - z.max())
return e / e.sum()
for T in [0.2, 1.0, 3.0]:
print(f"T={T}: {np.round(softmax(logits, T), 3)}")
输出:
T=0.2: [1. 0. 0. 0. 0. ] ← 接近贪心:几乎必选第一名
T=1.0: [0.867 0.118 0.016 0.009 0.006] ← 原始分布
T=3.0: [0.558 0.205 0.075 0.055 0.045] ← 抹平:冷门候选也有机会
选型建议(Agent 开发直接可用):
| 场景 | temperature | 理由 |
|---|---|---|
| 代码生成、结构化输出 | 0 ~ 0.2 | 要确定性和格式正确 |
| 通用问答 | 0.3 ~ 0.7 | 平衡稳定与流畅 |
| 创意写作、头脑风暴 | 0.8 ~ 1.2 | 需要多样性 |
注意:温度为 0 也不等于绝对确定(浮点并行计算等原因,少数 API 仍有微小随机性)——所以别把"temperature=0"当作"单元测试断言"的前提。
8.4 其他参数速览
- presence_penalty / frequency_penalty:压制已出现过的 token,缓解重复。生成"车轱辘话"时可以先拉高试试
- seed(部分 API 支持):固定采样随机源,配合低温提高可复现性
- stop 序列:遇到指定字符串提前终止生成(比如工具调用场景里检测到结束标记)
9. 上下文窗口:推理的资源边界
推理时所有内容共享一个预算——上下文窗口(以 token 计,第 1 章提过换算):
┌─────────────────── 上下文窗口(如 32K)───────────────────┐
│ 系统提示词 │ 对话历史 │ 工具定义 │ few-shot 示例 │ 当前问题 │ 生成空间 │
└──────────────────────────────────────────────────────┘
窗口对 Agent 开发是一等公民的约束:工具定义、历史轮次、检索回来的文档,全部在争抢这块空间;生成也发生在窗口内,超长输出会挤占输入。满了会怎样(截断或报错,各家实现不同)以及管理策略(滑动窗口、摘要压缩),第 6 章展开。
一个容易踩的坑:长上下文 ≠ 均匀注意力。研究发现模型对开头和结尾的内容最敏感,中段信息容易被忽略(lost in the middle 现象,第 3 章位置编码一节埋过伏笔)。实用对策:关键指令放开头或结尾,重要文档拆块后置最相关的在两端。
10. 成本:token 经济学
把推理的账算清楚(2026 年常见量级,具体看厂商):
| 计费项 | 说明 |
|---|---|
| 输入 token | 通常便宜;系统提示词+对话历史每轮重复计费 |
| 输出 token | 通常数倍于输入单价;decode 无法并行,生成慢是物理限制 |
| 缓存命中(prompt caching) | 相同前缀(如固定系统提示词)命中缓存可大幅打折,第 6 章详解 |
由此得出的三条省钱纪律(Agent 工程必修):
- 系统提示词能短则短——它每轮都在付费
- 历史别无脑全量携带——窗口和费用双杀,需要裁剪/摘要策略(第 13 章)
- 控制输出长度——输出单价更高,max_tokens 是成本护栏
11. 和 Agent 开发的接口
把本章内容收敛到三个可操作的认知:
- 指令遵循来自 SFT——模型"听话"的程度在出厂时已定型,系统提示词只能在边际上引导。指令越贴 mainstream 训练分布,越有效
- 知识截止于预训练——涉及实时信息、私有数据,必须外挂工具(搜索/RAG),别指望模型"知道"
- 生成即思考——想让模型答得好,往往不是"问得更狠",而是"给它生成中间过程的台阶"(先分析再结论、先列计划再执行)
本章小结
成长(模型怎么被造出来):
- 预训练 = 预测下一个词 × 万亿级语料 → 产出"续写机器"(基座)
- 后训练 = SFT(模仿示范)+ RLHF/DPO(对齐偏好)→ 产出"助手"
- CoT 利用"生成即上下文"的原理,用中间结论给自己搭脚手架
- 幻觉是训练目标的必然副产品,工程上靠工具外置与输出约束缓解
- base 版 vs instruct 版的命名差异对应流水线位置,开发永远用后者
开口(内容怎么被生成出来):
- 生成 = 自回归循环:预测 → 采样 → 拼接 → 再预测,直到
或上限 - K-V 缓存消除历史重复计算;prefill(吃输入)与 decode(吐 token)两阶段解释了 TTFT 与恒定生成速度
- 采样 = 截断(top-k/top-p)+ 重调(temperature)+ 抽签;低温确定保守,高温多样奔放
- 温度旋钮本质是分布锐度;T=0 也不保证绝对可复现
- 上下文窗口是共享预算:输入和生成在同一空间里此消彼长;中段信息易被忽略
- token 是唯一成本单位:短提示词、裁历史、限输出是三大省钱纪律
第一章「它是什么」完。从 token 与嵌入,到注意力架构,再到训练流水线与推理机制,你已经拥有了完整的 LLM 心智模型——足够看懂任何模型的官方文档和架构八卦。从下一章开始,我们进入第二章「怎么用好它」:先在本地把模型跑起来,然后一步步搭出你的第一个 Agent。
下一篇:05 · 本地模型与消息结构 —— 五分钟在本地跑起一个能对话的模型,顺便把 OpenAI SDK 这套"行业通用语法学"到手。