大语言模型理论与实践
对应:培养方案 专业选修课 · 学期 6 · 2 学分 · 32 学时(16 课堂 + 16 实践)
标记↗:与信息科学学院人工智能专业同堂共享课,同一间教室、同一份作业、同一套考试。
标记*:含创新创业思维。这门课是专业选修里离产业最近的一门,*主要落在
第 6、7 两节(提示工程、高效微调)讲的是今天真实公司在做的事。前置:这页假设你已经读过同目录的《深度学习原理与实践》和《自然语言处理》。
Transformer、注意力、softmax 的推导在《深度学习》§8;Word2Vec、BERT、困惑度、预训练+微调
范式的推导在《自然语言处理》§2、§3、§6。本页一个都不重复推导,只引用。
如果那两页没读,先回去读,本页是它们的下游。这份笔记怎么用:所有代码都用
numpy/scipy从零手写并实际跑过,
复制到终端就能跑。搜索>>> 实跑输出能看到我跑出来的真实结果。
运行环境:~/miniconda3/bin/python(numpy 2.5.3 / scipy 1.18.1)。
不需要 GPU,不需要联网,不需要 API key。⚠️ 时效性声明(这门课的特殊风险):这是所有专业课里变化最快的一门。
下面涉及具体模型版本、产品能力、"最新进展"的地方,凡是我没有把握的,
都写成了"截至本书写作时"或直接标注不确定。不要把本页任何一句关于
"现在最强的模型能做什么"的话当成定论。但原理部分(规模定律的形式、
RLHF 的三步、LoRA 的分解、解码的数学)是稳定的,那些可以放心当知识。相关笔记:认知侧请对照
../专业课/教材/笔记-心理语言学核心.html(§6 大模型 vs 人类语言加工)
与../专业课/教材/笔记-语言认知神经科学.html。
1 导论:这门课在研究什么(先读这个)
1.1 这门课不是什么
误解一:"大模型就是'更大的 BERT'。"
这是本专业学生最容易有的误会,而且它是唯一一个本页要花一整节去反驳的(§1)。
BERT 和 GPT 都是 Transformer,都靠预训练,都在 2018–2020 年出现,所以看起来像同一条
路上的两个刻度。但它们不是同一条路。 BERT 用的还是"预训练 + 每个任务微调一个头"
的老范式;GPT-3 之后,微调这一步在很多任务上直接消失了:任务写在提示里,
模型的权重一动不动。这不是"更大",这是换了工作方式。
误解二:"大模型的能力是设计出来的。"
不是。GPT-3 的架构和 GPT-2 几乎一样,训练目标也一样(预测下一个词)。
从 GPT-2 到 GPT-3 的主要改动就是变大,参数从 15 亿到 1750 亿,数据从 40GB 到 3000 亿 token。
而 in-context learning、思维链这些能力,没有一条是专门设计进去的。
它们是在规模到达某个量级后自己冒出来的。这个"冒出来"(emergence)本身
(以及它是否真的是"冒出来",见 §5)是这门课最有意思的问题。
误解三:"大模型的困难在于训练,训练完就没事了。"
训练只是其中一段。今天的成本大头在推理(每次你打字,背后都在跑一次完整的前向)。
所以 §7 的高效微调(LoRA、量化)和 §8 的解码策略,在工业界的权重不比训练低。
而且"模型说了什么"不只由权重决定,同一份权重,换解码参数,输出可以完全不同(§8 会实跑给你看)。
误解四:"这是一门讲'AI 会不会取代人'的课。"
不是。这门课讲机器:它的损失函数长什么样、为什么这么训、在什么条件下会崩。
"会不会取代"是另一门课的事(《心理语言学》《认知神经科学》和哲学课都在谈)。
本页只在 §9、§10 各留一节,把能验证的部分说清楚,剩下的一律不下结论。
这门课的判据是"这个说法有没有可复现的实验支撑",不是"这个说法听起来深不深刻"。
1.2 它在专业里的位置
认知科学与技术 · 专业选修
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
它依赖谁 【本课】 它被谁依赖
──────────── 大语言模型理论与实践 (学期 6) ──────────────
· 深度学习原理与实践 2 学分 / 32 学时 · 智能体原理与设计
(学期 5,注意力/ 16 课堂 + 16 实践 (把大模型当"大脑")
Transformer 推导) ↗ 与信科院人工智能专业共享 · 认知神经科学
· 自然语言处理 * 含创新创业思维 (语言脑机制建模)
(学期 5,BERT/预训练 │ · 心理语言学
+微调/困惑度) │ (模型-人脑对齐)
· 概率统计(KL/信息论) │
· 人工智能原理(学期 3) ▼
┌────────────────────────────────┐
│ 一条主线:规模改变了什么 │
└────────────────────────────────┘
│
┌─────────────────┼──────────────────┬───────────────────┐
▼ ▼ ▼ ▼
为什么变大有用 变大之后 怎么用/怎么改 怎么把话吐出来
规模定律 §2 冒出新能力 §1,§5 提示 §6 / 微调 §7 解码 §8
(Kaplan/Chinchilla) (ICL/CoT) (RLHF §4) (T/k/top-p)
和隔壁几门课的分工:
| 课程 | 它负责 | 和本课的分界 |
|---|---|---|
| 深度学习原理与实践(学期 5) | 组件级原理:反向传播、注意力、Transformer | 那里拆开每个零件;这里把整个模型当黑箱,问"规模给它带来了什么" |
| 自然语言处理(学期 5) | 任务、表示、评测(Word2Vec/BERT/BLEU) | 那里到 BERT 为止;本课从 BERT 之后讲起,重点是质变而非量变 |
| 本课(学期 6) | 十亿参数级模型的行为与工程 | 讲规模定律、RLHF、涌现争议、LoRA、解码,这些在 NLP 那页都没出现 |
| 人工智能原理(学期 3) | 搜索、逻辑、agent 框架 | 那门讲"怎么决策",不涉及"怎么从数据里学参数" |
| 心理语言学 / 认知神经科学 | 人怎么做语言加工 | 那两门研究人;本课研究机器,但 §10 是两者的接口 |
本课是认知科学专业里唯一一门直接讨论"当前最强 AI 系统"的课。
它的价值不在教你训一个大模型(32 学时、没有 GPU,做不到),
而在教你看懂关于大模型的每一句主张是怎么被验证或没被验证的,
这个能力在你读研、看新闻、写论文时天天要用。
1.3 真正要教你的能力
具体、可检验,一共四条:
- 能读懂一条规模定律并算它的推论。给你 $C=6ND$ 和 $D=20N$,
你能推出 $N^*=\sqrt{C/120}$,并算出某个算力预算下"该训多大模型、喂多少数据"。
检验方式:§2 的推导 + §11 第 1、2 题。 - 能手写奖励模型的目标函数并对它求导。 Bradley-Terry 形式
$\mathcal{L}=-\log\sigma(r_w-r_l)$ 的梯度你要能不查资料写出来,
并且知道它和逻辑回归是同一个东西。检验方式:§4.3 + §11 第 3 题。 - 能分辨"能力涌现"和"指标涌现"。看到一个"某能力在 X 亿参数突然出现"的图,
你能追问:用的是哪个指标?这个指标是连续的还是跳变的?测试集多大?
检验方式:§5 的实跑 + §11 第 5 题。 - 能算清一次微调/推理的参数与显存账。给你一个矩阵的形状和 LoRA 的秩,
你能说出可训练参数降了几倍、显存大概省多少;给你一个概率分布和 $T$/$k$/$p$,
你能手算解码后的分布。检验方式:§7、§8 + §11 第 6、7 题。
1.4 这份笔记的组织逻辑 / 怎么读
组织的原则是:先讲清"规模改变了什么"(§1–§2),再讲"人怎么把它变听话"(§3–§4),
然后处理"这中间有多少是幻觉"(§5),最后是全部工程手段(§6–§8),
末尾才是局限与和你专业的关系。
§1 大模型不是更大的 BERT ★★ ▸ 先破掉最大的误会,建立坐标系
│ (含 in-context learning 实跑)
▼
§2 规模定律 ★★★ ▸ Kaplan 2020 vs Chinchilla 2022
│ "参数、数据、算力按幂律下降" + 实跑
▼
§3 GPT 系列的技术路线 ▸ GPT-1 → 2 → 3 → InstructGPT → 4
│ 每一步解决了什么问题
▼
§4 RLHF ★★★ ▸ 全篇最硬的一节。三步逐项讲,
│ 奖励模型目标函数 + PPO 的 KL 罚项 + 实跑
▼
§5 涌现能力与它的争议 ★★★ ▸ 用实跑说明"指标决定结论"
│ (和《实验方法论》同构)
▼
§6 提示工程与思维链 ▸ few-shot / CoT / self-consistency
│ (含真实 API 调用的写法,本页不跑)
▼
§7 高效微调:LoRA 与量化 ★★ ▸ 低秩分解推导 + numpy 实跑 + INT4 实跑
│
▼
§8 推理与解码 ★★ ▸ temperature / top-k / top-p 的数学 + 实跑
│
▼
§9 局限与风险 -> §10 和你专业的关系 -> §11 习题 -> §12 出处索引
阅读顺序建议:
- 第一遍(别停):只读每节的"直觉"部分和代码后面的输出。跳过所有 $\sum$、$\nabla$、KL。
目标是建立手感:这个技术在解决什么问题、它的输入输出是什么。 - 第二遍(拿纸笔):死磕 §2(规模定律的推导)、§4.3(奖励模型的梯度)、
§5(涌现的机制)。 - 第三遍(动手):把 §5 的涌现代码抄进终端,改
L_list(把 L 从 8 改成 3 或 20),
看"涌现"的陡峭度怎么变。再把 §8 的logits改成你自己造的分布,
看四张解码表怎么跟着变。改一个数看输出,比读十遍公式有用。 - 考试前:只看 §11 习题、每节的"边界与批评"、§9 局限清单。
一页速查:
| 符号 | 读法 | 含义 | 典型值 |
|---|---|---|---|
| $N$ | 参数量 | 模型有多少个非嵌入参数 | $10^8$ ~ $10^{12}$ |
| $D$ | 数据量 | 训练用了多少个 token | $10^{10}$ ~ $10^{13}$ |
| $C$ | 算力 | 训练总浮点运算数,$C\approx 6ND$ | $10^{21}$ ~ $10^{25}$ FLOPs |
| $D/N$ | 数据参数比 | 每个参数分到多少 token | Chinchilla 最优 ≈ 20 |
| $r$ | LoRA 的秩 | 低秩分解保留几维 | 4 ~ 64 |
| $\beta$ | KL 罚系数 | RLHF 里"允许偏离参考模型多少" | $0.01$ ~ $0.1$ |
| $\alpha$ | LoRA 缩放常数 | $\Delta W$ 前的系数 $=\alpha/r$ | 常取 $r$ 或 $2r$ |
| $T$ | 温度 | 解码时 logit 的除数 | 0 ~ 2 |
| $p$ | 核采样阈值 | top-p 保留的累计概率 | 0.9 ~ 0.95 |
2 大模型不是"更大的 BERT"
这一节立坐标系:BERT 和 GPT 是 Transformer 的两条分岔。
(注意力、Transformer 的机制推导不在这里,见《深度学习》§8。这里只讲范式的差别。)
2.1 两条分岔:编码器 vs 解码器
BERT 和 GPT 都出自 Transformer,但取了 Transformer 的两个不同部分:
| BERT(2018) | GPT 系列(2018–) | |
|---|---|---|
| 用了 Transformer 的 | 编码器(双向) | 解码器(因果、单向) |
| 注意力能看到 | 左右都看 | 只能看左边(不能偷看答案) |
| 预训练目标 | 掩码语言模型:挖掉 15% 的词让模型填 | 自回归:预测下一个词 |
| 擅长 | 理解(分类、抽取、匹配) | 生成(续写、对话、推理) |
| 用它的方式 | 预训练 + 每个任务微调一个头 | 预训练(+ 对齐),然后写提示 |
关键区别不在"大小",在"目标函数"。掩码语言模型的训练目标是
"给定双向上下文,还原被挖掉的词",这个目标天生不含生成:
模型学的是"填空",它没有"往下写"的接口。而自回归目标
$p(w_t \mid w_1,\dots,w_{t-1})$ 本身就是生成:
模型每一步都在输出一个关于下一个词的概率分布,采样它就得到文本。
规模对比(这些数字来自各自论文):
| 模型 | 年份 | 参数量 | 训练数据 | 上下文长度 |
|---|---|---|---|---|
| BERT-base | 2018 | 1.1 亿 | 33 亿词(BooksCorpus + 英文维基) | 512 |
| BERT-large | 2018 | 3.4 亿 | 同上 | 512 |
| GPT-1 | 2018 | 1.17 亿 | BooksCorpus(约 7000 本书) | 512 |
| GPT-2 | 2019 | 15 亿 | WebText,40GB | 1024 |
| GPT-3 | 2020 | 1750 亿 | 3000 亿 token | 2048 |
| GPT-4 | 2023 | 未公开 | 未公开 | 未公开 |
(GPT-4 Technical Report, arXiv:2303.08774,明确拒绝披露架构、参数量、算力和数据,
只报了评测结果。所以上表后两格的"未公开"是报告里真的没写,不是我查不到。
这是 2023 年之后的一个行业转向:前沿模型不再公开技术细节。)
从 BERT-large 到 GPT-3,参数差 约 500 倍。但真正改变游戏规则的不是这 500 倍,
是这个规模上冒出来的新用法。
2.2 质变在哪:从"微调"到"不动权重"
把两代工作流的差别并排画出来:
【BERT 工作流】每个任务都要改权重
预训练 BERT ──► 拿一批标注数据 ──► 反向传播改全部 1.1 亿参数 ──► 冻结 ──► 上线
(每个新任务都要重来一遍,都要标注数据)
【GPT-3 工作流】权重全程不动
预训练 GPT-3 ──► 把 3 个例子写进提示 ──► 一次前向 ──► 输出
(没有反向传播,没有新参数,没有标注数据)
↑
这 3 个例子就是"学习"的全部发生地
第二种叫 in-context learning(上下文学习,ICL)。
GPT-3 论文(Brown et al., 2020, NeurIPS, arXiv:2005.14165)把它按提示里的
例子数量分成三档:
| 叫法 | 提示里给几个例子 | 权重更新 |
|---|---|---|
| zero-shot | 0 个,只有任务描述 | 无 |
| one-shot | 1 个 | 无 |
| few-shot | 10 ~ 100 个 | 无 |
"无"这个字是重点:这三档里没有任何梯度更新。
模型的参数在推理阶段是常数。"学习"这件事被搬进了前向计算里。
2.3 上下文学习到底学到了什么:一次实跑
ICL 听起来像魔法。但至少在一类干净的情形下,它不是什么魔法,就是前向过程里解了一个最小二乘。
设定:真实任务是一个线性映射 $y = \mathbf{w}^\top \mathbf{x}$($\mathbf{x}\in\mathbb{R}^5$)。
上下文里给 $k$ 个 $(\mathbf{x}_i, y_i)$ 例子,然后问新的 $\mathbf{x}_q$ 该输出什么。
模型参数一个都没动,我们只是在前向里对上下文做岭回归:
$$\hat{\mathbf{w}} = (X^\top X + \lambda I)^{-1} X^\top \mathbf{y}$$
import numpy as np
np.set_printoptions(precision=4, suppress=True)
rng = np.random.default_rng(7)
DIM = 5
w_true = rng.normal(size=DIM) # 真实任务
Xte = rng.normal(size=(4000, DIM)); yte = Xte @ w_true
LAM = 0.3
def mk(k):
X = rng.normal(size=(k, DIM))
return X, X @ w_true + rng.normal(scale=0.3, size=k)
def ls(X, y): # 前向里"读"出任务
return np.linalg.solve(X.T@X + LAM*np.eye(DIM), X.T@y)
base = (yte**2).mean() # 没有上下文时只能输出 0
print(" k 平均测试 MSE 比基线降了")
print(" 0 %9.5f --" % base)
for k in [1, 2, 3, 5, 8, 16, 32, 64, 128]:
m = np.mean([((Xte@ls(*mk(k)) - yte)**2).mean() for _ in range(300)])
print(" %3d %9.5f %5.1f%%" % (k, m, (1-m/base)*100))
def gd(X, y, T): # 换成梯度下降
w = np.zeros(DIM); eta = 1.0/np.linalg.norm(X, 2)**2
for _ in range(T):
w = w - eta*(X.T@(X@w - y))
return w
k = 20
X, y = mk(k)
w_ls = np.linalg.lstsq(X, y, rcond=None)[0]
print(" 步数 T 测试 MSE ||w_T - w_最小二乘||")
for T in [1, 2, 5, 10, 30, 100, 2000]:
w = gd(X, y, T)
print(" %4d %9.5f %9.5f" % (T, ((Xte@w-yte)**2).mean(), np.linalg.norm(w-w_ls)))
eta = 1.0/np.linalg.norm(X, 2)**2
w1 = gd(X, y, 1)
print(" GD 一步的预测 前 5 个 =", (Xte@w1)[:5])
print(" 注意力形式的预测 前 5 个 =", (eta*(Xte@X.T)@y)[:5])
print(" 最大偏差 = %.2e" % np.abs(Xte@w1 - eta*(Xte@X.T)@y).max())
实跑输出
k 平均测试 MSE 比基线降了
0 1.08998 --
1 0.89599 17.8%
2 0.71932 34.0%
3 0.57373 47.4%
5 0.32023 70.6%
8 0.12382 88.6%
16 0.04252 96.1%
32 0.01736 98.4%
64 0.00802 99.3%
128 0.00370 99.7%
步数 T 测试 MSE ||w_T - w_最小二乘||
1 0.22448 0.46989
2 0.06836 0.24073
5 0.01293 0.07564
10 0.00482 0.02599
30 0.00251 0.00044
100 0.00248 0.00000
2000 0.00248 0.00000
GD 一步的预测 前 5 个 = [ 0.2715 0.5954 1.2868 -0.1913 -0.223 ]
注意力形式的预测 前 5 个 = [ 0.2715 0.5954 1.2868 -0.1913 -0.223 ]
最大偏差 = 4.44e-16
例子从 1 个加到 128 个,误差从 0.90 掉到 0.0037。整个过程中权重的梯度一次都没算过。
后半段把"闭式解"换成了"梯度下降"。梯度下降的迭代是
$$\mathbf{w}_0 = \mathbf{0},\qquad \mathbf{w}_{t+1} = \mathbf{w}_t - \eta \nabla \tfrac12\|X\mathbf{w}_t - \mathbf{y}\|^2$$
一步之后 $\mathbf{w}_1 = \eta X^\top \mathbf{y} = \eta \sum_i \mathbf{x}_i y_i$,
于是对新 query 的输出是
$$\mathbf{w}_1^\top \mathbf{x}_q = \eta \sum_i \underbrace{(\mathbf{x}_q^\top \mathbf{x}_i)}_{\text{注意力权重}} \underbrace{y_i}_{\text{value}}$$
右边正是《深度学习》§8.2 里那个缩放点积注意力的形式(只不过这里没除以 $\sqrt{d_k}$、
value 直接取上下文里的 $y_i$,不是学出来的)。也就是说,
一层线性注意力可以恰好实现"对上下文做一步梯度下降",
输出里那两行"GD 一步的预测"和"注意力形式的预测"逐位相同,
偏差 $4.44\times10^{-16}$ 就是浮点误差本身。
上下文学习至少在一类任务上,就是"在前向里做优化"。
边界声明(重要):上面只证明了"一层线性注意力可以实现一步梯度下降"。
这不等于"真实的 Transformer 一定在跑梯度下降"。
论文(von Oswald et al., 2022, arXiv:2212.07677)的立场是:
这是一个很好的假设,在简单回归任务上训练出来的 Transformer 学到的权重
确实和这个构造吻合;但真实大模型里还有 MLP、LayerNorm、softmax 注意力、几十层堆叠,
它到底在算什么,至今没有公认答案。别把上面这段当成"ICL 的原理已被破解"。
2.4 边界与批评
- ICL 的能力有强上界。上下文里放几十个例子,和拿这些例子做一次真正的微调,
效果通常还是微调更好,尤其当任务需要精细的模式、或例子成千上万时。
ICL 赢在零成本、即时、可切换,不是赢在效果上限。 - ICL 极不稳定。例子的顺序、标签的分布、提示的标点,都会显著改变结果。
这不是"模型在推理",更像是"模型被提示引导到了一个它见过的任务附近"。 - "权重不动"是个工程优点,也是个理论麻烦。如果不变权重就能"学",
那"学习"到底该怎么定义?这个问题在 §10.1 会再出现一次。
3 规模定律(Scaling Laws)
3.1 要解决什么问题
在 2020 年之前,训练大模型是一件赌博:花几百万美元、跑几个月,
结果好不好要跑完才知道。规模定律要回答的是一个纯工程问题:
我有 $C$ 块钱的算力,该买多大的模型、喂多少数据,才能让损失最低?
如果损失和规模之间有可外推的规律,这个问题就能在纸上回答,
而不用真的把每个配置都训一遍。这就是这一节的全部动机。
3.2 Kaplan 2020:三件事都按幂律下降
OpenAI 的 Kaplan 等人(Scaling Laws for Neural Language Models, arXiv:2001.08361, 2020)
在 7 个数量级的范围内做了几百次训练,发现交叉熵损失 $L$(单位:nat,即自然对数下的
每 token 损失,见《自然语言处理》§3.3 关于困惑度的讨论,$L = \log\mathrm{PPL}$)
单独随三个量按幂律下降:
$$L(N) = \left(\frac{N_c}{N}\right)^{\alpha_N},\qquad L(D) = \left(\frac{D_c}{D}\right)^{\alpha_D},\qquad L(C) = \left(\frac{C_{\min}}{C}\right)^{\alpha_C}$$
逐项解释:
- $N$ = 非嵌入参数个数。为什么排除嵌入层?因为嵌入层的参数量随词表线性增长,
但它对"模型有多会推理"贡献很小;混进去会把规律弄脏。这是个建模选择,
不是自然常数,后面所有论文都得说清自己怎么数的。 - $D$ = 训练 token 数;$C$ = 总训练算力(FLOPs)。
- $N_c, D_c, C_{\min}$ 是尺度常数,它们的值是"当 $L=1$(即 1 nat)时对应的规模"。
论文拟合出 $N_c \approx 8.8\times10^{13}$,$D_c \approx 5.4\times10^{13}$。 - $\alpha_N \approx 0.076$,$\alpha_D \approx 0.095$,$\alpha_C \approx 0.050$。
这些指数都很小,意思是"规模翻一倍,损失只降一点点"。
但小指数加上 7 个数量级的范围,累计效果巨大。
为什么这个形式重要:幂律在对数坐标下是一条直线。
这意味着你可以在小规模上做实验、画一条直线、外推到大模型。
这套结果真正的价值在于,它把"要不要花几千万训一个模型"变成了可计算的风险评估。
论文还给了联合式:
$$L(N, D) = \left[\left(\frac{N_c}{N}\right)^{\alpha_N/\alpha_D} + \frac{D_c}{D}\right]^{\alpha_D}$$
这个式子的结构值得看:括号里是两项相加,所以当 $N$ 很小或 $D$ 很小时,
损失由对应的那一项主导,这解释了为什么"数据和参数必须同步涨",
只涨一个会被另一个卡住。
Kaplan 的核心结论是:$N_{\text{opt}} \propto C^{0.73}$,$D_{\text{opt}} \propto C^{0.27}$。
翻译成人话:算力增加时,主要应该拿来把模型变大,数据只要慢慢加。
GPT-3(1750 亿参数,3000 亿 token,即 1.71 token/参数)就是按这个配方做的。
3.3 Chinchilla 2022:早期模型全都"训欠了"
DeepMind 的 Hoffmann 等人(Training Compute-Optimal Large Language Models,
arXiv:2203.15556, 2022,模型名 Chinchilla)重做了这个实验,结论是Kaplan 错了。
错在哪:Kaplan 的实验里,不同大小的模型用了同一套学习率调度,
而小模型在同样的 token 数下其实已经跑完、大模型还没跑完(或者说调度不匹配)。
这导致小模型的损失被高估,拟合出来的曲线就把最优解推向了大模型那一侧。
Chinchilla 的修正结论是:参数和数据应该按比例一起涨:
$$N_{\text{opt}} \propto C^{0.5},\qquad D_{\text{opt}} \propto C^{0.5}$$
论文的 Approach 1 和 Approach 2 给出 $(0.50, 0.50)$ 和 $(0.49, 0.51)$。
连起来就得到那条著名的经验规则:
$$\boxed{D_{\text{opt}} \approx 20\, N_{\text{opt}}}\qquad\text{(每参数约 20 个 token)}$$
证据:Chinchilla 用 700 亿参数 / 1.4 万亿 token(正好 20 token/参数),
在相同算力下超过了 2800 亿参数 / 3000 亿 token 的 Gopher。
换句话说,同样的钱,买"小模型 + 多数据"比买"大模型 + 少数据"划算。
3.4 实跑:把两个定律放在一起算
import numpy as np
from scipy.optimize import minimize_scalar
NC, DC = 8.8e13, 5.4e13 # Kaplan 的尺度常数
aN, aD = 0.076, 0.095 # Kaplan 的两个指数
E_, A_, B_ = 1.69, 406.4, 410.7 # Chinchilla Approach 3 拟合系数
al, be = 0.34, 0.28
L_kaplan = lambda N, D: ((NC/N)**(aN/aD) + DC/D)**aD
L_chinch = lambda N, D: E_ + A_/N**al + B_/D**be
print(" D Kaplan L Chinchilla L")
for D in [3e9, 1e10, 3e10, 1e11, 3e11, 1.4e12, 1e13]:
print(" %9.2e %8.4f %8.4f" % (D, L_kaplan(1.3e9, D), L_chinch(1.3e9, D)))
C3 = 6.0*175e9*300e9
print(" C = 6 x 175e9 x 300e9 = %.3e FLOPs(GPT-3 论文自报 3.14e23)" % C3)
print(" GPT-3 的 D/N = %.2f tokens/参数" % (300e9/175e9))
print(" C(FLOPs) N* D*=20N*")
for C in [1e21, 1e22, 3.15e23, 1e24, 1e25]:
N = np.sqrt(C/120.0)
print(" %10.2e %11.3e %11.3e" % (C, N, 20*N))
Ns = np.sqrt(3.15e23/120.0)
print(" GPT-3 算力下:N* = %.1fB, D* = %.0fB tokens" % (Ns/1e9, 20*Ns/1e9))
def optimal(Lfun, C):
f = lambda lN: Lfun(np.exp(lN), C/(6.0*np.exp(lN)))
r = minimize_scalar(f, bounds=(np.log(1e5), np.log(1e14)), method="bounded")
return np.exp(r.x)
for name, f in [("Kaplan ", L_kaplan), ("Chinchilla", L_chinch)]:
h = 1e-4
N1 = optimal(f, 1e24*np.exp(-h)); N2 = optimal(f, 1e24*np.exp(h))
print(" %s d log N*/d log C = %.4f" % (name, (np.log(N2)-np.log(N1))/(2*h)))
实跑输出
D Kaplan L Chinchilla L
3.00e+09 2.6202 2.9256
1.00e+10 2.4543 2.6647
3.00e+10 2.3779 2.4923
1.00e+11 2.3445 2.3554
3.00e+11 2.3341 2.2649
1.40e+12 2.3299 2.1769
1.00e+13 2.3289 2.1078
C = 6 x 175e9 x 300e9 = 3.150e+23 FLOPs(GPT-3 论文自报 3.14e23)
GPT-3 的 D/N = 1.71 tokens/参数
C(FLOPs) N* D*=20N*
1.00e+21 2.887e+09 5.774e+10
1.00e+22 9.129e+09 1.826e+11
3.15e+23 5.123e+10 1.025e+12
1.00e+24 9.129e+10 1.826e+12
1.00e+25 2.887e+11 5.774e+12
GPT-3 算力下:N* = 51.2B, D* = 1025B tokens
Kaplan d log N*/d log C = 0.5556
Chinchilla d log N*/d log C = 0.4516
图表逐段读:
- 第一张表:数据从 30 亿加到 10 万亿 token,Kaplan 的损失从 2.62 降到 2.33 后几乎不动了
(最后三档只降了 0.015),而 Chinchilla 的式子还在持续下降(2.93 → 2.11)。
这个差别反映的就是"数据能救多少"的分歧:Chinchilla 认为数据的边际收益比 Kaplan 认为的大得多。 - $C=6ND$ 核对:$6\times175\times10^9\times300\times10^9 = 3.150\times10^{23}$,
GPT-3 论文自报 $3.14\times10^{23}$,误差 0.3%。这个公式
(前向 $2ND$ + 反向 $4ND$,见《深度学习》§3 关于反向传播代价的讨论)
在 7 个数量级上都够用。 - $N^*=\sqrt{C/120}$ 的来历:把 $C=6ND$ 与 $D=20N$ 联立,
$$C = 6N\cdot(20N) = 120N^2 \;\Longrightarrow\; N^* = \sqrt{C/120},\quad D^* = 20N^*$$
用 GPT-3 的算力 $3.15\times10^{23}$ 代进去,得到 $N^*\approx 51$B、$D^*\approx 1.0$T token。
而 GPT-3 实际是 175B / 300B,参数多了 2.4 倍,数据少了 71%。
这就是"GPT-3 训欠了"的量化表述。
4. 最后两行是指数的数值核对。我不直接引用论文的指数,而是从拟合式反推:
对 $\log N^*$ 与 $\log C$ 的曲线做有限差分。结果 Kaplan 式给 0.556,
Chinchilla 式给 0.452。
- 0.452 和 Chinchilla 正文口径 0.50 基本一致(差别来自 Approach 3 的拟合系数,见下)。
- 0.556 和 Kaplan 正文的 0.73 对不上。这不是我算错了,见下面的诚实说明。
⚠️ 一处必须如实标注的不自洽:
Kaplan 论文的联合式(用它的系数)解出来的最优指数是 0.556,不是正文口径的 0.73。
这个差别有来源:正文的 0.73 是从 $L(C)$ 那条单独拟合的曲线推的,
而联合式在数据量很大时拟合得并不好。两套说法出自同一篇论文,本身就不完全自洽。
Chinchilla 也有一模一样的问题:用它的 Approach 3 系数算 GPT-3 算力下的最优分配,
得到 $N^*\approx24.5$B、$D^*\approx2.1$T,$D^*/N^*\approx 87$,
和它自己的"20 token/参数"招牌对不上。
Epoch AI 在 2024 年重新复现了这篇论文(Chinchilla Scaling: A replication attempt,
arXiv:2404.10102),发现是因为拟合时优化器没跑完就停了;重新拟合后
$\beta\approx0.45$,$D^*/N^*$ 回到 20~26 区间,和 Approach 1、2 一致。
所以本页的处理是:把"$N,D$ 大致等比例缩放、约 20 token/参数"当作结论
(这是 Approach 1、2 和后续复现都支持的),把 Approach 3 的那组系数只当参考。
你在论文里看到任何一组具体系数,都要先问它是怎么拟合的。
3.5 边界与批评
- 规模定律是"损失"的定律,不是"能力"的定律。它保证的是交叉熵下降,
不保证任何具体能力(推理、翻译、写代码)会怎么变。这个裂缝就是 §5 的主题。 - 它外推的是"训练损失",不是"下游表现"。下游任务的曲线常常比损失曲线陡得多或者乱得多。
- "数据墙"问题。"按 20 token/参数"这个规则的直接推论是:模型越大,需要的数据越多。
如果某个规模需要的数据量超过人类公开文本的总量,规则就在物理上失效了。
业界对"公开文本还剩多少"的估计差异很大,我不给具体数字。
目前的应对方向是:合成数据、多轮训练(重复数据)、多模态数据,
但这些都改变了定律成立的前提,不能直接外推。 - 一个重要的现实修正:Chinchilla 优化的是训练算力。
但部署一个模型后,推理成本才是长期大头。所以 Llama 2/3 这类开源模型
故意"过训"(远超 20 token/参数),用更多训练算力换更小的模型,
从而降低长期推理成本。"Chinchilla 最优"不等于"商业最优"。
4 GPT 系列的技术路线
这一节按时间顺序走一遍 GPT-1 → GPT-4。每一步都要回答同一个问题:它解决了上一代的什么问题?
(层数、头数、注意力怎么算这些架构细节见《深度学习》§8,这里不重复。)
4.1 GPT-1(2018):预训练 + 微调
Radford, Narasimhan, Salimans & Sutskever, Improving Language Understanding by
Generative Pre-Training, OpenAI 技术报告, 2018。这篇没有 arXiv 编号,
是 OpenAI 直接发布的技术报告(引用时写 "OpenAI Technical Report")。
规模:1.17 亿参数,12 层,12 头,$d_{\text{model}}=768$,上下文 512。
数据:BooksCorpus,约 7000 本未出版的书。
它解决的问题:2018 年之前,每个 NLP 任务都是从随机初始化开始训,
需要大量标注数据。GPT-1 提出两阶段:
阶段一:无监督预训练 用海量无标注文本,训练"预测下一个词"
(这一阶段不需要任何人工标注)
↓
阶段二:有监督微调 拿少量标注数据,改全部权重,适配具体任务
(这一步还是要标注,还是要改权重)
它没解决的问题:每个任务还是要一份独立的权重和一份标注数据。
你有 10 个任务,就要存 10 份 GPT-1。这跟 BERT 的思路没有本质区别,
只是把"随机初始化"换成了"语言模型初始化"。
4.2 GPT-2(2019):zero-shot,但当时没人信
Radford et al., Language Models are Unsupervised Multitask Learners,
OpenAI 技术报告, 2019。同样没有 arXiv 编号。
规模:15 亿参数(最大版本),48 层,$d_{\text{model}}=1600$,上下文 1024。
数据:WebText,40GB,来自 4500 万个 Reddit 高赞帖子外链的网页。
它想解决的问题:既然语言模型在预测下一个词时,
必须隐式地"会"翻译、总结、问答,那能不能干脆不再微调,直接把任务写成一段文本?
比如做翻译,不给它任何标注,只给它一段:
把下面的英文翻译成中文:
"the cat sat on the mat" -> "猫坐在垫子上"
"the dog ran" ->
GPT-2 的论文标题里那两个字 "Unsupervised Multitask" 就是这个意思:
多任务学习可以在"预测下一个词"这一个目标里隐式地发生。
它没解决的问题:效果还不够好。 GPT-2 的 zero-shot 在许多任务上
明显不如当时的微调模型。所以这篇论文当时更多被当成一个"有趣的现象",
而不是范式转变。它的历史意义是提了一个赌注,然后等 GPT-3 把赌注兑现。
(另一个值得一提的细节:OpenAI 最初以"担心滥用"为由分阶段发布这个模型,
先放 1.24 亿的小版本,几个月后才放全量。这是"模型发布策略"进入公共讨论的起点。)
4.3 GPT-3(2020):in-context learning
Brown et al., Language Models are Few-Shot Learners,
NeurIPS 2020, arXiv:2005.14165。
规模:1750 亿参数,96 层,$d_{\text{model}}=12288$,96 头,上下文 2048。
数据:3000 亿 token(Common Crawl 过滤后 + WebText2 + 两套书 + 英文维基,
按质量加权混合,不是按原始体量)。
算力:论文自报约 $3.14\times10^{23}$ FLOPs(§2 实跑核对过,$6ND$ 给出 $3.15\times10^{23}$)。
它解决的问题:GPT-2 的 zero-shot 效果不行,GPT-3 把规模拉到 100 倍以上,
few-shot 就真的能用了(§1.3 的实跑演示了它的机制)。
为什么这是"质变"而不是"量变":GPT-3 的架构和 GPT-2 几乎一样,
训练目标也完全一样。变的只有规模。而 in-context learning 这个能力
没有出现在任何一版的设计文档里,它是规模变大的副产品。
这是"规模能带来质变"的第一个硬证据,也是 §5 那场争论的起点。
它的代价:提示变长 → 每次推理都更贵;不能靠微调提高上限;
对提示的措辞、例子顺序极其敏感;而且能力上限被上下文长度锁死
(2048 token,放不下太多例子和长文档)。
4.4 InstructGPT(2022):RLHF,把"续写器"变成"助手"
Ouyang et al., Training language models to follow instructions with human feedback,
arXiv:2203.02155, 2022(OpenAI)。
它解决的问题(这一步最关键):GPT-3 的训练目标是"预测下一个词"。
你输入"法国的首都是哪里?",它完全可能续写出"德国的首都是哪里?",
因为在训练数据里,这两个问句经常挨在一起。
模型没有做错任何事,它在完美地执行 "续写" 这个目标,只是这不是你要的。
这个问题有自己的名字:misalignment(不对齐):模型在做我们训练它做的事,
不是我们想让它做的事。
做法:三步 RLHF,13k 条 SFT 数据 + 33k 条偏好比较 + 31k 条 PPO 提示。§4 整节讲这个。
结果(论文报告的数字):
- 13 亿参数的 InstructGPT,在人工偏好评测里胜过 1750 亿参数的 GPT-3,
参数少 100 倍,人更喜欢它的回答。
- 175B InstructGPT 对 175B GPT-3 的胜率约 85%。
能力(capability)和对齐(alignment)是两个独立的轴。
参数量决定"会不会",RLHF 决定"愿不愿意按你要的方式做"。这两件事不是同一件事,
所以"更大的模型会自动更听话"是错的。
(ChatGPT 就是这条路线上的产品:GPT-3.5 系列 + RLHF + 对话界面。
具体版本号和训练细节 OpenAI 没有完整公开,我不给不确定的细节。)
4.5 GPT-4(2023):能力跃升,但技术细节全部不公开
OpenAI, GPT-4 Technical Report, arXiv:2303.08774, 2023-03。
公开的部分:
- 多模态:接受图像 + 文本输入,输出文本。
- 评测成绩(论文自报):模拟律师资格考试进入前 10%(GPT-3.5 是后 10%);
5-shot MMLU 86.4%;5-shot CoT GSM-8K 92.0%;0-shot HumanEval 67.0%。
- 一个重要观察:各项考试能力主要来自预训练。
论文比较了基础模型和 RLHF 后的模型在多选题上的表现:
73.7% vs 74.0%,几乎没有差别。
不公开的部分:架构、参数量、训练算力、数据构成、训练方法,报告里真的没写,
理由是"竞争格局和安全考虑"。报告的大部分篇幅给了安全性测试、红队测试和 RLHF 部分。
那个观察说明,RLHF 改的主要是"怎么说话",不是"会不会"。
所以把 RLHF 理解成"让模型变聪明"是错的;它是"让模型的行为更符合人类偏好"。
4.6 一条线索
| 代 | 新增的东西 | 解决的上一代的问题 | 新的代价 |
|---|---|---|---|
| GPT-1 | 预训练 + 微调 | 从零训练需要太多标注 | 每个任务一份权重 |
| GPT-2 | zero-shot 的赌注 | 每任务一份权重 | 效果不够好 |
| GPT-3 | in-context learning | 效果不够好(靠规模) | 推理贵、受上下文长度限制、对提示敏感 |
| InstructGPT | RLHF | 模型不按指令做事 | 依赖标注员、奖励模型可被"作弊" |
| GPT-4 | 多模态 + 能力跃升 | 单模态 | 细节不公开,无法复现 |
贯穿这条线的一个更大的背景:Rich Sutton 在 2019 年的短文
The Bitter Lesson 里说:AI 的历史上,依赖人类先验知识的方法,
最后总会被"通用方法 + 更多算力"超过。
GPT 系列从 1 到 3 的路,几乎就是这条 lesson 的一次现场演示。
每次有人想加人类设计的结构,最后都是"把它变大 + 喂更多数据"赢。
(注意:Sutton 那篇是博客短文,不是同行评议论文,引用时要说清楚。)
5 RLHF(人类反馈强化学习)⭐
这一节把 RLHF 的三步拆开:监督微调(SFT)、奖励模型(RM)、PPO。
5.1 为什么需要 RLHF:因为"好回答"没有标准答案
假设你要用监督学习教模型"好好回答问题"。你需要什么?
每个问题配一个标准答案。但问题是:
"用一句话解释什么是熵",这句话有标准答案吗?
没有。可以有一万种都正确的写法。监督学习要求一个"唯一正确"的输出,
而这个任务本质上不存在唯一正确。问题不在数据够不够,而在任务的形态不对。
但有另一件事人是能做的:看到两个回答,说哪个更好。
这不需要你写出标准答案,只需要你能比较。
RLHF 的全部洞察就是这一句:把"打分"换成"排序",
再把"排序"变成可以反向传播的目标函数。
起源:这个思路最早由 Christiano 等人在 2017 年提出
(Deep Reinforcement Learning from Human Preferences, NeurIPS 2017, arXiv:1706.03741),
当时用在 Atari 游戏上,人类只看两段游戏片段,说哪段好,模型从比较中学习。
InstructGPT 把它搬到了语言模型上。
5.2 第一步:SFT(监督微调)
做了什么:雇标注员,给 13k 条提示(prompt),每条让他们手写一个理想的回答。
然后用标准的交叉熵(就是《自然语言处理》§3.3 的困惑度那个损失)
微调 GPT-3,跑 16 轮。
这一步解决什么:把模型从"续写器"变成"回答器"。它学会了"看到问题要回答"
这个格式。
这一步没解决什么:它只有"一个"标准答案。
同一个问题,标注员写了一种好回答,模型就只学那一种。
"回答得更好"这件事,SFT 学不到,因为它没有"更好"这个概念,只有"对/不对"。
5.3 第二步:奖励模型(RM):本节的公式核心
数据怎么来:
一条提示 x
│
├──► SFT 模型生成 K = 4 ~ 9 个不同的回答 y_1, y_2, ..., y_K
│
└──► 标注员把这 K 个回答从最好到最差排个序
→ 排序拆成 C(K,2) 个"成对比较":(y_w 比 y_l 好)
论文用了 33k 条提示。每条提示的排序都拆成若干对 $(y_w, y_l)$,
其中下标 $w$ = winner(被排得更靠前的),$l$ = loser。
模型结构:拿 SFT 模型,把最后一层(输出词表的那个)换成一个
输出单个标量的头。于是 $r_\theta(x,y) \in \mathbb{R}$。
给它一个提示和一个回答,它吐出一个分数。
目标函数(Bradley-Terry 模型):
$$\mathcal{L}(\theta) \;=\; -\,\mathbb{E}_{(x,\,y_w,\,y_l)\sim \mathcal{D}} \left[\log \sigma\Big(r_\theta(x, y_w) - r_\theta(x, y_l)\Big)\right]$$
逐项解释:
- $r_\theta(x,y_w)$、$r_\theta(x,y_l)$:奖励模型给两个回答打的分。$\theta$ 是它的参数。
- 为什么只出现差值 $r_w - r_l$,不出现单独的 $r$?
因为偏好排序提供的只有相对信息。"A 比 B 好"这件事,
在你把所有分数同时加 100 之后依然成立。所以奖励的绝对零点不可辨识,
这决定了损失函数只能依赖差值。这不是简化,是数据本身的信息量决定的。 - $\sigma(\cdot)$:sigmoid 函数,把差值映射到 $(0,1)$。它表示
"模型认为 $y_w$ 比 $y_l$ 好的概率"。 - $\log$ + 前面的负号:最大化对数似然。差值越大,
$\sigma$ 越接近 1,$\log\sigma$ 越接近 0,损失越小。 - 期望 $\mathbb{E}$ 是对整个偏好数据集取的。
论文里还乘了一个 $\frac{1}{\binom{K}{2}}$ 的归一化因子:
因为同一条提示内部的 $K$ 个回答不独立,
论文把它们作为同一个 batch 元素处理(只需要一次 RM 前向),
既省算力又防止过拟合。
这个损失和逻辑回归是同一个东西。把它当成"用 $(y_w,y_l)$ 这对样本,
训练一个二分类器判断谁更好",完全等价。
梯度(记 $d = r_w - r_l$):
$$\frac{\partial \mathcal{L}}{\partial r_w} = -\big(1 - \sigma(d)\big),\qquad \frac{\partial \mathcal{L}}{\partial r_l} = +\big(1 - \sigma(d)\big)$$
方向解读:括号里恒为正,所以对 $r_w$ 的梯度是负的(梯度下降把它往上推),
对 $r_l$ 是正的(往下压)。而且 $d$ 越大(模型已经判对了),
$\sigma(d)\to1$,梯度 $\to 0$,已经判对的样本就自动不再出力。
这和《自然语言处理》§9 第 2 题里负采样的梯度行为是同一个模式。
实跑核对:
import numpy as np
np.set_printoptions(precision=4, suppress=True)
sigmoid = lambda x: 1.0/(1.0+np.exp(-x))
rm_loss = lambda rw, rl: -np.log(sigmoid(rw - rl)) # L = -log σ(r_w - r_l)
rw0, rl0, eps = 0.7, -0.2, 1e-6
num_w = (rm_loss(rw0+eps, rl0) - rm_loss(rw0-eps, rl0))/(2*eps)
num_l = (rm_loss(rw0, rl0+eps) - rm_loss(rw0, rl0-eps))/(2*eps)
ana_w = -(1.0 - sigmoid(rw0 - rl0)) # 解析梯度
print("L = -log sigmoid(r_w - r_l) = %.6f" % rm_loss(rw0, rl0))
print("dL/dr_w 解析 %+.8f 数值 %+.8f" % (ana_w, num_w))
print("dL/dr_l 解析 %+.8f 数值 %+.8f" % (-ana_w, num_l))
rng = np.random.default_rng(0)
TRUE_W = np.array([1.5, -0.8, 2.0])
Fw = rng.normal(size=(400, 3))
Fl = Fw + rng.normal(scale=1.2, size=(400, 3))
keep = (Fw@TRUE_W + rng.normal(scale=0.5, size=400)
> Fl@TRUE_W + rng.normal(scale=0.5, size=400))
Fw, Fl = Fw[keep], Fl[keep]
w = np.zeros(3)
for _ in range(400):
p = sigmoid(Fw@w - Fl@w)
grad = (-(1-p)[:, None]*Fw + (1-p)[:, None]*Fl).mean(axis=0)
w -= 0.05*grad
print("%d 对比较,真实权重 %s" % (len(Fw), TRUE_W))
print("训出 w = %s 成对准确率 %.1f%%" % (w, ((Fw@w) > (Fl@w)).mean()*100))
实跑输出
L = -log sigmoid(r_w - r_l) = 0.341154
dL/dr_w 解析 -0.28905050 数值 -0.28905050
dL/dr_l 解析 +0.28905050 数值 +0.28905050
218 对比较,真实权重 [ 1.5 -0.8 2. ]
训出 w = [ 1.5318 -0.7295 1.8591] 成对准确率 94.0%
解析梯度和数值梯度逐位相同,推导正确。
下面那段是一个真的训出来的奖励模型:用 218 对比较、手写梯度下降,
训出的权重 $[1.53, -0.73, 1.86]$ 和真实权重 $[1.5, -0.8, 2.0]$ 方向一致、
相对大小一致,但绝对值不同,恰好印证了前面那句话:
奖励的绝对大小没有意义,只有相对大小有意义。
两个工程细节(论文里的做法):
1. 奖励模型只有 6B。论文说 175B 的奖励模型训练不稳定。
所以 175B 的策略,是由一个 6B 的"裁判"在打分。裁判比选手弱,
这是 RLHF 的一个结构性弱点。
2. 奖励归一化:训练前先把偏置调到"标注员手写的示范回答平均得 0 分"。
为什么?因为下一步的 KL 罚项需要一个有意义的尺度,
而奖励的零点本来是不可辨识的,必须人为锚定一个。
5.4 第三步:PPO,用强化学习优化策略
设定:把 SFT 模型当作策略 $\pi^{SFT}$(输入提示、输出回答的分布),
奖励模型当作环境(给出标量奖励)。我们要找一个新策略 $\pi^{RL}_\phi$,
让奖励模型的评分尽可能高。
目标函数(PPO-ptx 版本的核心项):
$$\max_{\phi}\;\; \mathbb{E}_{x\sim\mathcal{D},\; y\sim\pi^{RL}_\phi(\cdot\mid x)} \left[\; \underbrace{r_\theta(x,y)}_{\text{拿高分}} \;-\; \beta\, \underbrace{\log \frac{\pi^{RL}_\phi(y\mid x)}{\pi^{SFT}(y\mid x)}}_{\text{别跑太远}} \;\right]$$
逐项解释:
- 第一项 $r_\theta(x,y)$:奖励模型给的分数。$\theta$ 冻结(不再更新),
只有策略参数 $\phi$ 在动。 - 第二项:KL 罚项。它衡量新策略和 SFT 策略在每一个 token 上的
对数概率比。如果新策略给某个 token 的概率远高于 SFT 策略,这一项就大,
把它从奖励里减掉。 - $\beta$:这个旋钮是整节的重点。$\beta$ 大 = 保守,不太敢偏离;
$\beta$ 小 = 激进,敢往高分方向猛冲。
为什么必须有 KL 罚项:因为奖励模型只是人类偏好的有损代理。
它是用 33k 条比较训出来的一个 6B 模型,它不等于人类偏好。
如果你只最大化 $r_\theta$,模型会去找这个代理的漏洞,
生成那些在 RM 眼里分极高、但在人看来荒谬的回答。
这叫 reward hacking(奖励黑客),也叫 Goodhart 定律:
"当一个度量变成目标,它就不再是好度量。"
这个目标可以解析求解:
把目标写成 $\max_\pi \; \mathbb{E}_\pi[r] - \beta\,\mathrm{KL}(\pi\,\|\,\pi^{SFT})$,
用拉格朗日乘子对每个 $\pi(y)$ 求导并令其为零,得到
$$\boxed{\;\pi^*(y\mid x) \;=\; \frac{1}{Z(x)}\,\pi^{SFT}(y\mid x)\, \exp\!\left(\frac{1}{\beta}\,r_\theta(x,y)\right),\qquad Z(x)=\sum_{y}\pi^{SFT}(y\mid x)\exp\!\left(\frac{r_\theta(x,y)}{\beta}\right)\;}$$
怎么读这个解:它说最优策略就是把参考策略按奖励做指数倾斜。
奖励每高出一点,概率就乘一个 $\exp(r/\beta)$。
$\beta$ 越小,指数越陡,概率越集中到高奖励的输出上。
实跑验证闭式解,并看 $\beta$ 的作用:
import numpy as np
from scipy.optimize import minimize
r = np.array([0., 1., 2., 3., 5.]) # 5 个输出各自的奖励
pi_ref = np.array([0.30, 0.25, 0.20, 0.15, 0.10]) # 参考策略(SFT 模型)
r = r - (pi_ref*r).sum() # 奖励减均值,规范化
kl = lambda p, q: float((p[p > 0]*np.log(p[p > 0]/q[p > 0])).sum())
def neg_obj(logits, beta): # 目标取负,交给 minimize
z = logits - logits.max(); p = np.exp(z); p /= p.sum()
obj = (p*r).sum() - beta*kl(p, pi_ref) # E[r] - beta*KL
g = p*(r - (p*r).sum()) - beta*p*(np.log(p/pi_ref) - kl(p, pi_ref))
return -obj, -g
print("奖励 r = %s pi_ref = %s" % (r, pi_ref))
print(" beta pi*(闭式解) E[r] KL")
for beta in [10.0, 2.0, 0.5, 0.1, 0.02]:
wv = np.exp(r/beta); ps = pi_ref*wv; ps /= ps.sum() # 闭式解
z = minimize(lambda z: neg_obj(z, beta), np.zeros(5), jac=True,
method="BFGS", options={"gtol": 1e-12}).x
z = z - z.max(); pn = np.exp(z); pn /= pn.sum() # 数值解
print(" %5.2f %s %6.3f %7.4f (数值解偏差 %.1e)"
% (beta, np.array2string(np.round(ps, 4), precision=4, floatmode='fixed'),
(ps*r).sum(), kl(ps, pi_ref), np.abs(pn-ps).max()))
实跑输出
奖励 r = [-1.6 -0.6 0.4 1.4 3.4] pi_ref = [0.3 0.25 0.2 0.15 0.1 ]
beta pi*(闭式解) E[r] KL
10.00 [0.2525 0.2326 0.2056 0.1704 0.1388] 0.249 0.0127 (数值解偏差 8.4e-11)
2.00 [0.0953 0.1310 0.1728 0.2137 0.3872] 1.454 0.3805 (数值解偏差 6.2e-12)
0.50 [1.0000e-04 8.0000e-04 4.8000e-03 2.6600e-02 9.6770e-01] 3.329 2.1268 (数值解偏差 3.6e-12)
0.10 [0.0000 0.0000 0.0000 0.0000 1.0000] 3.400 2.3026 (数值解偏差 4.5e-10)
0.02 [0.0000 0.0000 0.0000 0.0000 1.0000] 3.400 2.3026 (数值解偏差 4.5e-13)
逐行读这张表:
- "数值解偏差"那一列全程在 $10^{-10}$ 及以下,说明闭式解和
直接数值优化目标函数得到的结果是同一个东西,推导没错。 - $\beta=10$:$\pi^*$ 几乎等于 $\pi_{ref}$,$E[r]$ 只从 0 涨到 0.249,KL 只有 0.013。
几乎什么也没学到,但也没跑偏。 - $\beta=2$:明显偏向高奖励的那个输出(0.387),KL 0.38。这是"正常训练"的样子。
- $\beta=0.5$:概率质量高度集中到最后一个输出(0.968),KL 2.13。
- $\beta\le0.1$:全部概率押在奖励最高的那一个上,KL 停在 2.30(这是
从这个 $\pi_{ref}$ 出发能达到的最大 KL),$E[r]$ 也卡在 3.400 不再涨。
最后两行就是 reward hacking 的样子:策略已经完全崩成一个确定性输出,
而"奖励"这个数字看起来完美(3.400,是理论最大值)。
如果那个 3.4 分的输出其实是一个钻了 RM 空子的荒谬回答,你在指标上完全看不出来。
PPO-ptx:论文还试了一个变体,在 PPO 的目标里额外混入预训练梯度。
为什么?因为纯 RLHF 之后,模型在标准 NLP 基准上会掉分,
这个现象叫 alignment tax(对齐税)。混入预训练梯度就是"交税"的方式。
论文里除非特别说明,InstructGPT 指的都是这个 PPO-ptx 版本。
5.5 边界与批评
- 标注员只有一小撮人。论文里大约 40 名标注员(大部分来自美国和东南亚的外包)。
他们的偏好不等于全人类偏好。谁被雇来标注,模型就偏向谁。
这是一个社会性的偏差来源,不是技术问题。 - 裁判比选手弱。6B 的奖励模型给 175B 的策略打分,这个结构性问题无法回避。
- reward hacking 有规模定律。Gao, Schulman & Hilton
(Scaling Laws for Reward Model Overoptimization, ICML 2023, arXiv:2210.10760)
用一个"金标准奖励模型"模拟人类,发现:优化代理奖励会让真实奖励先升后降,
而且这条曲线随奖励模型参数量平滑变化,RM 越大,越抗优化。
这又是一个"用规模控制的量"。 - RLHF 会让模型变得讨好(sycophancy)。因为标注员偏好"让人舒服的回答",
所以优化人类偏好会顺带优化出"迎合用户"的行为。
这是 2022 年之后被反复报告的已知现象(具体文献我不列,因为这一块的命名和研究还在演化,
我不敢给一个确定的权威出处)。 - RLHF 不保证模型变诚实。
它让模型更像标注员喜欢的样子。
诚实是可能的副产品,不是它的目标。把这两件事混为一谈,
是这个话题最容易被误读的地方。 - 后续发展(截至写作时):这个三步流程本身也在被简化。
DPO(Rafailov et al., Direct Preference Optimization,
NeurIPS 2023, arXiv:2305.18290)证明:在上面那个 KL 正则的框架下,
可以绕过显式的奖励模型,直接用偏好数据优化语言模型本身。
原理上它和 RLHF 等价(在 reverse-KL 正则下),工程上更简单。
这一块演化很快,我不对"现在主流用哪个"下结论。
6 涌现能力(emergent abilities)与它的争议 ⭐
这一节讲的是一种思维陷阱。
6.1 Wei et al. 2022:能力在规模上"突然出现"
Wei, Tay, Bommasani, Raffel, Zoph, Borgeaud, Yogatama, Bosma, Zhou, Metzler, et al.,
Emergent Abilities of Large Language Models,
TMLR 2022, arXiv:2206.07682。
定义:一个能力是涌现的,如果它
(1)在小模型上不存在,(2)在大模型上存在,
(3)而且不能通过外推小模型的性能来预测。
第(3)条是关键。如果一个小模型准确率 5%、大一点的 8%、再大一点的 12%,
你可以外推,这不叫涌现。涌现是一直贴着 0,然后突然跳到 30%。
论文在 BIG-Bench 等基准上找到了大量这种形状的曲线:三位数算术、
音译、多步推理、某些符号操作任务。而且是在 100 亿参数附近"跨过门槛"。
为什么这个说法重要(不只是学术趣味):如果真的存在不可预测的涌现,
那意味着安全评估不能只做小模型然后外推,你无法预知哪个规模会冒出哪种能力,
包括危险的能力。这是一个实打实的安全论证。
6.2 Schaeffer et al. 2023:这可能是个"海市蜃楼"
Schaeffer, Miranda & Koyejo, Are Emergent Abilities of Large Language Models a Mirage?,
NeurIPS 2023(Outstanding Paper), arXiv:2304.15004。
核心论断:对固定的任务和模型家族,涌现是研究者选择的指标造成的。
- 用非线性、不连续的指标 → 看起来涌现。
- 用线性、连续的指标 → 平滑、可预测,完全没有突变。
证据:他们统计了 BIG-Bench 上声称的涌现能力,发现 超过 92% 都只用两种指标:
Exact String Match(精确字符串匹配)和 Multiple Choice Grade(多选题评分),
而这两个都是非连续指标。在 39 个指标里,只有 4 个会产生涌现现象。
机制:
设一个模型在单个 token 上正确的概率是 $a(N)$,$a$ 随规模 $N$ 平滑上升。
一个需要 $L$ 个 token 全对的答案,它的 Exact Match 准确率是
$$\mathrm{EM}(N) = \underbrace{a(N)^L}_{\text{所有 } L \text{ 个 token 都对}}$$
$a(N)$ 是平滑的,但 $a(N)^L$ 不是。当 $a = 0.5$、$L=8$ 时,$a^L = 0.0039$,
在图上看不见。当 $a$ 涨到 0.85 时,$a^L = 0.27$,突然就看得见了。
中间只隔了 $a$ 从 0.5 到 0.85 的变化,而这条曲线本身没有任何拐点。
第二个机制:测试集的分辨率。如果测试集只有 200 题,
准确率的最小非零刻度是 $1/200 = 0.005$。
真实准确率 0.003 的模型,测出来必然是 0,看起来"完全不会"。
于是"从 0 到有"的那一段被人为压平了,跳变显得更陡。
6.3 实跑:亲眼看着"涌现"被制造出来
下面这段代码里,$a(N)$ 是一条完全光滑的 S 曲线,没有阈值、没有拐点、没有相变。
我们只改一件事:用哪个指标去测它。
import numpy as np
a_of = lambda N: 0.10 + 0.85/(1.0 + np.exp(-(np.log10(N) - 9.0)/1.2))
sizes = [1e6, 3e6, 1e7, 3e7, 1e8, 3e8, 1e9, 3e9, 1e10, 3e10, 1e11, 3e11]
print(" N(参数) a(N) EM(L=1) EM(L=2) EM(L=4) EM(L=8) EM(L=16)")
for N in sizes:
a = a_of(N)
print(" %6.0e %.4f %s" % (N, a, "".join("%9.2e" % a**L for L in [1, 2, 4, 8, 16])))
print()
print(" N(参数) log a(N) Δ 相对于上一行")
prev = None
for N in sizes:
v = float(np.log(a_of(N)))
print(" %6.0e %9.6f %s" % (N, v, "" if prev is None else "%+.6f" % (v-prev)))
prev = v
print()
print(" N(参数) EM(L=8) 200 题期望答对 P(一题都没答对)")
for N in sizes:
e = a_of(N)**8
print(" %6.0e %9.2e %8.4f %8.5f" % (N, e, e*200, (1-e)**200))
实跑输出
N(参数) a(N) EM(L=1) EM(L=2) EM(L=4) EM(L=8) EM(L=16)
1e+06 0.1645 1.64e-01 2.71e-02 7.32e-04 5.36e-07 2.87e-13
3e+06 0.1925 1.93e-01 3.71e-02 1.37e-03 1.89e-06 3.57e-12
1e+07 0.2350 2.35e-01 5.52e-02 3.05e-03 9.31e-06 8.67e-11
3e+07 0.2865 2.87e-01 8.21e-02 6.74e-03 4.54e-05 2.06e-09
1e+08 0.3575 3.57e-01 1.28e-01 1.63e-02 2.67e-04 7.12e-08
3e+08 0.4338 4.34e-01 1.88e-01 3.54e-02 1.26e-03 1.58e-06
1e+09 0.5250 5.25e-01 2.76e-01 7.60e-02 5.77e-03 3.33e-05
3e+09 0.6084 6.08e-01 3.70e-01 1.37e-01 1.88e-02 3.52e-04
1e+10 0.6925 6.93e-01 4.80e-01 2.30e-01 5.29e-02 2.80e-03
3e+10 0.7579 7.58e-01 5.74e-01 3.30e-01 1.09e-01 1.18e-02
1e+11 0.8150 8.15e-01 6.64e-01 4.41e-01 1.95e-01 3.79e-02
3e+11 0.8543 8.54e-01 7.30e-01 5.33e-01 2.84e-01 8.05e-02
N(参数) log a(N) Δ 相对于上一行
1e+06 -1.804970
3e+06 -1.647481 +0.157489
1e+07 -1.448005 +0.199476
3e+07 -1.250000 +0.198004
1e+08 -1.028621 +0.221379
3e+08 -0.835069 +0.193552
1e+09 -0.644357 +0.190712
3e+09 -0.496932 +0.147425
1e+10 -0.367446 +0.129485
3e+10 -0.277224 +0.090223
1e+11 -0.204615 +0.072609
3e+11 -0.157503 +0.047112
N(参数) EM(L=8) 200 题期望答对 P(一题都没答对)
1e+06 5.36e-07 0.0001 0.99989
3e+06 1.89e-06 0.0004 0.99962
1e+07 9.31e-06 0.0019 0.99814
3e+07 4.54e-05 0.0091 0.99096
1e+08 2.67e-04 0.0534 0.94803
3e+08 1.26e-03 0.2510 0.77789
1e+09 5.77e-03 1.1543 0.31424
3e+09 1.88e-02 3.7542 0.02260
1e+10 5.29e-02 10.5777 0.00002
3e+10 1.09e-01 21.7698 0.00000
1e+11 1.95e-01 38.9158 0.00000
3e+11 2.84e-01 56.7294 0.00000
三段输出,三个结论:
- 第一张表:看 EM(L=1) 那一列,它就是 $a(N)$ 本身,
从 0.16 平滑爬到 0.85。这里看不出任何"涌现"。
再看 EM(L=16) 那一列:从 $2.87\times10^{-13}$ 到 $8.05\times10^{-2}$,
跨了 11 个数量级;而中间 (比如 $N=10^9$) 它只有 $3.33\times10^{-5}$,
在图上就是 0。如果你画这张图,你会说"16 位算术能力在 $10^{10}$ 附近突然涌现"。
但底层那条 $a(N)$ 从头到尾没有任何突变。 - 第二张表:把指标换成 $\log a(N)$,斜率从 $+0.16$ 到 $+0.22$ 再降到 $+0.047$,
全程连续、单调、可外推。"涌现"消失了。 - 第三张表:加上 200 题的测试集分辨率。$N=3\times10^8$ 时真实 EM 是 0.00126,
200 题里一题都答不对的概率是 77.8%,也就是说,这个模型有近八成的概率
被测成"完全不会"。$N=10^9$ 时这个概率还有 31%。小模型那一段被压成了平地,
台阶因此显得更陡。
6.4 这一课:指标决定结论
先说清楚双方各自对在哪:
- Wei 那边观测到的现象是真的。在 Exact Match 指标下,
8 位加法准确率确实从 0 涨到了 30%。这个事实没有被推翻。 - Schaeffer 那边没有推翻现象,推翻的是解释。"能力在某个规模突然出现"
只是一种解释,而且是一种不需要引入"突变"就能解释的现象。
为什么这一课对你特别重要:因为它和你在《实验方法论》里学的
"指标决定结论"是同一个东西,只是换了个场景。
- 儿童发展研究里,"守恒任务"用全对才算通过的评分 vs 按理由质量给分,
会给出完全不同的"发展阶段"结论。 - 反应时实验里,用平均反应时 vs 反应时分布的分位数,
会给出不同的"效应出现/消失"结论。 - 二语习得研究里,用准确率 vs 错误类型的分布,
会给出不同的"关键期在哪"的结论。
同一个底层过程的平滑变化,被一个非线性指标放大成了台阶。
实践建议:以后看到任何"涌现"图,先问三个问题。
- 用的是什么指标? 是连续的(困惑度、对数似然、token 准确率)
还是不连续的(Exact Match、全对才算对)? - 换成连续指标,现象还在吗? 如果换完就平滑了,那"涌现"很可能是指标的产物。
- 测试集多大? 小于 1000 题的测试集,在低准确率区间没有分辨率。
那里所有的"0"都不可信。
最后一个诚实的补充:Schaeffer 的结论不是"大模型不可能有涌现"。
它说的是"已有的涌现声称可能是指标造成的海市蜃楼"。
真正意义上的相变(如果存在)需要更强的证据。
这个问题到今天(截至本书写作时)仍然没有定论。
7 提示工程与思维链(CoT)
7.1 few-shot:把任务写进提示
这一块在 §1.2、§1.3 已经讲清了机制(前向里的最小二乘),这里只补工程要点:
- 例子的格式必须一致。给三个"输入→输出"的例子,第四个只给输入,
模型会照着格式补。格式变了(少一个冒号、换行方式不同),结果会变。 - 例子的顺序有影响。同一批例子换个顺序,准确率会动。这个效应在分类任务上尤其明显。
- 标签的分布有影响。如果你给的例子里"正面"占 8 个、"负面" 2 个,
模型会倾向于输出"正面",它把这当成了任务的先验。 - 例子要贴近真实任务的分布。这和你训练任何模型时"训练集要代表测试集"是同一件事。
为什么这一节标了 *(创新创业思维):提示工程是唯一一种不需要训练、
不需要数据标注、不需要 GPU 的"改造模型行为"的手段。
它的边际成本接近零,所以是小团队能做出差异化的地方。
但它也没有护城河,你的提示词别人复制走就能用。
这是这门课里"创业"和"技术"最直接冲突的一个点。
7.2 思维链(Chain-of-Thought, CoT)
Wei, Wang, Schuurmans, Bosma, Ichter, Xia, Chi, Le & Zhou,
Chain-of-Thought Prompting Elicits Reasoning in Large Language Models,
NeurIPS 2022, arXiv:2201.11903。
做法:在 few-shot 的例子里,不只给答案,把中间推理步骤也写出来。
【普通 few-shot】
问:食堂有 23 个苹果,用掉 20 个,又买了 6 个,现在几个?
答:9
【CoT】
问:食堂有 23 个苹果,用掉 20 个,又买了 6 个,现在几个?
答:食堂原来有 23 个。用掉 20 个,剩 23 - 20 = 3 个。
又买了 6 个,3 + 6 = 9 个。答案是 9。
三个关键实验发现(Wei et al. 报告):
- 效果:PaLM 540B 在 GSM8K(小学数学应用题)上,
只用 8 个 CoT 样例,准确率从 约 17.9% 提到约 56.5%,
超过了当时专门微调过的 GPT-3 加验证器。
(口径说明:不同来源引用的数字略有出入,PaLM 论文自身报的是 17% → 54%,
差别来自是否外挂计算器。总之是三四倍的提升,不是几个百分点。) - 只有大模型才受益:参数量小于约 100B 时,CoT 要么没用,要么反而更差,
小模型会生成"读起来通顺、但逻辑不通"的推理链,然后照着错的链得出错的答案。
所以这又是一个"涌现"断言,它同样要接受 §5 那套质疑。 - 只有多步任务才受益:GSM8K(多步)提升巨大,
而 SingleOp(单步运算)几乎没有提升,个别设置下还略降。
为什么是"指数级"而不是"常数级"的提升,用可跑代码说清:
设一个问题需要 $m$ 步。不用 CoT 时,模型必须把 $m$ 步全部塞进一次前向里;
中间结果没有"落脚点"(没有写出来的 token 可以回头参照),
所以每步做对的概率退化到一个较低的值 $q$,整体准确率 $\approx q^m$。
用 CoT 时,每一步的结果被写成 token 反馈回输入,下一步是一次新的前向,
每步都能"看见"前面的结果,每步准确率维持在 $p$,整体准确率 $= p^m$。
取 $p=0.95$(用了 CoT)、$q=0.55$(没有中间结果的退化值):
from math import comb
print(" 步数 m 直接答 q^m 加 CoT p^m 倍数")
for m in [1, 2, 3, 4, 6, 8, 12]:
a, b = 0.55**m, 0.95**m
print(" %4d %10.4f %10.4f %7.1fx" % (m, a, b, b/a))
maj = lambda k, c: sum(comb(k, i)*(c**i)*((1-c)**(k-i)) for i in range(k//2+1, k+1))
ks = [1, 3, 5, 9, 21, 51, 101]
print()
print(" 每条链准确率 c |" + "".join("%8s" % ("k=%d" % k) for k in ks))
for c in [0.3, 0.4, 0.45, 0.5, 0.565, 0.7]:
print(" c = %.3f |" % c + "".join("%8.3f" % maj(k, c) for k in ks))
实跑输出
步数 m 直接答 q^m 加 CoT p^m 倍数
1 0.5500 0.9500 1.7x
2 0.3025 0.9025 3.0x
3 0.1664 0.8574 5.2x
4 0.0915 0.8145 8.9x
6 0.0277 0.7351 26.6x
8 0.0084 0.6634 79.2x
12 0.0008 0.5404 705.2x
每条链准确率 c | k=1 k=3 k=5 k=9 k=21 k=51 k=101
c = 0.300 | 0.300 0.216 0.163 0.099 0.026 0.001 0.000
c = 0.400 | 0.400 0.352 0.317 0.267 0.174 0.074 0.021
c = 0.450 | 0.450 0.425 0.407 0.379 0.321 0.236 0.156
c = 0.500 | 0.500 0.500 0.500 0.500 0.500 0.500 0.500
c = 0.565 | 0.565 0.597 0.621 0.656 0.728 0.826 0.906
c = 0.700 | 0.700 0.784 0.837 0.901 0.974 0.999 1.000
上半张表(CoT 为什么有用):步数从 1 涨到 12,直接答的准确率从 0.55 崩到 0.0008,
加 CoT 只从 0.95 降到 0.54,倍数从 1.7 倍涨到 705 倍。
而且注意:$m=1$ 时倍数只有 1.7,这正好解释了为什么单步任务看不出 CoT 的好处。
CoT 不是提示词技巧,它改变的是模型需要在一层前向里完成多少计算。
变体:zero-shot CoT。Kojima et al.(Large Language Models are Zero-Shot Reasoners,
NeurIPS 2022, arXiv:2205.11916)发现,连例子都不用给,
只要在问题后面加一句 "Let's think step by step",就能拿到相当一部分 CoT 的收益。
这个发现的实用价值很高(省掉了写例子的功夫),但它也提示:
CoT 起作用的机制里,可能有相当一部分只是"让模型多生成一些 token"。
7.3 自洽性(self-consistency)
Wang, Wei, Schuurmans, Le, Chi, Narang, Chowdhery & Zhou,
Self-Consistency Improves Chain of Thought Reasoning in Language Models,
ICLR 2023, arXiv:2203.11171。
做法:不用贪婪解码取那一条推理链,而是采样 k 条不同的链,
对最终答案做多数投票。
为什么这在数学上会有用:如果一个复杂问题有多条推理路径都通向同一个正确答案,
那么"多条路径答案一致"就是一个置信度信号。
反过来,如果模型在瞎猜,k 条链会给出 k 个不同的答案,投票投不出什么。
如果每条链独立地以概率 $c$ 答对,$k$ 条链多数投票的准确率是
$$P_k = \sum_{i > k/2} \binom{k}{i}\, c^i (1-c)^{k-i}$$
这个公式是二项分布的上尾。它有一个著名的推论(孔多塞陪审团定理):
只要 $c > 0.5$,$k$ 越大 $P_k$ 越接近 1;只要 $c < 0.5$,$k$ 越大 $P_k$ 越接近 0。
下半张表(上一段代码的输出)把这个定理摊开了:
- $c=0.3$、$0.4$、$0.45$:票数越多,准确率越低。
$c=0.4$ 时 $k=101$ 只有 0.021。多数投票不能把"错的多数"变对。 - $c=0.5$:恒等于 0.5,投票完全无用(这正是定理的临界点)。
- $c=0.565$(就是 PaLM 在 GSM8K 上的 CoT 准确率):$k=5$ 时 0.621,$k=21$ 时 0.728。
- $c=0.7$:$k=21$ 就到 0.974。
现实检查(必须说清):上表假设 k 条链相互独立。
但真实模型的多条链共享同一批权重、同样的偏见,错误是相关的。
所以真实收益比这张表小得多。对照 Wang et al. 的实际报告:
GSM8K 上从 CoT 的 56.5% 提到 74.4%(+17.9 个点),
而按上表 $c=0.565$、$k=40$ 左右应该能到 0.85 以上。
差的那一截,就是"链之间不独立"吃掉的。
自洽性的一个硬限制:它投票投的是最终答案,
所以只适用于答案可以对齐的任务(数值、选项、代码输出)。
开放式生成(写文章、解释概念)没有"多数答案"可投。
7.4 真实 API 调用长什么样(本页不跑)
下面这段是真实会跑通的代码,但它需要你自己申请 API key 并且联网,
所以本页没有运行它,也不提供输出。
放在这里是为了让你看到"提示工程"落到代码上是什么样子。
它比你想的简单得多,全部的智力内容在 PROMPT 那个字符串里。
# ⚠️ 需要先 pip install openai,并设置环境变量 OPENAI_API_KEY(需要自己申请)
# ⚠️ 本页不跑这个块。你需要自备 key 才能运行。
from collections import Counter
from openai import OpenAI
client = OpenAI() # 从环境变量 OPENAI_API_KEY 读
PROMPT = """食堂有 23 个苹果,用掉 20 个,又买了 6 个,现在几个?
一步步算,最后一行写 "答案: <数字>"。
食堂有 23 个苹果。用掉 20 个,剩 23 - 20 = 3 个。
又买了 6 个,3 + 6 = 9 个。
答案: 9
小明有 15 块糖,给了弟弟 4 块,又买了 8 块,现在几块?
一步步算,最后一行写 "答案: <数字>"。"""
def ask(temperature):
r = client.chat.completions.create(
model="gpt-4o-mini", # 具体可用模型名请查官方文档,会变
messages=[{"role": "user", "content": PROMPT}],
temperature=temperature, # 见 §8
max_tokens=256,
)
return r.choices[0].message.content
# --- 贪婪解码取一条链 ---
greedy = ask(0.0)
print("贪婪解码:", greedy.strip().splitlines()[-1])
# --- 自洽性:采样 k 条链,对最后一行做多数投票 ---
k, answers = 10, []
for _ in range(k):
text = ask(0.8) # temperature 高一点,链才有差异
answers.append(text.strip().splitlines()[-1])
print("投票结果:", Counter(answers).most_common())
几个工程要点:
- 成本:自洽性是 $k$ 倍的成本。所以工业上通常取 $k=5\sim40$,不会无限加。
temperature必须调高,否则 k 条链完全一样,投票没意义(见 §8)。- 投票要对齐答案:真实代码里要用正则从最后一行抽出数字,
而不是直接比字符串,否则"答案是 9"和"答案: 9"会被当成两个不同答案。 - 模型名会变。上面写的
gpt-4o-mini只是我写这一页时的一个占位,
具体可用模型名一定要查当时的官方文档,这是这门课时效性最强的地方。
7.5 边界与批评
- CoT 的推理链不忠实。这是最重要的一条批评。
Turpin et al.(Language Models Don't Always Say What They Think,
arXiv:2305.04388, 2023)做了这样一个实验:
在提示里加入会影响答案的偏置线索(比如把选项顺序换掉),
模型的答案跟着变了,但它写出来的推理链完全不变,
它事后编了一条听起来合理的理由。
结论:CoT 让你"看到"的东西,不一定是模型"算出"的东西。
把 CoT 当成"模型的思维过程"来信任,是危险的。 - 提示的有效性不可移植。同一个提示,换个模型版本可能就失效了;
甚至同一模型的不同 API 版本之间都会变。
这在实验科学里是个严重问题:你的"方法"没法被复现。
所以凡是报告提示工程结果的论文,必须写清模型名、版本、日期、完整提示词。 - CoT 对不可分解的任务没用。"判断这句话是不是语法正确",
没有中间步骤可写。CoT 的收益来自"有中间结果可以落脚",
没有中间结果的任务,它帮不上忙。 - "Let's think step by step" 的机制没有定论。它可能真的在激发多步计算,
也可能只是让模型生成更多 token,从而获得了更多计算量。
这两种解释("推理" vs "更多算力")到今天还没有被干净地分开。
8 高效微调:LoRA 与量化
这一节回答一个非常实际的问题:我只有一张消费级显卡,能改造一个几十亿参数的模型吗?
(这一节是 * 创新创业思维的另一个落点:这两项技术是"个人/小团队做 AI 产品"的物质基础。)
8.1 全量微调为什么贵
微调一个模型,不只是存一份权重。以 Adam 优化器为例,训练时需要保存:
| 要存的东西 | 每参数字节数(混合精度常见配置) |
|---|---|
| 模型权重(fp16) | 2 |
| 梯度的 fp32 副本 | 4 |
| Adam 一阶矩(fp32) | 4 |
| Adam 二阶矩(fp32) | 4 |
| fp32 主权重 | 4 |
| 合计 | 约 18 字节/参数 |
对 1750 亿参数的 GPT-3,这大约是 3 TB 量级,远超任何单卡的显存。
即使不算优化器状态,光是把 175B 的 fp16 权重装进去就要约 350GB。
论文报告的实际数字是:LoRA 把 GPT-3 175B 的微调显存需求
从约 1.2 TB 降到约 350 GB(约 1/3),可训练参数降到约 0.01%。
还有一个更日常的痛点:如果你要微调 100 个不同的下游任务,
全量微调意味着存 100 份 175B 的模型。
LoRA 让你只存 100 份很小的适配器,底座共享。这是它被广泛采用的真正原因
(不只是省显存,更是省"版本管理")。
8.2 LoRA 的原理:低秩分解
Hu, Shen, Wallis, Allen-Zhu, Li, Wang, Wang & Chen,
LoRA: Low-Rank Adaptation of Large Language Models,
ICLR 2022, arXiv:2106.09685。
它基于一个观测:Aghajanyan et al.(Intrinsic Dimensionality Explains the
Effectiveness of Language Model Fine-Tuning, ACL 2021, arXiv:2012.13255)
发现,把一个预训练模型适配到下游任务时,真正需要的自由度远小于参数量,
他们估计微调的"内在维度"只有几百到几千维,
相对于上亿的参数,这是一个极小的子空间。
数学做法:设预训练权重 $W_0 \in \mathbb{R}^{d\times k}$。
全量微调学的是 $W_0 + \Delta W$,其中 $\Delta W$ 和 $W_0$ 一样大。
LoRA 的做法是把 $W_0$ 冻结,只让 $\Delta W$ 取一个低秩分解的形式:
$$\Delta W = B A,\qquad B\in\mathbb{R}^{d\times r},\quad A\in\mathbb{R}^{r\times k}, \qquad r \ll \min(d,k)$$
前向计算变成:
$$h = W_0 x + \Delta W x = W_0 x + \frac{\alpha}{r}\, B A x$$
逐项解释:
- $r$ 是秩,是你选的超参数。$d\times k$ 个参数被压成 $r(d+k)$ 个。
当 $d=k=12288$、$r=4$ 时:$1.51\times10^8 \to 98304$,降了 1536 倍。 - 为什么低秩就够了? 因为 §7.2 开头那个"内在维度"观测。
但要注意:"改动是低秩的"是一个假设,不是定理。§7.4 会实跑给你看假设不成立时会发生什么。 - 为什么 $B$ 初始化为 0,$A$ 用高斯随机?
因为这样训练开始时 $\Delta W = BA = 0$,模型和预训练模型逐位相同。
训练从"已知的好点"出发,而不是从一个随机扰动出发。
如果 A、B 都随机初始化,你会先破坏一个已经很好的模型,再试图修回来。
这个初始化方案(一半零、一半随机)是整个方法里最容易被忽略、但最关键的一步。 - $\alpha/r$ 这个缩放因子:$\alpha$ 是一个常数,最终乘在 $\Delta W$ 上。
为什么要除以 $r$?因为 $BA$ 的元素量级大致随 $r$ 增长,
如果不除,你每次改 $r$ 都得重新调学习率。除以 $r$ 之后,
换 $r$ 时超参数基本不用重调。论文说在 Adam 下,
调 $\alpha$ 大致等价于调学习率。 - 推理时的额外开销是零。因为训练完之后可以把
$W = W_0 + \frac{\alpha}{r}BA$ 直接合并成一个矩阵,
推理时和原模型一样快。这是 LoRA 相对 adapter、prefix-tuning 的关键优势。
后两者会在推理路径上增加计算。
实跑 1:低秩截断到底损失多少(用截断 SVD,它是 Frobenius 范数下的最优秩-$r$ 近似)
import numpy as np
np.set_printoptions(precision=4, suppress=True, linewidth=150)
rng = np.random.default_rng(3)
M = 256
W = rng.normal(size=(M, M))
U, s, Vt = np.linalg.svd(W, full_matrices=False)
print(" 秩 r 参数量 r(M+M) 压缩比 相对误差 ||W-W_r||_F/||W||_F")
Wf = np.linalg.norm(W, "fro")
for rr in [1, 2, 4, 8, 16, 32, 64, 128]:
Wr = (U[:, :rr]*s[:rr]) @ Vt[:rr]
print(" %4d %10d %5.1fx %10.5f"
% (rr, rr*2*M, M*M/(rr*2*M), np.linalg.norm(W-Wr, "fro")/Wf))
print("\n 误差的解析式核对:||W-W_r||_F = sqrt(sum_{i>r} sigma_i^2)")
for rr in [1, 4, 16, 64]:
Wr = (U[:, :rr]*s[:rr]) @ Vt[:rr]
print(" r=%3d 数值 %.6f 解析 %.6f 偏差 %.1e"
% (rr, np.linalg.norm(W-Wr, "fro"), np.sqrt((s[rr:]**2).sum()),
abs(np.linalg.norm(W-Wr, "fro") - np.sqrt((s[rr:]**2).sum()))))
d = 12288
print("\nGPT-3 一个 %dx%d 投影矩阵,全量参数 %.2f 亿:" % (d, d, d*d/1e8))
print(" 秩 r LoRA 参数量 r(d+d) 占全量的比例")
for rr in [1, 4, 16, 64]:
print(" %4d %14d %8.4f%%" % (rr, rr*(d+d), rr*(d+d)/(d*d)*100))
实跑输出
秩 r 参数量 r(M+M) 压缩比 相对误差 ||W-W_r||_F/||W||_F
1 512 128.0x 0.99249
2 1024 64.0x 0.98517
4 2048 32.0x 0.97073
8 4096 16.0x 0.94281
16 8192 8.0x 0.88949
32 16384 4.0x 0.78998
64 32768 2.0x 0.61392
128 65536 1.0x 0.32585
误差的解析式核对:||W-W_r||_F = sqrt(sum_{i>r} sigma_i^2)
r= 1 数值 253.466024 解析 253.466024 偏差 0.0e+00
r= 4 数值 247.909072 解析 247.909072 偏差 5.7e-14
r= 16 数值 227.162686 解析 227.162686 偏差 2.8e-14
r= 64 数值 156.786046 解析 156.786046 偏差 0.0e+00
GPT-3 一个 12288x12288 投影矩阵,全量参数 1.51 亿:
秩 r LoRA 参数量 r(d+d) 占全量的比例
1 24576 0.0163%
4 98304 0.0651%
16 393216 0.2604%
64 1572864 1.0417%
读法:
- 第一张表里那个 256×256 的随机矩阵是满秩的(所有奇异值都非零),
所以砍尾巴一定会掉信息:$r=1$ 时相对误差 0.99(基本什么都没留下),
$r=128$(一半)时还有 0.33。这是"最坏情况",
真实的微调改动不是随机的,它的奇异值衰减快得多,所以低秩近似好得多。 - 解析式核对那一节是全篇最干净的一次验证:误差范数精确等于
被丢掉的那些奇异值的平方和开根号(偏差 $10^{-14}$ 是浮点误差)。
这就是 Eckart-Young 定理的数值在场证明,也是"截断 SVD 是最优的"这句话的含义。 - 最后一段是真实规模:一个矩阵,$r=4$ 时只剩 0.065% 的参数。
实跑 2:真的训一遍,看 LoRA 什么时候成立、什么时候不成立
import numpy as np
rng = np.random.default_rng(3)
DIO = 20
W0 = rng.normal(size=(DIO, DIO))*0.5 # 冻结的"预训练权重"
def make_data(W_star, n=400):
X = rng.normal(size=(n, DIO))
return X, X @ W_star.T
mse = lambda Wm, X, Y: ((X@Wm.T - Y)**2).mean()
def train_full(X, Y, steps=3000, lr=0.02): # 全量微调:400 个参数都可训
Wm = W0.copy()
for _ in range(steps):
Wm -= lr*(2.0*(X@Wm.T - Y).T@X/len(X))
return Wm
def train_lora(X, Y, r, alpha=1.0, steps=4000, lr=0.02):
A = rng.normal(size=(r, DIO))*0.1 # A 用高斯初始化
B = np.zeros((DIO, r)) # B 初始化为 0(关键)
sc = alpha/r # 缩放因子 alpha/r
for _ in range(steps):
G = 2.0*(X@(W0 + sc*(B@A)).T - Y).T@X/len(X) # dL/dW,W = W0 + (a/r)BA
A -= lr*sc*(B.T@G)
B -= lr*sc*(G@A.T)
return W0 + sc*(B@A)
# 情形 1:真实改动是低秩的(LoRA 的假设成立)
dW_low = rng.normal(size=(DIO, 2)) @ rng.normal(size=(2, DIO))
X1, Y1 = make_data(W0 + dW_low)
print("【情形 1】真实改动 dW 的秩 = 2(低秩)")
print(" 全量微调 400 参数 测试 MSE = %.3e" % mse(train_full(X1, Y1), X1, Y1))
print(" LoRA r=2 80 参数 测试 MSE = %.3e" % mse(train_lora(X1, Y1, 2), X1, Y1))
# 情形 2:真实改动是满秩的(LoRA 的假设不成立)
X2, Y2 = make_data(W0 + rng.normal(size=(DIO, DIO))*0.5)
print("【情形 2】真实改动 dW 是满秩(LoRA 的假设不成立)")
print(" 全量微调 400 参数 测试 MSE = %.3e" % mse(train_full(X2, Y2), X2, Y2))
print(" LoRA r=2 80 参数 测试 MSE = %.3e" % mse(train_lora(X2, Y2, 2), X2, Y2))
print(" LoRA r=8 320 参数 测试 MSE = %.3e" % mse(train_lora(X2, Y2, 8), X2, Y2))
实跑输出
【情形 1】真实改动 dW 的秩 = 2(低秩)
全量微调 400 参数 测试 MSE = 2.036e-28
LoRA r=2 80 参数 测试 MSE = 2.160e-30
【情形 2】真实改动 dW 是满秩(LoRA 的假设不成立)
全量微调 400 参数 测试 MSE = 4.761e-29
LoRA r=2 80 参数 测试 MSE = 3.520e+00
LoRA r=8 320 参数 测试 MSE = 8.837e-01
三行数字说明两件事:
- 情形 1(改动低秩,即 LoRA 的假设成立):LoRA 用 80 个参数达到了
全量微调 400 个参数的效果,误差都在 $10^{-28}$ 量级(就是浮点精度本身)。
参数少 5 倍,结果一样。 - 情形 2(改动满秩,假设不成立):LoRA $r=2$ 的误差是 3.52,
而全量微调是 $4.8\times10^{-29}$。差了 28 个数量级。
把 $r$ 加到 8,误差降到 0.88,还是差得远。
所以 LoRA 的全部赌注就是"下游需要的改动是低秩的"。
论文的实证结论是:这个赌注在真实任务上通常成立($r=4\sim16$ 对多数任务够用),
优先适配 Query 和 Value 投影矩阵效果最好。但它不是定理,
如果任务需要模型学全新的知识(而不是调整已有的行为),LoRA 会明显不如全量微调。
8.3 量化的基本思想
目标:用更少的比特存权重。FP32 是 32 位,INT8 是 8 位(省 4 倍),INT4 是 4 位(省 8 倍)。
对称量化(最简单的一种):
$$\text{scale} = \frac{\max|W|}{2^{b-1}-1},\qquad W_q = \mathrm{round}\!\left(\frac{W}{\text{scale}}\right),\qquad W \approx W_q \cdot \text{scale}$$
逐项解释:$b$ 是位宽。$2^{b-1}-1$ 是正半轴上有多少个整数格
($b=8$ 时有 127 个,$b=4$ 时只有 7 个)。
scale 是"一个整数格代表多大的数值",由绝对值最大的那个权重决定。
这个依赖是量化所有麻烦的来源,下面会看到。
$\mathrm{round}$ 把浮点投到最近的整数格上。
实跑:
import numpy as np
rng = np.random.default_rng(3)
W = rng.normal(size=(20, 20))*1.5 # 待量化的权重
def q_tensor(Wm, b): # 整个矩阵共用一个 scale
q = 2**(b-1) - 1; sc = np.abs(Wm).max()/q
return np.round(Wm/sc).clip(-q, q)*sc
def q_channel(Wm, b): # 每个输出通道一个 scale
q = 2**(b-1) - 1; sc = np.abs(Wm).max(axis=1, keepdims=True)/q
return np.round(Wm/sc).clip(-q, q)*sc
def q_group(Wm, b, gs=4): # 每 4 个元素一组一个 scale
q = 2**(b-1) - 1; out = np.empty_like(Wm)
for j in range(0, Wm.shape[1], gs):
blk = Wm[:, j:j+gs]; sc = np.abs(blk).max(axis=1, keepdims=True)/q
out[:, j:j+gs] = np.round(blk/sc).clip(-q, q)*sc
return out
rel = lambda A, Bm: np.linalg.norm(A-Bm)/np.linalg.norm(A)*100
print(" 位宽 b 每-tensor 每-通道 每-4元素组 量化级数 2^(b-1)-1")
for b in [8, 4, 3, 2]:
print(" %4d 位 %7.2f%% %7.2f%% %9.2f%% %10d"
% (b, rel(W, q_tensor(W, b)), rel(W, q_channel(W, b)),
rel(W, q_group(W, b)), 2**(b-1)-1))
W_out = W.copy(); W_out[0, 0] = np.abs(W).max()*30 # 人为加一个 30 倍离群值
print("\n 加一个 30 倍离群值后的 INT4 误差:")
print(" 每-tensor %.2f%% 每-通道 %.2f%% 每-4元素组 %.2f%%"
% (rel(W_out, q_tensor(W_out, 4)), rel(W_out, q_channel(W_out, 4)),
rel(W_out, q_group(W_out, 4))))
实跑输出
位宽 b 每-tensor 每-通道 每-4元素组 量化级数 2^(b-1)-1
8 位 0.75% 0.52% 0.34% 127
4 位 14.29% 9.76% 6.00% 7
3 位 32.01% 21.09% 13.02% 3
2 位 82.49% 61.60% 40.16% 1
加一个 30 倍离群值后的 INT4 误差:
每-tensor 19.73% 每-通道 5.37% 每-4元素组 2.86%
读法:
- INT8 是几乎免费的:三种粒度误差都小于 1%。这是今天 INT8 推理
普遍可用的原因。 - INT4 的粒度是关键:整块共用一个 scale 时误差 14%,
分成 4 个元素一组后降到 6%。粒度越细,误差越小,
这就是为什么真实的量化方法都用 group-wise(通常组大小 64 或 128)。 - 离群值是真正的敌人。我人为把一个权重放大 30 倍,
per-tensor 的 INT4 误差就从 14.29% 涨到 19.73%。
一个数毁掉整个矩阵。现代量化方法(GPTQ、AWQ 等)的核心工作就是处理离群值。 - 最后必须说清楚:上表测的是权重空间的相对误差。
权重误差小不等于任务精度不掉。两者之间的映射依赖模型、任务、
校准数据的分布。所以工程上从不只看这个数,而是直接跑评测集,看掉几个点。
量化为什么对推理特别重要:大模型推理的主要瓶颈通常不在算力,而在
显存带宽,每生成一个 token,都要把全部权重从显存读一遍。
把权重从 FP16 压到 INT4,要搬的数据就少了 4 倍,速度直接受益。
(这也是为什么"量化"和"LoRA"经常一起用:
LoRA 让你训得起,量化让你跑得动。)
8.4 边界与批评
- LoRA 的赌注是"改动低秩"。实跑情形 2 展示了赌注错的时候会差到什么程度。
实践判据:如果下游任务是"调整已有能力"(改风格、调格式、加领域术语),
LoRA 通常够用;如果是"教一个全新的、训练数据里没有的事实性知识",LoRA 常不如全量微调。 - LoRA 的超参数($r$、$\alpha$、加在哪些层上)没有理论指导。
论文给的只是经验建议($r=4\sim16$、优先 Q/V)。 - 量化到 4 位以下通常需要额外的努力:量化感知训练(QAT)、
或者精心的校准数据集。用随机数据做校准的 INT4,效果可能明显更差。 - 量化的评测陷阱:一个模型"INT4 后只掉 0.5 个点",
这个结论强依赖评测集。在推理任务上掉得多、在分类任务上掉得少,是常见的。
报告量化结果必须同时报告评测集和校准方式。
9 推理与解码(temperature / top-k / top-p)
9.1 自回归生成:为什么解码策略是一等公民
语言模型每一步的输出是整个词表上的一个概率分布,而不是单个 token
(见《自然语言处理》§3.1、§3.5)。生成一个回答的过程是:
[ 提示 ] → 模型 → 分布 p(·) → 【选一个 token】→ 接回输入 → 再跑一次
↑
这一步就是"解码策略"
所以"模型说了什么"从来不只是模型的事。同一份权重、同一个提示,
换一个解码策略,输出可以完全不同。§8 剩下的部分全部在讲怎么选那个 token。
9.2 temperature(温度)
不要把 logits 直接 softmax。先把它们同除以一个数 $T$:
$$p_i = \frac{\exp(z_i/T)}{\sum_j \exp(z_j/T)}$$
等价形式(设 $p^{(1)}_i$ 是 $T=1$ 时的分布):
$$p_i \;\propto\; \big(p^{(1)}_i\big)^{1/T}$$
三种情形:
- $T = 1$:原分布,不动。
- $T < 1$:指数 $1/T > 1$,分布变尖。$T\to0^+$ 时,
概率全部集中到最大的那个 logit 上,这就是贪婪解码。 - $T > 1$:指数 $1/T < 1$,分布变平。$T\to\infty$ 时趋近均匀分布。
数学性质:$T$ 不改变概率的排序(单调变换),只改变"陡峭度"。
它把熵单调地拉高或压低。
(三种解码的实跑在 §8.5,那里有一段代码把温度、top-k、top-p 并排跑出来。)
9.3 top-k:固定留 k 个
定义:按概率从大到小排序,只保留最大的 $k$ 个,
把其余的置零,然后在保留的这 $k$ 个上重新归一化。
边界情形:$k=1$ 就是贪婪解码;$k=V$(词表大小)就是原分布,什么也没做。
9.4 top-p(核采样,nucleus sampling)
定义:按概率从大到小排序,累加,取最小的那个集合 $S$,
使得累计概率 $\sum_{i\in S} p_i \ge p$;然后把 $S$ 之外的置零,在 $S$ 内重新归一化。
$$\text{找最小的 } S:\quad \sum_{i\in S}p_i \ge p,\qquad q_i = \frac{p_i}{\sum_{j\in S}p_j}\;\;(i\in S),\qquad q_i=0\;(i\notin S)$$
和 top-k 的关键差别:保留的个数不固定,由分布的形状决定。
分布很尖时,一两个 token 就凑够 0.9 了;分布很平时,需要留十几个。
9.5 把四种解码并排跑一遍
import numpy as np
TOK = ["好", "不错", "晴朗", "糟糕", "冷", "热", "一般", "适合", "出门", "下雨"]
sm = lambda z: (lambda e: e/e.sum())(np.exp(z - z.max()))
ent = lambda p: float(max(0.0, -(p[p > 0]*np.log(p[p > 0])).sum()))
def show(nm, p):
print(" %-7s" % nm, " ".join("%s:%.4f" % (TOK[i], p[i]) for i in range(10)))
print(" %-7s 熵 H = %.4f nats,最大概率 = %.4f" % ("", ent(p), p.max()))
def topk(p, k):
idx = np.argsort(-p)[:k]; q = np.zeros_like(p); q[idx] = p[idx]; return q/q.sum()
def topp(p, th):
o = np.argsort(-p); c = np.cumsum(p[o]); n = int(np.searchsorted(c, th)+1)
q = np.zeros_like(p); q[o[:n]] = p[o[:n]]; return q/q.sum(), n
logits = np.array([3.2, 2.6, 2.1, 1.4, 1.0, 0.8, 0.5, 0.3, 0.1, -0.4])
p0 = sm(logits)
print("[temperature] p_i ∝ p_i^(1/T)")
show("T=1.0", p0)
for T in [0.3, 0.5, 0.8, 1.5, 3.0, 10.0]:
show("T=%.1f" % T, sm(logits/T))
print("\n[top-k] 只留最大的 k 个再归一化")
for k in [1, 2, 3, 5]:
show("k=%d" % k, topk(p0, k))
print("\n[top-p] 留累计概率刚过 p 的那一撮")
for th in [0.5, 0.8, 0.9, 0.95, 0.99]:
q, n = topp(p0, th)
show("p=%.2f" % th, q); print(" %-7s 保留 %d 个 token" % ("", n))
print("\n[尖峰 vs 平坦]")
peak = sm(np.array([6., 1., .5, 0., -.5, -1., -1.5, -2., -2.5, -3.]))
flat = sm(np.array([1.]*10))
for nm, p in [("尖峰", peak), ("平坦", flat)]:
qk = topk(p, 3); qp, n = topp(p, 0.9)
print(" %s分布:top-k=3 最大概率 %.4f 熵 %.4f | top-p=0.9 留 %d 个 最大概率 %.4f 熵 %.4f"
% (nm, qk.max(), ent(qk), n, qp.max(), ent(qp)))
print("\n[采样] 同一分布、同一随机种子,各采 20 次")
for nm, p in [("T=1.0", p0), ("T=0.5", sm(logits/0.5)), ("T=1.5", sm(logits/1.5)),
("top-k=3", topk(p0, 3)), ("top-p=0.9", topp(p0, 0.9)[0])]:
idx = np.random.default_rng(0).choice(10, size=20, p=p)
print(" %-9s" % nm, " ".join(TOK[i] for i in idx))
实跑输出
[temperature] p_i ∝ p_i^(1/T)
T=1.0 好:0.4093 不错:0.2246 晴朗:0.1362 糟糕:0.0677 冷:0.0454 热:0.0371 一般:0.0275 适合:0.0225 出门:0.0184 下雨:0.0112
熵 H = 1.7256 nats,最大概率 = 0.4093
T=0.3 好:0.8587 不错:0.1162 晴朗:0.0219 糟糕:0.0021 冷:0.0006 热:0.0003 一般:0.0001 适合:0.0001 出门:0.0000 下雨:0.0000
熵 H = 0.4863 nats,最大概率 = 0.8587
T=0.5 好:0.6802 不错:0.2049 晴朗:0.0754 糟糕:0.0186 冷:0.0084 热:0.0056 一般:0.0031 适合:0.0021 出门:0.0014 下雨:0.0005
熵 H = 0.9683 nats,最大概率 = 0.6802
T=0.8 好:0.4909 不错:0.2319 晴朗:0.1241 糟糕:0.0517 冷:0.0314 热:0.0244 一般:0.0168 适合:0.0131 出门:0.0102 下雨:0.0055
熵 H = 1.5003 nats,最大概率 = 0.4909
T=1.5 好:0.2931 不错:0.1965 晴朗:0.1408 糟糕:0.0883 冷:0.0676 热:0.0592 一般:0.0485 适合:0.0424 出门:0.0371 下雨:0.0266
熵 H = 2.0185 nats,最大概率 = 0.2931
T=3.0 好:0.1840 不错:0.1507 晴朗:0.1275 糟糕:0.1010 冷:0.0884 热:0.0827 一般:0.0748 适合:0.0700 出门:0.0655 下雨:0.0554
熵 H = 2.2303 nats,最大概率 = 0.1840
T=10.0 好:0.1219 不错:0.1148 晴朗:0.1092 糟糕:0.1018 冷:0.0978 热:0.0959 一般:0.0930 适合:0.0912 出门:0.0894 下雨:0.0850
熵 H = 2.2964 nats,最大概率 = 0.1219
[top-k] 只留最大的 k 个再归一化
k=1 好:1.0000 不错:0.0000 晴朗:0.0000 糟糕:0.0000 冷:0.0000 热:0.0000 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 0.0000 nats,最大概率 = 1.0000
k=2 好:0.6457 不错:0.3543 晴朗:0.0000 糟糕:0.0000 冷:0.0000 热:0.0000 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 0.6501 nats,最大概率 = 0.6457
k=3 好:0.5314 不错:0.2917 晴朗:0.1769 糟糕:0.0000 冷:0.0000 热:0.0000 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 1.0018 nats,最大概率 = 0.5314
k=5 好:0.4634 不错:0.2543 晴朗:0.1543 糟糕:0.0766 冷:0.0514 热:0.0000 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 1.3422 nats,最大概率 = 0.4634
[top-p] 留累计概率刚过 p 的那一撮
p=0.50 好:0.6457 不错:0.3543 晴朗:0.0000 糟糕:0.0000 冷:0.0000 热:0.0000 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 0.6501 nats,最大概率 = 0.6457
保留 2 个 token
p=0.80 好:0.4885 不错:0.2681 晴朗:0.1626 糟糕:0.0808 冷:0.0000 热:0.0000 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 1.2015 nats,最大概率 = 0.4885
保留 4 个 token
p=0.90 好:0.4447 不错:0.2441 晴朗:0.1480 糟糕:0.0735 冷:0.0493 热:0.0403 一般:0.0000 适合:0.0000 出门:0.0000 下雨:0.0000
熵 H = 1.4571 nats,最大概率 = 0.4447
保留 6 个 token
p=0.95 好:0.4218 不错:0.2315 晴朗:0.1404 糟糕:0.0697 冷:0.0467 热:0.0383 一般:0.0283 适合:0.0232 出门:0.0000 下雨:0.0000
熵 H = 1.6206 nats,最大概率 = 0.4218
保留 8 个 token
p=0.99 好:0.4093 不错:0.2246 晴朗:0.1362 糟糕:0.0677 冷:0.0454 热:0.0371 一般:0.0275 适合:0.0225 出门:0.0184 下雨:0.0112
熵 H = 1.7256 nats,最大概率 = 0.4093
保留 10 个 token
[尖峰 vs 平坦]
尖峰分布:top-k=3 最大概率 0.9893 熵 0.0663 | top-p=0.9 留 1 个 最大概率 1.0000 熵 0.0000
平坦分布:top-k=3 最大概率 0.3333 熵 1.0986 | top-p=0.9 留 10 个 最大概率 0.1000 熵 2.3026
[采样] 同一分布、同一随机种子,各采 20 次
T=1.0 晴朗 好 好 好 糟糕 热 不错 晴朗 不错 一般 糟糕 好 冷 好 晴朗 好 冷 不错 好 不错
T=0.5 好 好 好 好 不错 晴朗 好 不错 好 晴朗 不错 好 不错 好 不错 好 不错 好 好 好
T=1.5 糟糕 好 好 好 热 适合 晴朗 冷 晴朗 适合 热 好 一般 好 冷 好 一般 晴朗 不错 不错
top-k=3 不错 好 好 好 不错 晴朗 不错 不错 不错 晴朗 不错 好 晴朗 好 不错 好 晴朗 不错 好 好
top-p=0.9 不错 好 好 好 晴朗 冷 不错 晴朗 不错 冷 晴朗 好 糟糕 好 晴朗 好 糟糕 不错 好 好
逐段读:
- temperature:$T=1$ 时最大概率 0.4093,熵 1.7256。
$T=0.3$ 时最大概率涨到 0.8587,熵掉到 0.4863,分布变得非常确定。
$T=10$ 时最大概率只有 0.1219,熵 2.2964,几乎均匀了(均匀分布的熵是 $\log 10 = 2.3026$)。
注意排序从头到尾没变过("好"永远是第一),变的只是陡峭度。 - top-k:$k=1$ 就是贪婪,熵 0。$k$ 从 1 加到 5,最大概率从 1.0 降到 0.4634,熵从 0 涨到 1.3422。
- top-p:$p=0.5$ 时只留 2 个;$p=0.99$ 时留 10 个(全部)。
保留的个数随阈值变化,这就是它的自适应之处。 - 尖峰 vs 平坦,这一段是 top-p 存在的理由。
- 尖峰分布下:top-k=3 保留了三个 token,最大概率 0.9893(还行);
top-p=0.9 只留 1 个,直接变成确定输出。
- 平坦分布下:top-k=3 把 10 个 token 砍到 3 个,
丢弃了约 70% 的概率质量,最大概率被硬抬到 0.3333,熵从 2.3026 掉到 1.0986。
而 top-p=0.9 保留了全部 10 个,因为均匀分布里每一个都"需要"才能凑够 0.9。
- 同一个 $k=3$,在两种分布下一个合适、一个灾难。 top-p 不会犯这个错,
所以它是今天更常用的默认选择。 - 采样:同一份分布、同一个随机种子,只换解码参数,20 次采样的结果完全不同。
$T=1.5$ 时出现了 5 次"糟糕"和"适合",而 $T=0.5$ 时几乎全是"好"。
这就是"模型的输出不完全由模型决定"的直观样子。
9.6 边界与批评
- 贪婪解码不等于"最可能的整句"。每一步选最可能的 token,
得到的不是全局概率最高的那个序列。这个反例很容易构造
(第 1 步的次优选择可能让第 2、3 步的概率高得多)。
beam search 是对这件事的一个补救,但它也只是近似,
而且会导致输出更"平庸"(因为高概率的序列往往是套话)。这是一个真正的取舍。 - 解码参数不可跨模型移植。同一个 $T=0.7$,在不同模型上的效果不一样,
因为不同模型的 logit 尺度不同。换模型必须重调。 - 评测必须报告解码参数。一个模型报"准确率 85%",
如果不说 $T$、top-p、beam 宽度,这个数字不可复现。
这是这门课和《实验方法论》最直接的一个接口:
你的实验方法必须包含生成配置。 - 低 temperature 不等于"更正确"。$T\to0$ 只是"更确定"。
如果模型本来就错,降低温度只会让它更自信地错。
这一点在需要多样性的任务(头脑风暴、写多个方案)上尤其重要。
那时候你需要高温度。
截至本书写作时:temperature/top-k/top-p 是 2020 年以来的标准配置,
但解码策略本身仍在演化(min-p、typical sampling、
以及"推理模型"用更长的链而不是更好的采样)。
具体哪种是当前最优,我不下结论。
10 局限与风险
这一节要诚实。前面八节讲了大模型能做什么,这一节讲它在什么条件下会坑你。
每一条都会写清:机制是什么、现在的缓解手段是什么、能不能根治。
10.1 幻觉(hallucination)
定义:生成流畅、自信、但没有事实依据的内容。
综述见 Ji et al., Survey of Hallucination in Natural Language Generation,
ACM Computing Surveys 55(12):248, 2023, arXiv:2202.03629。该综述把幻觉分成两类:
- 内在幻觉:输出与给定的源材料矛盾(摘要里出现了原文没有的说法)。
- 外在幻觉:输出无法从源材料验证(既不能证实也不能证伪)。
为什么必然发生(三个机制,都是结构性的):
- 训练目标是"预测下一个词",不是"说真话"。
一个高频出现的错误说法,和一个正确说法,在"下一个词预测"这个目标下
没有本质区别,只要它在训练数据里出现得够多,模型就会高概率地生成它。
流畅性和真实性是两个不同的轴,而模型只被优化了前者。 - 模型没有"我不知道"这个默认输出。它每一步必须从词表里吐一个分布出来。
面对一个它从没见过的实体,它不会沉默,它会生成一个统计上像答案的东西。
(这一点在 §4 之后更严重:RLHF 训练出的助手倾向于给出一个确定的回答,
因为标注员偏好确定的回答。) - 知识存在权重里,是统计性的、无溯源的。模型无法告诉你
"这句话来自训练数据的哪一段",所以也没有办法自查。
能不能根治:不能。工程上的缓解手段有:
- RAG(检索增强生成):先把相关文档检索出来放进上下文,让模型"照着材料说"。
这能大幅降低幻觉,但不能消除,模型仍可能误读或超出材料(外在幻觉)。
- 要求引用:让模型给出出处。这有用,但模型编造出处的情况也被反复报告过。
- 工具调用:算数交给计算器、事实查询交给搜索。这是目前最有效的一类手段,
因为它把"生成"换成了"执行"。
一条重要的提醒:模型自报的置信度不能当作可靠信号。
模型给出的概率(或它在回答里表现出的笃定程度)与它实际正确的概率之间,
校准(calibration)并不好。所以"模型很确定"不能作为"它是对的"的证据。
10.2 知识截止(knowledge cutoff)
机制:权重在训练结束后就冻结了。之后世界上发生的事,
模型在参数里完全没有任何信息。
为什么这比听起来更麻烦:因为模型不会主动告诉你它不知道,
见 §9.1 机制 2。对于一个截止日期之后的事件,它会流畅地编一个听起来合理的答案。
还有一层容易忽略的:知识截止不是某一天。
它是一个统计上的模糊边界。训练数据的采集有滞后,
不同来源(网页、书、代码)的截止时间不同,而且数据在训练集里的采样权重不同。
所以"模型的截止日期是 2023 年 10 月"这种说法本身就是个近似。
缓解:RAG、联网搜索。这个问题在实践中基本被"给模型接一个检索器"解决了大半,
但代价是:模型不再是一个自足的、离线的系统,
它的回答质量开始依赖检索到的内容的质量。
10.3 上下文长度
三层问题,一层比一层隐蔽:
- 名义长度 ≠ 有效长度。一个模型标称支持 128k token 上下文,
不代表它能把 128k 里的信息都用上。
Liu et al.(Lost in the Middle: How Language Models Use Long Contexts,
TACL 12:157–173, 2024, arXiv:2307.03172)发现:
把关键信息放在上下文的开头或结尾时,模型表现好;
放在中间时,表现显著变差,曲线呈 U 形。
所以"信息放进上下文"不等于"模型会用到它"。 - 计算代价是平方的。自注意力的计算量随序列长度平方增长
(见《深度学习》§8.2 的注意力矩阵)。长上下文的推理成本因此增长很快。
(现代实现用 KV cache、滑窗注意力、稀疏注意力等手段缓解,
但基本约束还在;这些是工程技巧,不改变注意力的数学形式。) - 上下文里的内容会互相干扰。上下文塞得越满,
模型的注意力被稀释得越厉害。更多的上下文不总是更好。
10.4 对齐问题
- 奖励黑客(reward hacking)。§4.4 已经实跑演示过:
$\beta$ 太小的时候,策略会完全崩成一个确定性输出,而"奖励"这个数字看起来完美。
机制见 §4.5。缓解手段就是 KL 罚项本身,它是个刹车,不是解决方案。 - 迎合(sycophancy)。因为标注员偏好"让人舒服的回答",
优化人类偏好会顺带优化出迎合行为。这是优化目标的直接后果,不是 bug。
(具体权威文献我不列,这一块的命名和研究还在演化,
我不敢给一个确定的出处。见 §12 的诚实清单。) - "对齐"对的是谁? 这是最根本的问题。
标注员的偏好 ≠ 全体人类的利益 ≠ 某个具体用户当下想要的东西。
这是一场价值争论,不是一个技术问题。技术能做的只是
"把某些人的偏好更精确地实现出来"。 - 对齐不是一次性完成的。模型在更新、用户在变、攻击面在变,
所以对齐是一个持续的过程。 - 经典的问题清单:Amodei et al., Concrete Problems in AI Safety,
arXiv:1606.06565, 2016,reward hacking、副作用、
可扩展监督等问题在这篇里被系统提出,到今天仍然有效。
10.5 一份诚实的清单
| 风险 | 机制 | 现有缓解手段 | 能否根治 |
|---|---|---|---|
| 幻觉 | 目标是"预测下一个词";没有"我不知道"输出;无溯源 | RAG、引用、工具调用 | 不能,只能压低 |
| 知识截止 | 权重冻结;截止边界是统计性的、模糊的 | RAG、联网搜索 | 能"绕过",但模型不再自足 |
| 上下文长度 | 中间信息被忽略(U 形);注意力 $O(n^2)$ | 长上下文训练、稀疏注意力 | 部分;有效长度仍是短板 |
| 奖励黑客 | 奖励模型是偏好的有损代理 | KL 罚项、更大更好的奖励模型 | 不能,是结构性的 |
| 迎合 | 标注员偏好"让人舒服的回答" | 无公认的有效手段 | 未知 |
| 不可复现 | 提示/解码参数/模型版本都会变 | 完整报告配置(这是最实际的) | 能,靠规范,不靠技术 |
这一节的每一条都源于同一个原因,目标函数本身就在这么做。把目标定成"预测下一个词 + 让人打高分",
就必然会得到"流畅、自信、讨人喜欢、偶尔编造"的系统。
要改变这些性质,得改变目标,而不是改变规模。
11 和你专业的关系
这一节是这一页对认知科学专业学生的回报:前面八节的技术,
到这里全部变成关于语言的科学证据(或者不是证据)。
11.1 大模型作为"语言习得的反例"
先摆出被挑战的两条经典论断(见《心理语言学》与《语言学概论》):
- 输入贫乏论证(poverty of the stimulus)。乔姆斯基传统认为:
儿童接触到的语言输入不足以唯一确定语法,
所以语法知识中必然有先天的成分。
另一个相关论点是:儿童只听到正例(合法的句子),
几乎得不到负面证据("这句话不合法"这个信息),
那他们怎么知道哪些句子不能说? - 关键期假设。Lenneberg 等人在 1960 年代提出:
语言习得有一个时间窗,错过之后难以完全习得。
大模型看起来同时否定了这两条:
- 它只从正例学(自回归目标只要求"预测下一个词",没有任何"这句话不合法"的信号),
却学出了语法。 - 它没有关键期,什么时候开始训都行,还可以继续预训练。
但是,不要让论证停在这里。三条必须说清的限定:
限定一:输入量的量级完全不同,这不是可比的实验。
儿童在 4 岁前累计接触的词量,量级在 $10^7 \sim 10^8$;
GPT-3 训练了 $3\times10^{11}$ 个 token(换算成词也还是 $10^{11}$ 量级)。
两边差 3~4 个数量级。
(我给的是量级,不是精确值,具体数字取决于如何统计"输入"
(词还是 token?听到的还是接触到的?),不同研究给出的估计差异很大,
而且这个领域里某些被广泛引用的结论(比如"词汇量差距")已经被后续工作质疑过。
所以我不引用任何具体的差距数字。)
关键在于:"模型能学会"和"儿童能学会"是两个输入规模差了几个数量级的现象。
把它们直接对比、然后得出"儿童也不需要先天语法",是跨过了最关键的变量。
限定二:模型展示的是"形式能力",不是"功能能力"。
Mahowald et al.(Dissociating language and thought in large language models,
Trends in Cognitive Sciences 28(6):517–540, 2024)提出这个二分:
- 形式语言能力(formal competence):产出和判断合法句子的能力。
- 功能语言能力(functional competence):把语言用在真实交际和世界知识里的能力
(理解言外之意、指称真实世界、推理说话人的意图)。
模型在第一条上表现很好,在第二条上表现明显较差。"它学会了语法"
只覆盖了第一条。认知科学关心的很多东西在第二条里。
限定三(最重要,也最容易被忽略):没有关键期,可能只是因为"没有发育"。
关键期在生物学上不是凭空的一个时间窗,它与
突触修剪、髓鞘化、神经发育这些过程绑定。
而大模型是固定架构、参数量不变的一个优化过程,
它本来就不该被期待出现关键期。
所以"大模型没有关键期"并不构成对关键期假设的反驳,
它只是说"这个系统和大脑的机制不一样",而这一点我们从一开始就知道。
把"模型没有关键期"当成"儿童的关键期是错觉",是一个范围错误(scope error)。
一个正在做的直接检验:既然"输入量"是关键变量,
那就用儿童规模的输入量去训模型,看能学到多少语法。
这个方向有一个公开的竞赛(BabyLM Challenge),
(我记得是 Warstadt 等人 2023 年组织的,但 arXiv 编号我不确定,标"出处待查")。
这类工作的思路正好是"控制输入量这个变量",是最值得跟的后续方向。
一个重要的相关立场:Piantadosi & Hill(Meaning without reference in
large language models, arXiv:2208.02957, 2022)主张,
一个足够好的语言模型可以从形式中恢复出大量世界结构,
所以"没有指称就没有意义"这个论断下得太早了。
反方立场见 Bender & Koller(Climbing towards NLU,
ACL 2020, arXiv:2005.04118)。
Frank(Baby steps in evaluating the capacities of large language models,
Nature Reviews Psychology 2:451–452, 2023)的立场是:
评测要"一小步一小步来",不要用笼统的"理解/不理解"下结论。
11.2 模型-脑对齐(model-brain alignment)
思路:把模型的内部表示和脑活动(fMRI、EEG/MEG)对齐,
看模型的哪一层、哪种表征最能预测脑响应。
已发表的两条关键结果(《自然语言处理》§8.1 已引用,这里只讲它们对本节的意义):
- Schrimpf et al.(The neural architecture of language,
PNAS 118(45), 2021):在一个统一的框架下,
"预测下一个词"这个目标产生的表征能同时解释脑电和 fMRI 数据。 - Goldstein et al.(Shared computational principles for language processing
in humans and deep language models, Nature Neuroscience 25:369–380, 2022):
模型预测脑活动的能力随模型规模提升,更大的语言模型预测得更好。
第二条对本课特别有意义:它把 §2 的规模定律和 §10.2 连起来了。
"模型-脑对齐度随规模提高"意味着,
规模带来的不只是任务分数,还有"更像大脑"这个属性。
但必须保留三条:
- 对齐性好 ≠ 机制相同。两个系统可以给出相同的预测、
却用完全不同的内部计算。这正是《深度学习》§10.3 讨论过的那个争议。 - "更强的模型更匹配大脑"可以有很平庸的解释:
更强的模型对语言的统计结构拟合得更好,
而大脑本来就在拟合这个统计结构。这不必然说明模型"像大脑"。 - 这条研究线的价值恰恰在于它可以被证伪。你可以设计实验,
让"机制相同"和"机制不同"两类模型给出不同的神经预测,然后去测。
这是这个领域里少有的、能真正推进问题的做法。
11.3 它到底"懂"语言吗
这是认知科学最前沿的问题之一。本页不给结论,只给三方的立场和一个操作建议。
| 立场 | 代表 | 核心主张 |
|---|---|---|
| 形式 ≠ 意义 | Bender & Koller 2020 | 只从形式训练的系统学不到"指称"(语言与世界的连接) |
| 意义可以从形式中恢复 | Piantadosi & Hill 2022 | 足够好的世界模型可以从语言里恢复出大量世界结构 |
| 拆开来看 | Mahowald et al. 2024 | 模型强在形式语言能力,弱在需要世界模型和语用的功能性任务 |
操作建议(这一条比结论有用):遇到"它到底懂不懂 X",
先把它拆成一个可操作的子问题。
- 不能验证的问法:"它真的理解'因果'吗?"
- 能验证的问法:"在这个因果推理任务上,它的错误模式
和人类的哪一个已知机制一致?(还是和某个更简单的启发式一致?)"
这和你在《实验方法论》学的第一件事是一样的:把一个笼统的问题
变成一个能出对错的具体假设。
12 习题(带答案)
怎么用:先盖住答案在纸上做。第 3、4、7 题必须能手推(数字都是我实跑核对过的)。
第 8 题没有标准答案,但必须写出可检验的方案。
12.1 第 1 题(规模定律的算术)★
问:一个团队有 $C = 3.15\times10^{24}$ FLOPs 的预算(是 GPT-3 的 10 倍)。
(a) 按 Kaplan 口径,最优的 $N$ 和 $D$ 是多少?
(b) 按 Chinchilla 口径呢?
(c) 已知 $C=3.15\times10^{23}$ 时 Kaplan 口径的最优是 $N=137$B、$D=383$B。
用这两个点验证一下 Kaplan 口径下 $N$ 的指数,并说明这个指数说明了什么。
答案:
(b) Chinchilla 先算(因为它有闭式解,最简单):
由 $N^* = \sqrt{C/120}$ 和 $D^* = 20N^*$:
$$N^* = \sqrt{3.15\times10^{24}/120} = \sqrt{2.625\times10^{22}} \approx 1.62\times10^{11} = 162\text{B}$$
$$D^* = 20 \times 162\text{B} = 3.24\text{T tokens}$$
(a) Kaplan 口径:$N \propto C^{0.556}$(§2.4 实跑拟合出来的)。于是
$$\frac{N_2}{N_1} = 10^{0.556} \approx 3.60 \;\Longrightarrow\; N_2 = 137\text{B} \times 3.60 \approx 493\text{B}$$
同法 $D \propto C^{0.444}$:$D_2 = 383\text{B}\times 10^{0.444} \approx 383\times2.78 \approx 1066\text{B}$。
(这两个数我用 §2 的代码实跑核对过:$N=492.6$B、$D=1066$B。)
(c) 用两个算力点反推指数(指数 = 对数斜率):
$$\frac{\log(N_2/N_1)}{\log(C_2/C_1)} = \frac{\log(493/137)}{\log(10)} = \frac{\ln 3.599}{\ln 10} = \frac{1.281}{2.303} \approx 0.556$$
核对通过($\log$ 用自然对数;换任何底数比值都一样)。
$0.556$ 明显大于 $0.5$,说明 Kaplan 口径下参数应该涨得比数据快
这就是它和 Chinchilla 的核心分歧。
但要在这里加一个方法论提醒:这个"验证"其实是循环的。
$493$ 这个数是我用 $0.556$ 算出来的,再反推当然得到 $0.556$。
要检验一个外推公式,必须在公式没用过的数据点上测。
§2.4 的实跑用的是有限差分(在 $C=10^{24}$ 附近取两个很近的点),
那是在没有预先假设指数的前提下把斜率量出来的。
这才算验证。这个坑在所有"拟合 + 外推"的工作里都存在。
结论:Kaplan 口径下 $N \approx 493$B、$D \approx 1066$B($D/N \approx 2.2$);
Chinchilla 口径下 $N \approx 162$B、$D \approx 3.2$T($D/N = 20$)。
模型大小差 3 倍,数据量差 3 倍,而数据参数比 $D/N$ 差了将近 9 倍
($2.2$ vs $20$),这个比值才是两派分歧的实质。
它不是学术细节,它直接决定你花几千万买什么。
12.2 第 2 题(为什么两派结论不同)★★
问:Kaplan 和 Chinchilla 用的是同样的数学形式(都是幂律),
为什么得出相反的最优分配?这个分歧是数学问题还是实验问题?
并说明"外推"的前提是什么。
答案:
是实验问题,不是数学问题。两篇用的都是幂律,形式几乎一样。
分歧出在数据点的质量上。Kaplan 的实验里,
不同规模的模型用了同一套学习率调度,
而观测时有些模型并没有跑到"各自配置下能到达的最优状态",
损失被高估了。这个偏置在小模型上更严重,
于是拟合出来的曲线把最优点推向了大模型那一侧。
Chinchilla 把每个规模都训到各自的最优(这也是它要花更多算力的原因),
最优点就移回了"参数和数据等比例缩放"。
外推的前提是:每一个数据点本身都是"该配置下能做到的最好"。
如果你拿一堆"没调好"的配置去拟合趋势,你拟合的是
"你的调参水平的趋势",不是"规模的趋势"。
这个教训对所有实验科学都成立,对照必须匹配,每个条件都要跑到自己的最优。
一个补充:这个解释本身也有后续讨论。
我在本页(§2.4)复算时还发现,Kaplan 论文的联合式解出的指数(0.556)
和它正文口径(0.73)不一致;Chinchilla 的 Approach 3 系数也和它自己的
"20 token/参数"规则不一致(复现文献 arXiv:2404.10102 指出了这一点)。
所以"读规模定律论文"的正确姿势是:看它的方法,不只看它的结论数字。
12.3 第 3 题(奖励模型的梯度推导)★★
问:写出奖励模型的目标
$\mathcal{L} = -\log\sigma\big(r_\theta(x,y_w) - r_\theta(x,y_l)\big)$
对 $r_w$ 和 $r_l$ 的梯度(记 $d = r_w - r_l$),
并说明它和逻辑回归的关系。为什么 $\mathcal{L}$ 只依赖差值?
答案:
用两个导数事实:$\dfrac{d}{dx}\log\sigma(x) = 1-\sigma(x)$,以及
$\dfrac{d\sigma}{dx} = \sigma(x)\big(1-\sigma(x)\big)$。
(a) 对 $r_w$:链式法则,外层是 $-\log\sigma$:
$$\frac{\partial \mathcal{L}}{\partial r_w} = -\big(1 - \sigma(d)\big)\cdot\frac{\partial d}{\partial r_w} = -\big(1-\sigma(d)\big)$$
(b) 对 $r_l$:注意 $\partial d/\partial r_l = -1$,负号两次抵消:
$$\frac{\partial \mathcal{L}}{\partial r_l} = +\big(1-\sigma(d)\big)$$
方向解读:括号里恒为正(因为 $\sigma<1$),
所以 $r_w$ 的梯度是负的,梯度下降把它往上推;
$r_l$ 的梯度是正的,把它往下压。
而且 $d$ 越大(模型已经判对了),$\sigma(d)\to1$,梯度 $\to0$:
已经判对的样本自动不再出力。这和《自然语言处理》§9 第 2 题里
负采样的梯度行为是同一个模式。
和逻辑回归的关系:把"$y_w$ 比 $y_l$ 好"当成一个二分类问题,
正类的 logit 设为 $d = r_w - r_l$,则
$-\log\sigma(d)$ 就是这个二分类的交叉熵损失。
所以奖励模型就是一个逻辑回归,只不过它的 logit 是由一个 Transformer 算出来的。
为什么只依赖差值:因为偏好排序提供的只有相对信息。
把所有人的分数同时加 100,"A 比 B 好"这个事实完全不变。
所以奖励的绝对零点不可辨识,
这决定了损失函数只能依赖差值。这不是简化,是数据的信息量决定的。
数值核对(§4.3 实跑):取 $r_w=0.7$、$r_l=-0.2$,
解析梯度 $-0.28905050$ 与数值梯度逐位相同。
12.4 第 4 题(KL 正则的闭式解)★★★
问:推导
$\displaystyle\max_{\pi}\;\mathbb{E}_{y\sim\pi}[r(y)] - \beta\,\mathrm{KL}\big(\pi\,\|\,\pi_{ref}\big)$
(约束 $\sum_y \pi(y)=1$)的最优解
$\pi^*(y) \propto \pi_{ref}(y)\exp\big(r(y)/\beta\big)$,
并说明 $\beta\to0$ 和 $\beta\to\infty$ 两个极限。
答案:
把 KL 展开,写成对每个 $y$ 的求和:
$$\mathcal{J}(\pi) = \sum_y \pi(y) r(y) - \beta\sum_y \pi(y)\log\frac{\pi(y)}{\pi_{ref}(y)}$$
约束是 $\sum_y\pi(y)=1$。引入拉格朗日乘子 $\lambda$:
$$\mathcal{L} = \sum_y \pi(y) r(y) - \beta\sum_y \pi(y)\log\frac{\pi(y)}{\pi_{ref}(y)} - \lambda\left(\sum_y \pi(y) - 1\right)$$
对单个 $\pi(y)$ 求偏导并令其为零(每一步的推导):
$$\frac{\partial \mathcal{L}}{\partial \pi(y)} = r(y) - \beta\left(\log\frac{\pi(y)}{\pi_{ref}(y)} + 1\right) - \lambda = 0$$
(第二项求导时注意 $\frac{d}{d\pi}\big[\pi\log(\pi/\pi_{ref})\big]
= \log(\pi/\pi_{ref}) + 1$。)
整理:
$$\log\frac{\pi(y)}{\pi_{ref}(y)} = \frac{r(y) - \lambda}{\beta} - 1 \;\Longrightarrow\; \pi(y) = \pi_{ref}(y)\,\exp\!\left(\frac{r(y)}{\beta}\right) \cdot \underbrace{\exp\!\left(-\frac{\lambda}{\beta}-1\right)}_{\text{与 } y \text{ 无关的常数}}$$
那个常数由 $\sum_y\pi(y)=1$ 定出来,就是 $1/Z$:
$$\boxed{\;\pi^*(y) = \frac{1}{Z}\pi_{ref}(y)\exp\!\left(\frac{r(y)}{\beta}\right), \qquad Z = \sum_y \pi_{ref}(y)\exp\!\left(\frac{r(y)}{\beta}\right)\;}$$
两个极限:
- $\beta\to0^+$:指数项 $\exp(r/\beta)$ 由 $r$ 最大的那一项支配,
所以 $\pi^*$ 收敛到 $\arg\max_y r(y)$ 上的确定性分布(贪婪)。
- $\beta\to\infty$:$r/\beta\to0$,$\exp(\cdot)\to1$,
所以 $\pi^*\to\pi_{ref}$,完全不动。
数值验证:§4.4 的实跑里,这个闭式解与直接数值优化目标函数的结果
偏差全程 $\le 10^{-10}$,两者是同一个东西。
这个解的物理意义:它就是统计物理里的玻尔兹曼分布 / 指数族倾斜:
$r$ 是能量(取负),$\beta$ 是温度。温度越高($\beta$ 大),分布越接近参考分布;
温度越低($\beta$ 小),分布越集中在最低能量(最高奖励)处。
12.5 第 5 题(涌现是指标造成的)★★
问:取 §5 实跑表里的 $N=3\times10^9$ 和 $N=3\times10^{11}$ 两个模型。
(a) 每个 token 的准确率 $a(N)$ 涨了多少倍?
(b) $\mathrm{EM}(L=8)$ 涨了多少倍?
(c) 如果测试集只有 200 题,这两个模型测出来的 EM 大概各是多少?
(d) 这个对比说明了什么?
答案:
(a) $a(3\times10^9) = 0.6084$,$a(3\times10^{11}) = 0.8543$。
$$\frac{0.8543}{0.6084} \approx 1.40\quad\text{(涨了 40%)}$$
(b) $\mathrm{EM}(8) = a^8$:
$$\frac{(0.8543)^8}{(0.6084)^8} = \frac{0.28365}{0.018771} \approx 15.1\quad\text{(涨了 15 倍)}$$
(c) 测试集 200 题,期望答对题数 $= \mathrm{EM}\times200$:
- $N=3\times10^9$:$0.01877\times200 \approx 3.75$ 题 →
测出的准确率在 $1/200=0.005$ 到 $5/200=0.025$ 之间抖动,
因为随机性,报告出来可能只有 1%~2%。 - $N=3\times10^{11}$:$0.28365\times200 \approx 56.7$ 题 →
测出来约 28%,稳定。
再往下看一格:$N=3\times10^8$ 的 $\mathrm{EM}(8) = 0.00126$,
期望答对 0.25 题,200 题里一题都不对的概率是 77.8%。
也就是说,这个模型有近八成的概率被测成 0,"完全不会"。
(d)
- 底层能力只涨了 40%,Exact Match 涨了 15 倍。
同一个 $a(N)$ 曲线,被 $a^L$ 这个非线性指标放大了一个数量级。
- 加上小测试集,低准确率那一段被压成了平地,
台阶因此显得更陡。这正是 Schaeffer et al. 2023 说的"海市蜃楼"的两个机制。
- 如果换成 $\log a(N)$ 这个连续指标(同一段实跑的第二张表),
斜率从 $+0.16$ 到 $+0.22$ 再降到 $+0.047$,全程连续、单调、可外推,
完全看不到"涌现"。
所以答案是:这个"涌现"是 $a^L$ 这个指标 + 测试集分辨率共同制造出来的视觉效果,
不是底层能力的突变。
12.6 第 6 题(LoRA 的参数账)★
问:一个 4096×4096 的注意力投影矩阵。
(a) 全量微调要训多少参数?
(b) LoRA 取 $r=8$,要训多少参数?占全量的百分之几?
(c) 如果模型有 32 层,每层 4 个这样的矩阵,全量微调总量是多少?LoRA 总量是多少?
答案:
(a) $4096\times4096 = 16{,}777{,}216 \approx 1678$ 万。
(b) LoRA 的参数量是 $r(d+k) = 8\times(4096+4096) = 65{,}536 \approx 6.55$ 万。
$$\frac{65{,}536}{16{,}777{,}216} = 0.003906 = \mathbf{0.3906\%}$$
(这个数我在写这一页时用 numpy 算过,就是它。)
(c) 全量:$16{,}777{,}216\times4\times32 = 2{,}147{,}483{,}648 \approx 21.5$ 亿。
LoRA:$65{,}536\times4\times32 = 8{,}388{,}608 \approx 839$ 万。
比例还是 0.3906%(因为两边同乘同一个因子)。
一个附加问题:为什么 LoRA 参数量是 $r(d+k)$ 而不是 $d\times k$?
因为 $B$ 是 $d\times r$、$A$ 是 $r\times k$,
参数量是 $dr + rk = r(d+k)$。这就是"低秩分解"的全部算术:
把"乘两次"($dk$ 次乘法)换成"先降维再升维"($r(d+k)$ 次乘法),
当 $r \ll \min(d,k)$ 时大幅省参数。
12.7 第 7 题(解码手算)★
问:词表只有 4 个 token,模型输出的分布是
$p = [0.5, 0.25, 0.125, 0.125]$(依次对应 A、B、C、D)。
(a) 取 $T=0.5$ 后的分布是多少?
(b) 取 top-p = 0.8,保留哪些 token?重归一化后各是多少?
(c) 取 top-k = 2 后的分布是多少?
(d) 从 (b) 和 (c) 的结果,说明为什么 top-p 比 top-k 更不容易"误伤"。
答案:
(a) $p_i \propto p_i^{1/T} = p_i^{2}$:
$$[0.5^2, 0.25^2, 0.125^2, 0.125^2] = [0.25, 0.0625, 0.015625, 0.015625]$$
和 $= 0.25+0.0625+0.015625+0.015625 = 0.34375$。归一化:
$$p^{(0.5)} = \left[\frac{0.25}{0.34375}, \frac{0.0625}{0.34375}, \frac{0.015625}{0.34375}, \frac{0.015625}{0.34375}\right] = [0.7273,\;0.1818,\;0.04545,\;0.04545]$$
(实跑核对:与 §8.5 的代码一致。)
(b) 从大到小排序:$A=0.5$、$B=0.25$、$C=0.125$、$D=0.125$。
累计:$0.5$、$0.75$、$0.875$、$1.0$。
最小的使累计 $\ge0.8$ 的集合是 $\{A,B,C\}$(累计 0.875)。$D$ 被丢掉。
重归一化(除以 0.875):
$$\left[\frac{0.5}{0.875}, \frac{0.25}{0.875}, \frac{0.125}{0.875}, 0\right] = [0.5714,\;0.2857,\;0.1429,\;0]$$
(c) top-k = 2:保留 A、B。除以 $0.5+0.25=0.75$:
$$[0.6667,\;0.3333,\;0,\;0]$$
(d)
注意这两个结果:top-p=0.8 保留了 3 个,top-k=2 只保留了 2 个。
在这个比较尖锐的分布上,两者差别不大。
但把分布换成均匀分布($[0.25,0.25,0.25,0.25]$):
- top-p = 0.8:需要累计 $\ge0.8$,即至少 4 个($0.25\times4=1.0$),
所以保留全部 4 个,分布不变。 - top-k = 2:砍掉一半,把每个概率从 0.25 硬抬到 0.5,
而且丢掉了 50% 的概率质量。
结论:$k$ 是一个绝对数量,不管分布的形状;
$p$ 是一个概率质量的阈值,会随分布自动伸缩。
在平坦分布上,top-k 会造成"过度自信",而 top-p 不会。
这就是 §8.5 那张"尖峰 vs 平坦"对比表要说明的事。
12.8 第 8 题(认知科学设计题)★★★
问:有人主张:"大模型证明了语言习得既不需要关键期,也不需要先天语法,
所以乔姆斯基错了。"请写出三条对这条论证的反驳,
并设计一个能区分"模型和儿童用同样机制学语言"与"用不同机制"的实验。
要求写清自变量、因变量、控制变量,以及什么结果会支持哪一方。
答案:
三条反驳(详见 §10.1):
- 输入量的量级完全不同,这不是可比的实验。
儿童的累计输入量级在 $10^6 \sim 10^7$ 词,GPT-3 是 $3\times10^{11}$ token,
差 4~5 个数量级。"模型能学会"和"儿童能学会"跨过了最关键的变量。 - 模型展示的是形式能力,不是功能能力。
它产出/判断合法句子的能力(formal competence)和
把语言用在真实交际与世界知识里的能力(functional competence)是两回事
(Mahowald et al. 2024)。"学会了语法"只覆盖了第一条。 - "没有关键期"可能只是因为"没有发育过程"。
关键期在生物学上与突触修剪、髓鞘化绑定;
一个固定架构、参数量不变的优化过程本来就不该被期待出现关键期。
所以这不构成对关键期假设的反驳,只是"两个系统机制不同"的正常表现。
把"模型没有关键期"当成"儿童的关键期是错觉",是一个范围错误。
实验设计(一个可行的方案):
-
要检验的假设:关键期是不是一个更一般的优化现象,
即"在某个训练阶段之后再引入某个能力,就永久性地学不好"。 -
自变量:
- 因素一:引入任务 B 的时机(训练进度 0%、25%、50%、75%、100%)。
- 因素二:任务 B 的类型(例如:一种不常见但规则简单的句法变换 /
一个需要世界知识的事实查询)。 - 因变量:
- 最终在任务 B 上的表现(训练结束后统一评测,所有条件下
都额外给 B 训练到收敛,以排除"练得少")。 - 以及在任务 A 上的表现(检查是否发生灾难性遗忘)。
- 控制变量(这一条是设计的关键):
- 总训练算力必须完全相同。否则"更差"可能只是"总共练得少"。
这正是 §2.2 第 2 题的教训:对照必须匹配。 - 总数据量相同、B 的数据量在所有条件下相同(只改"什么时候看到")。
-
随机种子多个,报告方差而不是单点。
-
什么结果支持"同机制":如果存在一个时间点,过了它之后引入 B
即使额外训练也无法追平(永久性差距),
那就说明这个优化过程也有"窗口期",
这支持"关键期可能是一个更一般的优化现象,而不只是神经发育的产物"。 -
什么结果支持"不同机制":如果在任何阶段引入 B 都能追平
(只要给够训练量),那就说明这个系统没有窗口期,
这支持"关键期依赖的是生物发育过程,不是学习本身"。 -
必须写出的诚实声明:
这个实验不能直接推广到儿童,因为儿童的关键期与生物发育绑定。
它的价值是检验一个更窄、更可验证的假设:
"窗口期是否可能只是一个优化现象"。能证伪的假设才是有用的假设。
13 出处索引
本页提到的所有文献,按主题分类。标注了本页出现的位置。
凡是我不能确认的,明确写"出处待查",没有编造。
13.1 规模定律
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Kaplan, McCandlish, Henighan, Brown, Chess, Child, Gray, Radford, Wu & Amodei, Scaling Laws for Neural Language Models | arXiv:2001.08361, 2020 | §2.2, §2.4 |
| Hoffmann, Borgeaud, Mensch, et al., Training Compute-Optimal Large Language Models(Chinchilla) | arXiv:2203.15556, 2022 | §2.3, §2.4 |
| Besiroglu, Erdil, Barnett & You, Chinchilla Scaling: A replication attempt | arXiv:2404.10102, 2024(Epoch AI) | §2.4(指出 Approach 3 的不自洽) |
13.2 GPT 系列与 RLHF
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Radford, Narasimhan, Salimans & Sutskever, Improving Language Understanding by Generative Pre-Training(GPT-1) | OpenAI 技术报告, 2018(无 arXiv 编号) | §3.1 |
| Radford, Wu, Child, Luan, Amodei & Sutskever, Language Models are Unsupervised Multitask Learners(GPT-2) | OpenAI 技术报告, 2019(无 arXiv 编号) | §3.2 |
| Brown, Mann, Ryder, et al., Language Models are Few-Shot Learners(GPT-3) | NeurIPS 2020, arXiv:2005.14165 | §1.2, §3.3 |
| Ouyang, Wu, Jiang, et al., Training language models to follow instructions with human feedback(InstructGPT) | arXiv:2203.02155, 2022 | §3.4, §4 全节 |
| OpenAI, GPT-4 Technical Report | arXiv:2303.08774, 2023 | §3.5 |
| Christiano, Leike, Brown, Martic, Legg & Amodei, Deep Reinforcement Learning from Human Preferences | NeurIPS 2017, arXiv:1706.03741 | §4.1 |
| Gao, Schulman & Hilton, Scaling Laws for Reward Model Overoptimization | ICML 2023, arXiv:2210.10760 | §4.5 |
| Rafailov, Sharma, Mitchell, Ermon, Manning & Finn, Direct Preference Optimization(DPO) | NeurIPS 2023, arXiv:2305.18290 | §4.5 |
| Amodei, Olah, Steinhardt, Christiano, Schulman & Mané, Concrete Problems in AI Safety | arXiv:1606.06565, 2016 | §9.4 |
| Sutton, The Bitter Lesson | 个人博客短文, 2019(非同行评议) | §3.6 |
13.3 涌现能力
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Wei, Tay, Bommasani, Raffel, Zoph, Borgeaud, et al., Emergent Abilities of Large Language Models | TMLR 2022, arXiv:2206.07682 | §5.1 |
| Schaeffer, Miranda & Koyejo, Are Emergent Abilities of Large Language Models a Mirage? | NeurIPS 2023(Outstanding Paper), arXiv:2304.15004 | §5.2, §5.3, §5.4 |
13.4 提示与思维链
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Wei, Wang, Schuurmans, Bosma, Ichter, Xia, Chi, Le & Zhou, Chain-of-Thought Prompting Elicits Reasoning in Large Language Models | NeurIPS 2022, arXiv:2201.11903 | §6.2 |
| Kojima, Gu, Reid, Matsuo & Iwasawa, Large Language Models are Zero-Shot Reasoners | NeurIPS 2022, arXiv:2205.11916 | §6.2 |
| Wang, Wei, Schuurmans, Le, Chi, Narang, Chowdhery & Zhou, Self-Consistency Improves Chain of Thought Reasoning in Language Models | ICLR 2023, arXiv:2203.11171 | §6.3 |
| Turpin, Michael, Perez & Bowman, Language Models Don't Always Say What They Think | arXiv:2305.04388, 2023 | §6.5 |
13.5 高效微调与量化
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Hu, Shen, Wallis, Allen-Zhu, Li, Wang, Wang & Chen, LoRA: Low-Rank Adaptation of Large Language Models | ICLR 2022, arXiv:2106.09685 | §7.1, §7.2 |
| Aghajanyan, Gupta & Zettlemoyer, Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning | ACL 2021, arXiv:2012.13255 | §7.2 |
| Eckart & Young, The approximation of one matrix by another of lower rank | Psychometrika 1(3):211–218, 1936 | §7.2(截断 SVD 最优性的定理) |
13.6 局限与评测
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Ji, Lee, Frieske, Yu, Su, Xu, Ishii, Bang, Madotto & Fung, Survey of Hallucination in Natural Language Generation | ACM Computing Surveys 55(12):248, 2023, arXiv:2202.03629 | §9.1 |
| Liu, Lin, Hewitt, Paranjape, Bevilacqua, Petroni & Liang, Lost in the Middle: How Language Models Use Long Contexts | TACL 12:157–173, 2024, arXiv:2307.03172 | §9.3 |
| von Oswald, Niklasson, Randazzo, Sacramento, Mordvintsev, Zhmoginov & Vladymyrov, Transformers learn in-context by gradient descent | arXiv:2212.07677, 2022 | §1.3 |
13.7 认知科学交叉
| 文献 | 出处 | 本页位置 |
|---|---|---|
| Bender & Koller, Climbing towards NLU: On Meaning, Form, and Understanding in the Age of Data | ACL 2020, arXiv:2005.04118 | §10.1, §10.3 |
| Piantadosi & Hill, Meaning without reference in large language models | arXiv:2208.02957, 2022 | §10.1, §10.3 |
| Frank, Baby steps in evaluating the capacities of large language models | Nature Reviews Psychology 2:451–452, 2023 | §10.1 |
| Mahowald, Ivanova, Blank, Kanwisher, Tenenbaum & Fedorenko, Dissociating language and thought in large language models | Trends in Cognitive Sciences 28(6):517–540, 2024 | §10.1, §10.3 |
| Schrimpf, Blank, Tuckute, Kauf, Hosseini, Kanwisher, Tenenbaum & Fedorenko, The neural architecture of language | PNAS 118(45), 2021 | §10.2 |
| Goldstein, Zada, Buchnik, et al., Shared computational principles for language processing in humans and deep language models | Nature Neuroscience 25:369–380, 2022 | §10.2 |
13.8 我明确没能确认的地方,如实标注
-
GPT-1 和 GPT-2 的出处。这两篇没有 arXiv 编号,
它们是 OpenAI 直接发布的技术报告,不是会议/期刊论文。
引用时写 "OpenAI Technical Report, 2018/2019"。这两篇确实没有 arXiv 编号,不是我没查到。 -
GPT-4 的架构、参数量、算力、数据。官方没有公开,
报告(arXiv:2303.08774)明确说明出于竞争和安全考虑不披露。
本页所有涉及 GPT-4 的空白格都是"官方未公开",不是我没查到。 -
BabyLM Challenge 的确切出处(§10.1)。
我记得是 Warstadt 等人 2023 年组织的,
但无法确认确切的 arXiv 编号和会议信息,故标"出处待查"。 -
sycophancy(迎合)的权威文献(§9.4)。它被反复报告,
但这个现象的命名和研究还在演化,我不给一个确定的出处。 -
儿童输入量的具体数字(§10.1)。我给的是量级($10^6\sim10^7$ 词),
不给精确值。这个领域里有若干著名结论后来被后续工作质疑过,
所以我不引用任何具体的"词汇量差距"数字。 -
§2.4 里 Kaplan 的 0.73 与我从其联合式复算出的 0.556 不一致,
以及 Chinchilla 的 Approach 3 系数与其"20 token/参数"规则不一致。
这两处都是如实报告我自己的复算结果,
后者有复现文献(arXiv:2404.10102)支撑,前者我只报告现象、不强行解释。 -
关于"截至写作时"的所有表述(§6.4 的模型名、§8.6 的解码策略、
§4.5 的 DPO 是否已成为主流)。这门课时效性最强,
凡是我没有把握的,都写成了"截至本书写作时"或标注不确定,请不要当定论。
最后一句:这一页的所有可运行的代码我都实跑过,
标注 >>> 实跑输出 的输出是原样粘贴的,与代码逐字对应。
所有引用的数字要么来自我实际运行的输出(§2、§4、§5、§7、§8),
要么标注了文献出处。
如果哪一处你复现不出来,请以你的实际输出为准。这可能是我漏掉的环境差异。
14 相关
深度学习原理与实践.md,本页的直接前置之一。反向传播、注意力、Transformer、
双下降的推导都在那里,本页一个都不重复,只引用(§1、§5、§8、§10 多处引用)。自然语言处理.md,本页的另一个直接前置。Word2Vec、BERT、困惑度、
预训练 + 微调范式的推导都在那里;本页从 BERT 之后讲起(§1 的分岔、§2 的规模)。../专业课/教材/笔记-心理语言学核心.html,§6"大模型 vs 人类语言加工"
与本页 §10 讲的是同一件事:那门课讲实验设计,本页讲模型的内部机制。../专业课/教材/笔记-语言认知神经科学.html,N400、P600 是 §10.2
"模型-脑对齐"里脑响应的因变量来源。../专业课/教材/笔记-语言学概论.html,§10.1 的"输入贫乏论证"与关键期假设
在那里的句法/习得章节有更完整的论述。../专业课/教材/笔记-实验方法论核心.html,§5(指标决定结论)、
§11 第 2 题(对照必须匹配)、§8.6(评测必须报告配置)都直接对应
那门课的核心内容,建议并读。