对应 Maps to:CMU 17-630 Prompt Engineering(Prof. Travis Breaux),第 1 周 · History of Language Models。原课 Schedule 对这一讲的说明是:“a review from word and sentence embeddings, to the transformer and generative pre-trained models”(从词向量、句向量,一路回顾到 Transformer 和生成式预训练模型)。 依据说明:原课的 slides 和阅读清单都在 Canvas 上,没有公开。本讲按 Schedule 的说明,沿着这条历史线上的原始论文展开:Shannon(1948/1951)、Bengio et al.(2003)、Mikolov et al.(2013)、Pennington et al.(2014)、Reimers & Gurevych(2019)、Bahdanau et al.(2015)、Vaswani et al.(2017)、Peters et al.(2018)、Radford et al.(2018/2019)、Devlin et al.(2019)、Raffel et al.(2020)、Brown et al.(2020)、Olsson et al.(2022)、Ouyang et al.(2022),以及把这段历史归纳成”四个范式”的综述 Liu et al.(2023)。引用的数字都核对过原文。§0.6 的玩具世界和全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:
code/lecture01/(Python 标准库 + numpy + PyTorch;6 个脚本 + 6 个模块;不需要 API key、不需要 GPU;第一次运行 05 号脚本时会在 CPU 上训练一个迷你 GPT,约 50 秒,之后读缓存;其余脚本都在 25 秒以内) 预计阅读时间:4–5 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 说清楚这门课所说的”提示工程”是什么:不只是”怎样写好提示词”,而是在模型参数冻结的前提下,怎样用上下文(指令、示例、外部信息)可靠地构造出一个系统;并能把它放进 NLP 的”四个范式”里。
- 用自己的话解释语言模型的定义、困惑度的直觉,以及 n-gram 模型为什么同时受困于”看得太近”和”见得太少”。
- 解释分布式假设,说出 PPMI-SVD 和 word2vec 学到的”相似”是什么意思,以及静态词向量的三个盲点:分布孪生词、多义词、反义词。
- 说出把句子压成一个向量时丢掉了什么(顺序、否定),以及为什么”上下文相关的表示”解决了这个问题。
- 画出 Transformer 自注意力的计算过程,解释 √d 缩放、因果遮罩、位置编码各自解决什么问题,并能讲清楚”归纳头”这个两层电路怎样实现”照着前文抄”。
- 讲出从 ELMo / GPT-1 / BERT / T5 到 GPT-2 / GPT-3、再到 InstructGPT / ChatGPT 的演变,以及每一步怎样改变了”人怎样让模型做事”。
- 把历史转化成几条提示工程的直觉:base 模型在续写文档;格式和触发词决定任务;示例告诉模型结构;知识有方向(反转诅咒);温度是多样性和可靠性的旋钮——并在一个自己训练出来的迷你 GPT 上亲眼验证它们。
0.2 先修与本讲范围 Prerequisites & scope
原课面向软件工程、数据分析方向的研究生,默认会编程、学过一些机器学习。这份讲义另外写了 3 篇前置讲,专门照顾计算机系统背景较少的读者:P1 文本、字节与 token;P2 把 LLM 当作一个软件服务(API、消息格式、重试、缓存、记账);P3 提示实验的统计学。本讲用到这些知识的地方很少,用到时会就地给出直觉。
| 默认你已经会 Assumed | 本讲只给直觉,后面再展开 Previewed only |
|---|---|
| Python 基础(不熟的写法代码注释里都有解释) | 子词分词(BPE)与 token 的计算(前置讲 P1) |
| 条件概率、对数、向量点积、矩阵乘法 | 调用云端 LLM 的 API、chat 消息格式(前置讲 P2) |
| 知道神经网络是”可训练的函数”,梯度下降在做什么 | 置信区间与配对检验(前置讲 P3;本讲的表格已经附上了 95% 区间) |
| 不需要用过任何 LLM API | 上下文学习的细节(第 2 讲)、指令微调(第 3 讲)、RLHF 与对齐(第 5、6 讲) |
如果你学过 CS224N,§2–§7 的很多内容会是复习;这一讲的视角不同——每一段历史都会落到同一个问题上:这一步怎样改变了”人怎样让模型做事”?
0.3 本讲地图 Map
§1 这门课在讲什么:从"改参数"到"写上下文";NLP 的四个范式;为什么要从历史讲起
§2 语言模型是什么:Shannon、n-gram、困惑度、稀疏与平滑、窗口 ← 代码 01
§3 词的分布式表示:one-hot → 共现 → PPMI-SVD / word2vec;类比、多义、反义 ← 代码 02
§4 从词到句子:词袋、TF-IDF、学出来的句向量;顺序和否定 ← 代码 03
§5 神经语言模型:Bengio 2003、RNN / LSTM、seq2seq、注意力的诞生
§6 Transformer:自注意力、√d、多头与位置、因果遮罩、归纳头 ← 代码 04
§7 预训练范式:ELMo、GPT-1、BERT、T5;那个年代的"提示" ← 代码 05(演示 1–2)
§8 GPT-2 与 GPT-3:提示的诞生;base 模型在续写文档;敏感性;反转诅咒;规模;温度 ← 代码 05(演示 3–5)、06
§9 从 GPT-3 到 ChatGPT:指令微调、RLHF、chat 模板、推理模型
§10 历史给提示工程的八条直觉 §11 课程地图
§12 小结 §13 常见误区 §14 练习 §15 延伸阅读 §16 下一讲预告
0.4 记号与术语 Notation & terms(全课统一)
| 术语 | 本讲义里的意思 |
|---|---|
| token | 模型处理文字的最小单位。真实模型用子词(一个英文单词常是 1–2 个 token,一个汉字常是 1–2 个 token);本讲的玩具语料用”一个词 = 一个 token” |
| 词表(vocabulary) | 模型认识的全部 token;词表外的词在本讲的代码里变成 <unk> |
| 语言模型(LM) | 给”下一个 token”打概率的函数 |
| 上下文(context)/ 上下文窗口 | 模型在预测时能看到的前文;窗口是它最多能看多少个 token |
| 提示(prompt)/ 续写(completion) | 交给模型的前文 / 模型接着写出来的部分 |
| base 模型 | 只做过预训练(预测下一个 token)的模型 |
| chat / instruct 模型 | 在 base 模型之上又做过指令微调、RLHF 等训练,会”听指令、对话”的模型 |
| 零样本 / 少样本(zero-shot / few-shot) | 提示里不给 / 给几个”输入 → 输出”的示例(demonstrations) |
| 上下文学习(in-context learning, ICL) | 参数不变,只靠提示里的指令和示例让模型完成新任务 |
| 困惑度(perplexity) | ,越低越好;直觉是”平均在几个 token 之间犹豫” |
| 温度(temperature) | 采样时把分布变成 : 更保守, 更随机, 是贪心 |
| 嵌入(embedding) | 把 token、句子映射成稠密向量;本讲里”词向量""句向量”都是嵌入 |
0.5 关于这门课 · About 17-630
17-630 是 CMU 软件与社会系统系(S3D)开给 MSE、数据分析等项目研究生的课,主讲 Travis Breaux(他的研究方向是需求工程、隐私与法规遵从)。课程主页把它概括为一门讲提示语言模型的设计与评估的课。几点值得先知道:
- 视角是工程的。syllabus 明确说,这门课把提示工程从”怎样写提示词”重新定义为”怎样从一套正在形成的科学基础里挑选技术,构造可靠的基于提示的系统”。三条学习目标是:凭直觉设计提示指令与模板;把复杂推理任务分解成多阶段提示;设计实验评估提示、优化提示表现。
- 内容横跨三块:提示技术(ICL、CoT、RAG、工具、分解、自动提示、智能体、多智能体辩论),对齐(幻觉、偏见、谄媚、超级对齐),以及评估(benchmark、指标、实验设计)。讲义的 17 讲和原课 14 周的对应关系见 SYLLABUS §0.2。
- 作业是动手的:在文本分类上比较指令、模板和示例(第 2 讲);函数调用,单阶段 vs 多阶段(第 8、9 讲);RAG 的内容导入和检索器(第 7 讲);外加一个学期项目(第 17 讲)。原课给每个学生 50 美元的 OpenAI 额度用于完成作业。
- 允许用 AI 助手做作业和项目(小测验和期末考试除外)——对一门教你怎样用 LLM 的课来说,这很自然。
0.6 本讲的玩具世界 · The toy world
真实的大模型在几万亿 token 上训练,我们没法在笔记本上复现,也没法知道它到底”见过”什么。所以本讲的代码造了一个完全透明的小世界(pretrain_world.py):
- 24 个虚构的国家,每个有首都、语言、出口商品、领导人四个事实。名字是随机拼出来的(volel、grorken、driltese……),模型不可能从别处知道它们。
- 5 种文档,模仿网页文本的多样性,拼成约 23 万词的预训练语料:
| 文档 | 例子 | 作用 |
|---|---|---|
| article 百科词条 | article : volel is a country in the north . the capital of volel is grorken . ... the leader of volel is kiro . | 每个国家的四个事实,措辞随机 |
| faq 问答页 | faq : q : what is the capital of vanda ? a : dulo . q : ... | 只出现 24 国里的 16 国 |
| quiz 题目列表 | quiz : what is the capital of volel ? who leads mina ? ... | 只有问题、没有答案 |
| reviews 评论页 | reviews : review : the soup was not cold . rating : positive . ... | 情感;含 but 句、not 句 |
| story 小故事 | story : the queen sits on the throne . she wears a crown . ... | 人物词的性别、王室、年龄特征;多义词 bank |
- 两个故意的不对称,用来做在真实模型上做不了的干净实验:
- 8 个国家的知识只出现在百科词条里,从没以问答形式出现过——用问答格式问它们,能答对吗?(§8.5)
- 首都在语料里两个方向都说(“the capital of X is C”、“C is the capital of X”),领导人只按”X 的领导人是 L”的方向说,L 从来不出现在 X 前面——反过来问”L 是谁的领导人”,能答对吗?(§8.5)
在这个语料上,本讲依次训练 n-gram 模型(§2)、两种词向量(§3)、句向量(§4),最后训练一个 26 万参数的迷你 GPT(2 层、4 个头、96 维、上下文 64 个 token;tiny_gpt.py),在 CPU 上训练约 50 秒。它和 GPT-3 是同一种结构,只是小了约 70 万倍。
怎样看待玩具实验的结论:玩具世界能把一个机制放大、隔离出来让你看清楚(例如”窗口外的词对 n-gram 没有影响""反着问答不出来”),但不能证明真实模型上的数字。凡是能和论文里真实模型的结果对照的地方,讲义都会给出论文的数字。
1. 这门课在讲什么 · What this course is about
1.1 一个范式转变:从”改参数”到”写上下文”
先看两种让模型做一个新任务(比如判断评论的情感)的方式:
| 微调(fine-tuning) | 提示(prompting) | |
|---|---|---|
| 你要准备什么 | 几千到几万条带标签的数据 | 一段说明 + 也许几个示例 |
| 模型的参数 | 被梯度下降改掉 | 冻结不动 |
| 换一个任务 | 再训练一个模型 | 换一段提示 |
| 需要什么 | GPU、训练代码、机器学习知识 | 会写字(以及会做实验) |
原课 syllabus 开篇讲的就是这个转变:大语言模型让人不必再为每个新领域准备成千上万条训练样本去微调,只要写一段包含指令、示例和其他上下文的提示,参数冻结的模型就能在推理时”现学”这个任务——这就是上下文学习。
这件事为什么可能?——这正是本讲要用历史回答的问题。简短的答案是:一个在海量文本上训练”预测下一个词”的模型,为了预测得好,被迫学会了文本里出现的几乎所有”任务”(问答、翻译、摘要、分类……),因为这些任务的例子本来就散落在网页里。提示做的事,是用一段前文把模型”放到”那个任务的语境里。
1.2 NLP 的四个范式
Liu et al.(2023)的综述《Pre-train, Prompt, and Predict》把 NLP 的历史归纳成四个范式。每个范式里,研究者花力气”工程”的对象不一样:
| 范式 | 大致年代 | 工程的对象 | 例子 |
|---|---|---|---|
| ① 全监督学习(非神经网络) | 2015 年以前为主 | 特征工程:人工设计特征 | 用词频、词性、句法特征 + 逻辑回归 / SVM 做情感分类 |
| ② 全监督学习(神经网络) | 2013–2018 | 结构工程:设计网络结构 | 词向量 + CNN / LSTM,每个任务训练一个模型 |
| ③ 预训练 + 微调 | 2017–2019 | 目标工程:设计预训练目标和微调方式 | BERT 在大语料上预训练,再在每个任务上微调 |
| ④ 预训练 + 提示 + 预测 | 2019 至今 | 提示工程:设计输入 | GPT-3:同一个模型,靠不同的提示做不同的任务 |
本讲的 §2–§4 是范式 ①② 的故事,§5–§7 是 ③,§8–§9 是 ④。注意一个趋势:人的工作越来越往”输入端”移动。到了范式 ④,模型是一个固定的、通用的东西,你能改的只剩下输入——提示工程就是这样诞生的。
1.3 提示工程 ≠ “写好提示词”
如果提示工程只是”找到一句神奇的话”,它不值得一门课。原课把它重新定义为构造可靠的基于提示的系统,这里有三层意思:
- 提示是程序,模型是解释器。一个提示有结构(指令、示例、输入、输出格式),可以被分解成多个阶段(第 4、7–10、13–15 讲),可以被测试。
- “这个提示更好”是一个需要实验证明的论断。§8.4 会看到,格式上的一个小改动可以让准确率从 100% 掉到 0;Zhao et al.(2021)和 Sclar et al.(2024)在真实模型上看到了同样的敏感性。不做对照实验、不报置信区间,就不知道自己的改动是真的有效还是运气(第 2、17 讲,前置讲 P3)。
- 可靠 ≠ 平均准确率高。幻觉、偏见、谄媚、注入攻击,是基于提示的系统在”准确率”之外必须面对的失败(第 5、6、12、16 讲)。
1.4 为什么要从历史讲起
因为几乎每一个提示技巧,都是模型训练方式的一个推论。理解模型从哪里来,你就能预测它对提示的反应,而不是死记”提示词模板大全”。这一讲会从历史里提炼出八条直觉(§10),后面每一讲都会用到。先预告几条:
- 一个 base 模型只是在续写文档;提示有没有用,取决于它像不像某种文档的开头,以及在那种文档里,你想要的回答是不是最自然的续写(§8.3)。
- 模型只能”看见”它的上下文窗口里的东西,而且看的方式(注意力)是有偏好的(§2.6、§6)。
- 知识是以训练数据里出现的格式和方向存下来的;问法要和它对得上(§8.5)。
- 示例的主要作用之一是告诉模型格式和结构(§8.2)。
2. 语言模型是什么:从 Shannon 到 n-gram · What is a language model
配套代码:
01_ngram.py、模块ngram_lm.py
2.1 定义:给一串词打概率
语言模型(language model)是一个给文字序列打概率的模型。按概率的链式法则,一个序列的概率可以拆成”每一步预测下一个词”的乘积:
所以语言模型等价于一个”给定前文,下一个词是什么”的预测器。这个看起来很朴素的定义,是从 1940 年代一直延续到 GPT-5 的同一个定义——今天所有的大语言模型,本质上仍然是在计算 。
提示工程的第一条直觉就藏在这个公式里:提示就是”前文”。你写什么提示,就是在给这个条件分布设定条件。模型的回答不是”思考出来”的,而是从 里一个词一个词地取出来的。
2.2 Shannon 的游戏
信息论的创始人 Claude Shannon 在 1948 年的《通信的数学理论》里,就用”按统计规律随机生成英文”来说明语言的结构:只按字母频率生成,是一串乱码;按”前一个字母决定下一个字母”的统计生成,开始像英文的拼写;按”前一个词决定下一个词”生成,就出现了像模像样的短语。
1951 年,他又做了一个实验:让人去猜一段英文的下一个字母,猜错了就告诉他正确答案,统计平均要猜几次。从这些数据他估计出,在看得到很长前文时,英文的熵大约在每个字母 0.6 到 1.3 比特之间——远低于”26 个字母随机出现”的 4.7 比特。这个差距就是语言的冗余:前文让下一个字母变得高度可预测。
这个游戏就是今天语言模型训练的目标——“猜下一个”。区别只在于玩家从人换成了模型,前文从几十个字母变成了几十万个 token。
2.3 马尔可夫假设与 n-gram:数数
直接估计 是不可能的:长长的前文几乎每一个都只出现过一次。n-gram 模型做一个大胆的简化——马尔可夫假设:下一个词只取决于前面 个词。
就是数数:在训练语料里,“前 个词”后面跟过哪些词、各几次。2-gram(bigram)看前 1 个词,3-gram(trigram)看前 2 个词。从 1970 年代 IBM 的语音识别,到 2000 年代的统计机器翻译,几十年里最成功的语言模型都是 n-gram。
在玩具语料上数一数(01 号脚本演示 1)。问一道知识题,提示是 <doc> faq : q : what is the capital of vanda ? a :,不同的 n 看见的上下文不同:
| 模型 | 真正看见的上下文 | 预测 top-1 |
|---|---|---|
| 2-gram | : | the(0.31)✗ |
| 3-gram | a : | wheat(0.05)✗ |
| 5-gram | vanda ? a : | dulo(0.38)✓ |
| 7-gram | capital of vanda ? a : | dulo(0.99)✓ |
5-gram 看见了国名,却没看见问的是什么——问首都、问语言、问领导人的问题都以”<国家> ? a :”结尾,它把概率分给了三种答案(首都 0.38、语言 0.30、领导人 0.30),这次碰巧首都最大。7-gram 同时看见国家和关系,才给出 0.99。n-gram 只看见窗口里的东西,窗口之外的一切都不存在。
再换一个国家 volel——36 篇关于它的百科词条都写了它的首都,但这个事实从没以问答格式出现过。7-gram 的上下文 capital of volel ? a : 在训练里根本不存在,退回更短的上下文后只剩一个几乎平坦的分布(最大概率 0.04)。n-gram 记住的是字符串,不是事实。§8.5 会看到迷你 GPT 在同一个问题上的表现。
2.4 怎么评估:困惑度
怎样比较两个语言模型谁更好?最常用的指标是测试集上的困惑度(perplexity):
直觉:如果一个模型每一步都在 个词之间均匀地犹豫,它的困惑度就是 。完全随机(均匀分布在 个词上)的困惑度是 ;完美预测是 1。困惑度和交叉熵损失是一回事: 就是平均每个 token 的交叉熵——训练神经语言模型时我们最小化的正是它。
2.5 稀疏性与平滑:看得越远越好吗?
既然看得远能区分”问首都还是问语言”,为什么不把 n 一直加大?01 号脚本演示 2 给出了答案(Kneser–Ney 平滑):
| 模型 | 训练困惑度 | 测试困惑度 | 测试 n 元组没见过的比例 |
|---|---|---|---|
| 1-gram | 73.167 | 72.831 | 0.0% |
| 2-gram | 5.175 | 5.244 | 0.0% |
| 3-gram | 2.586 | 2.616 | 0.1% |
| 5-gram | 2.123 | 2.240 | 1.9% |
| 7-gram | 1.797 | 2.042 | 9.6% |
| 8-gram | 1.721 | 2.057 | 15.0% |
| 10-gram | 1.567 | 2.123 | 30.8% |
三件事:
- 只看词频的 1-gram 困惑度约 73——在 290 个词里大约相当于在 73 个之间瞎猜;看前 1 个词就骤降到 5.2。语言的大部分结构是局部的。
- 训练困惑度随 n 一直下降,测试困惑度在 n = 7 时最低,之后反而上升。原因在最后一列:n 越大,测试里出现的 n 元组在训练里没见过的比例越高(n = 10 时接近三分之一)。这就是数据稀疏(data sparsity):组合的数量随 n 指数增长,任何语料都覆盖不了。
- 这份玩具语料是模板生成的、极其规整,n-gram 在这里表现得比在真实语言上好得多。真实英语里 5-gram 就已经极度稀疏:Google 的 Brants et al.(2007)为了让 n-gram 机器翻译更好,用了多达 2 万亿词的语料。
没见过的 n 元组怎么办?如果直接数数(最大似然估计,MLE),它的概率就是 0;一句话里只要有一个 0,整句的概率就是 0、困惑度就是无穷大。平滑(smoothing)就是从见过的事件那里”匀”一点概率给没见过的事件:
| 平滑方法 | 思路 | 3-gram 测试困惑度 | 7-gram 测试困惑度 |
|---|---|---|---|
| 不平滑(MLE) | 直接数数 | ∞(0.07% 的 token 概率为 0) | ∞(9.61% 为 0) |
| 加 1(add-k, k=1) | 每个计数都 +1 | 4.841 | 24.970 |
| 加 0.01 | 每个计数都 +0.01 | 2.647 | 3.742 |
| 插值的绝对折扣 | 每个见过的计数扣掉 0.75,扣下来的按更短上下文的模型分出去 | 2.617 | 2.116 |
| Kneser–Ney | 同上,但低阶模型用”这个词跟在多少种不同的词后面” | 2.616 | 2.042 |
加 1 平滑把太多概率分给了从没出现过的组合,在大 n 时尤其糟糕。插值和 Kneser–Ney(1995)的思路是**“没见过长的上下文,就退回短的上下文”**——这个”退回”(backoff)思想在 n-gram 时代统治了二十年,Chen & Goodman(1998)的系统比较确认了修正版 Kneser–Ney 是最好的平滑方法之一。
n-gram 的两难就此成形: 小了,看得太近(§2.6); 大了,见得太少。§5 的神经语言模型要打破的正是这个两难。
2.6 n-gram 能被”提示”吗?——窗口
n-gram 模型当然可以续写一段提示。但它的上下文窗口只有 个词,窗口外的提示写了等于没写。01 号脚本演示 3 用两个任务看得很清楚。
代词:故事里 “the queen lives in the palace . ___“,下一个词是 he 还是 she?决定答案的 queen 在 4–6 个位置之前(1050 个测试位置):
| 模型 | 窗口 | 人物词在窗口内的比例 | 代词准确率 |
|---|---|---|---|
| 2-gram / 3-gram / 4-gram | 1 / 2 / 3 | 0% | 49.3% |
| 5-gram | 4 | 3.7% | 51.3% |
| 6-gram | 5 | 68.0% | 83.3% |
| 7-gram | 6 | 100% | 100% |
人物词一落到窗口外,准确率就是瞎猜的一半;窗口一盖住它,立刻接近 100%。
否定:两个只差一个 not 的提示,review : the soup was not cold . rating :(正确答案 positive)和 review : the soup was cold . rating :(negative)。5-gram 看见的都是 cold . rating :,给出的分布一模一样(两者都是 negative 0.84,最大差 0.000);7-gram 看得见 not,才把两句分开(positive 0.99 vs negative 1.00)。
这就是为什么 n-gram 时代没有”提示工程”:你在提示开头写”请判断下面这条评论的情感”,模型一个字都看不到。能利用整段提示里的远距离信息,是神经网络语言模型、尤其是 Transformer 带来的能力,也是提示工程能存在的前提。
2.7 生成:局部通顺 ≠ 整体连贯
用 n-gram 模型按温度 1 采样,从 <doc> story : 开始各写 200 段(01 号脚本演示 4):
| 模型 | 整段没跑题的比例 | 代词与人物性别一致 |
|---|---|---|
| 2-gram | 4.0% | 40.0%(10 处) |
| 3-gram | 20.5% | 46.8%(77 处) |
| 5-gram | 87.5% | 55.3%(217 处) |
| 8-gram | 93.5% | 99.2%(370 处) |
一段 2-gram 生成的”故事”长这样:story : who leads balan ? what language do people speak in the capital of kumis known for the market . he cares for ? a : ...——每两个相邻的词都说得通,可写着写着就从故事变成了题目列表、问答页、评论。它只记得上一个词,不记得自己在写哪种文档。“知道自己在写哪种文档”是一个需要长上下文的能力,§8.3 会看到它正是 base 模型对提示做出反应的方式。
3. 词的分布式表示 · Distributed word representations
配套代码:
02_word_vectors.py、模块embeddings.py
3.1 one-hot 的问题
在 n-gram 模型眼里,每个词都是一个互不相关的符号。用向量来说,就是 one-hot:一个 维的向量,只有自己那一维是 1。任何两个不同的词,点积都是 0——queen 和 king 的相似度,与 queen 和 coffee 的相似度一样,都是 0。
这正是 §2.5 稀疏性的根源:模型在训练里见过 “the queen wears a crown”,却不能由此推断 “the princess wears a crown” 也很可能,因为在它看来 princess 和 queen 毫无关系。要打破稀疏性,就得让”相似的词”有”相似的表示”,这样关于一个词学到的东西,才能泛化到和它相似的词上。
3.2 分布式假设
“相似”从哪里来?语言学家给出了答案。Harris(1954)提出,意思相近的词出现在相似的上下文里;Firth(1957)有一句被引用无数次的话:“You shall know a word by the company it keeps.”(看一个词和谁在一起,就知道它是什么意思。)这叫分布式假设(distributional hypothesis)。
它的妙处在于:意思可以从纯文本里自动统计出来,不需要任何人工标注。这是从 LSA 到 word2vec 再到 GPT 一以贯之的思想——GPT 的预训练也是”从上下文里学意思”,只是学的方式复杂得多。
3.3 路线一:数共现,再压缩(PPMI-SVD)
最直接的做法:
- 共现矩阵 : = 词 出现在词 左右 个位置之内的次数(本讲用 )。
- PPMI(正的点互信息):原始计数被 the、”.” 这种和谁都一起出现的词主导,说明不了什么。点互信息问的是” 和 一起出现的次数,比它们各自独立出现时预期的多多少倍”:
- SVD 压缩:PPMI 矩阵每行有 维、大部分是 0。用截断 SVD 压到 维(本讲 10 维或 50 维),“出现在相似上下文里的词”被迫共用少数几个方向,相似性才能传递——两个词即使没有一个共同的邻居,也可能得到相近的向量。这一脉做法叫 LSA(潜在语义分析,Deerwester et al. 1990)。
02 号脚本演示 1 看 queen 这一行:原始计数的前三名是 the(331 次)、”.”(159)、”:“(95);换成 PPMI 后,前三名变成了 rules、lives、wears——描述”王室人物做什么”的动词。注意 crown、palace 不在里面:它们离 queen 3 个词以上,不在 ±2 的窗口里。窗口大小决定了向量能”看到”什么,这一点 §3.7 还会回来。
3.4 路线二:训练一个预测器(word2vec)
另一条路线来自神经网络。Bengio et al.(2003)的神经语言模型(§5.1)里,每个词先被映射成一个可训练的稠密向量;训练”预测下一个词”的副产品,就是一套词向量。可那个模型太慢,词向量只是副产品。
Mikolov et al.(2013)的 word2vec 把这件事做到了极致:干脆不要语言模型了,只训练一个极简的任务——skip-gram:给定中心词,预测它的邻居。再配上负采样(negative sampling),把”在整个词表上做 softmax”换成”判断一个词是不是真实邻居”的二分类:
是中心词的”输入向量”, 是真实邻居的”输出向量”, 是按词频的 0.75 次方随机抽的 个”假邻居”。真实邻居的点积被推大,假邻居的被推小。训练这样一个浅层模型极快,Google 用它在约 1000 亿词的新闻语料上训练出了 300 万个词的向量并公开发布,词向量从此成为 NLP 的标准组件。一年后,Stanford 的 GloVe(Pennington et al. 2014)提出直接拟合共现次数的对数,效果相当。
embeddings.py 用 numpy 实现了 SGNS(skip-gram + 负采样),包括 word2vec 的高频词下采样:每个 token 以 的概率被丢掉( 是它的频率),免得 the、”.” 占满训练时间。32 维、3 遍,在 23 万词上几秒钟就训练完。
3.5 两条路线本是一家
Levy & Goldberg(2014)证明了一个漂亮的结论:SGNS 在隐式地分解一个”平移过的 PMI 矩阵”()。也就是说,路线一和路线二是同一个目标的两种算法。Levy, Goldberg & Dagan(2015)进一步发现,word2vec 早期的很多优势来自一些”超参数小技巧”(上下文分布的 0.75 次方平滑、下采样……),把这些技巧移植到 PPMI-SVD 上,两者的差距基本消失。
02 号脚本演示 2 印证了这一点:queen 在两种方法里的最近邻都是 king、lady、lord(PPMI-SVD:king 1.00、lady 0.99、lord 0.99;SGNS:lady 0.96、king 0.95、lord 0.94)。
3.6 词向量学到了什么
“相似”是可替换性。queen 的邻居是 king、lady、lord,不是因为它们”意思相同”,而是因为它们可以出现在同样的位置上。再看一个首都 grorken(volel 的首都):两种方法给出的前 10 个邻居里,别的首都占了 6 个(PPMI-SVD)和 5 个(SGNS)——这是”同一类别”的相似;而两种方法的第 1 名都是 volel 的语言 driltese,PPMI-SVD 还把 volel 本身排在第 2——这是”同一话题”的相似(relatedness)。做检索时这两种相似都会出现(第 7 讲):和查询最像的,常常是同一类的别的东西。
类比。word2vec 最出名的例子是 (Mikolov, Yih & Zweig 2013):如果”性别”在向量空间里是一个固定的方向,king 加上这个方向就落在 queen 附近。02 号脚本演示 3 测了两组类比:
| 词向量 | 性别类比(90 题)完全答对 | 答到”分布孪生词”也算对 | 国家 → 首都(552 题) |
|---|---|---|---|
| PPMI-SVD(10 维) | 51.1% | 82.2% | 10.0% |
| PPMI-SVD(50 维) | 61.1% | 81.1% | 99.8% |
| SGNS(32 维) | 60.0% | 85.6% | 96.4% |
两个现象值得细想:
- 性别类比只有六成答对,但大部分错误是”答成了分布孪生词”。man : woman :: king : ? 答成了 lady 而不是 queen。看
pretrain_world.py就明白:一个人物词出现在什么语境里,只由”王室 / 年轻 / 家庭 / 性别”四个特征决定,king 和 lord 的特征完全一样,所以它们在语料里的分布一模一样(SGNS 里 cos(king, lord) = 0.960)。**没有任何只看共现的方法能把 queen 和 lady 分开。**如果孪生词之间随机选一个,“完全答对”的期望值正好是 54/90——和表里的 54、55 几乎一样。这是分布式假设最纯粹的形式:词的意思 = 它的上下文分布;分布相同,向量就相同。 - 国家 → 首都的类比在 50 维时几乎全对,10 维时只有 10%。一个国家和它的首都总出现在同一篇文章里,共享”这篇文章的词”(语言、领导人……);向量要有足够多的维度,才能给 24 个国家各留一个”话题方向”。
3.7 静态词向量的盲点
多义词(演示 4)。bank 在语料里一半出现在金融语境(money、loan),一半出现在河岸语境(river、fish);vault 只在金融语境,shore 只在河岸语境。PPMI-SVD(50 维)里,cos(bank, vault) = 0.73、cos(bank, shore) = 0.66,而 cos(vault, shore) = −0.00;用 vault 和 shore 去拟合 bank,得到 。一个词只有一个向量,它就只能是各种意思的加权平均——Arora et al.(2018)证明了词向量近似是各个词义向量的线性叠加。“river bank” 和 “bank loan” 里的 bank 应该是两个不同的向量:这要等到 §7 的上下文相关表示。
反义词(演示 5)。cold 和 warm 出现在完全相同的位置(“the soup was ___”),分布式方法很难把它们分开:
| 词向量 | 同极性形容词的平均相似度 | 褒 vs 贬的平均相似度 | 最近的形容词与自己同极性 |
|---|---|---|---|
| PPMI-SVD(50 维) | 0.999 | 0.999 | 9/20 |
| SGNS(打开下采样) | 0.965 | 0.925 | 20/20 |
| SGNS(关闭下采样) | 0.999 | 0.999 | 12/20 |
PPMI-SVD 和关闭下采样的 SGNS 都分不开褒贬(“最近的形容词”是褒是贬接近随机)。打开下采样的 SGNS 却完全分开了——这不是因为它”更懂语义”,而是因为下采样在构造窗口之前就删掉了 ”.”、”:” 这些高频词,窗口被”撑大”了,”… cold . rating : negative” 里的 negative 进了 cold 的窗口(Levy et al. 2015 讨论过这种”脏下采样”)。极性信息来自评分标签,不来自形容词本身。
这个小插曲给出了一条贯穿全课的教训:**表示里有什么,取决于训练信号里有什么。**词向量能区分褒贬,当且仅当上下文里有能区分褒贬的东西。§8 会看到,同一条教训放在 GPT 身上就是:“模型会做什么任务,取决于预训练语料里出现过什么任务。”
3.8 对提示工程意味着什么
- 嵌入向量今天仍然是检索的基础:RAG 系统用嵌入找”和问题最相似的文档段落”(第 7 讲)。你要知道”相似”至少有两种(同类 vs 同话题),也要知道它可能把反义的东西判成相似。
- “提示里的词换成同义词,模型应该不受影响”——这个直觉的基础正是分布式表示:同义词有相近的表示。但 §8.4 会看到,在格式词(“q :” vs “Q :“)这种地方,模型的表示不一定相近,小改动可能带来大变化。
4. 从词到句子:句向量 · Sentence embeddings
4.1 为什么需要句子向量
很多应用要比较的是句子或段落:搜索(哪段文档回答了这个问题?)、去重、聚类、分类,以及今天最重要的 RAG(第 7 讲)。我们需要一个函数,把任意长度的文字变成一个固定长度的向量,使得”意思相近的句子,向量相近”。
4.2 最简单的两种:词袋
- 平均词向量:句子向量 = 句子里所有词向量的平均。Arora, Liang & Ma(2017)发现,加上合适的加权(按词频降权)和去掉一个公共方向,这个”简单得过分”的基线在很多语义相似度任务上很难被打败。
- TF-IDF:每个词一维,值 = 词频 × log(总句数 / 含这个词的句数)。几乎每句都有的词(the、was)权重接近 0,稀有的词(not、cold)权重大。这是搜索引擎几十年来的基础做法,第 7 讲的 BM25 是它的改进版。
两者有一个共同点:不管词的顺序,所以叫”词袋”(bag of words)。
4.3 词袋的盲点
03 号脚本把这两种向量和”上下文相关的句子向量”(§6–§8 的迷你 GPT 在句子各个位置上的最后一层向量,取平均)放在一起比较。先看几对句子的余弦相似度(演示 1):
| 句子对 | 情感关系 | 平均词向量 | TF-IDF | 迷你 GPT |
|---|---|---|---|---|
| “the soup was cold but the bread was fresh” vs “the bread was fresh but the soup was cold” | 相反 | 1.000 | 1.000 | 0.961 |
| “the soup was cold” vs “the soup was not cold” | 相反 | 0.995 | 0.899 | 0.886 |
| “the soup was cold” vs “the bread was stale” | 相同 | 0.996 | 0.000 | 0.994 |
调换 but 前后两半,词袋完全不变,可两句话的情感正好相反。迷你 GPT 的向量对顺序敏感(0.961,比表里所有”同情感”的句子对都低)。TF-IDF 给 not 很高的权重,所以对 not 句的相似度掉得多一些——但它并不知道 not 会反转意思,只知道”多了一个稀有词”。
再直接测”能不能用来分类”:用 2000 条带标签的训练评论做 k 近邻(k = 7),给 300 条新评论分类(演示 2):
| 句子向量 | 简单句 | but 句 | not 句 | 总体 |
|---|---|---|---|---|
| 平均词向量 | 100.0% | 33.3% | 75.3% | 81.3% |
| TF-IDF | 97.1% | 43.9% | 32.9% | 71.3% |
| 迷你 GPT | 100.0% | 100.0% | 100.0% | 100.0% |
- 简单句(所有形容词同一极性)大家都能做对:数一数褒义词、贬义词就够了。
- but 句上两种词袋方法都在随机水平或更低:一句 but 句里褒贬两种词都有,词袋不知道哪一半算数。
- not 句上 TF-IDF 只有 32.9%:和 “the coffee was not warm” 字面最像的,常常是同样的词、只是没有 not 的句子,标签正好相反。演示 3 拿这句话去检索,TF-IDF 的第 2、3 名都是 “the coffee was warm”(positive),前 10 名里有 7 条情感和查询相反。
- 迷你 GPT 的向量三类全对。它从来没被教过”情感”,只是为了预测 “rating :” 后面的词,必须在内部把 not、but 的作用算清楚。好的表示是预测任务的副产品——这句话是从 word2vec 到 GPT 一以贯之的主题。
4.4 学出来的句向量
从 2014 年起,研究者开始专门训练句子编码器:
| 方法 | 年份 | 核心思想 |
|---|---|---|
| Paragraph Vector(doc2vec) | Le & Mikolov 2014 | 给每段文字一个可训练的向量,和词向量一起预测段落里的词 |
| Skip-Thought | Kiros et al. 2015 | 用 RNN 编码一句话,训练它去生成前一句和后一句(把 skip-gram 搬到句子层面) |
| InferSent | Conneau et al. 2017 | 在自然语言推理数据(SNLI:判断两句话是蕴含、矛盾还是中立)上有监督地训练编码器 |
| Universal Sentence Encoder | Cer et al. 2018 | 多任务训练的 Transformer / DAN 编码器 |
| Sentence-BERT | Reimers & Gurevych 2019 | 把 BERT 改成”孪生网络”,对输出做平均池化,用句子对训练 |
Sentence-BERT 的动机很能说明问题:用原始的 BERT 比较两句话,要把它们拼在一起送进模型(cross-encoder),在 1 万个句子里找最相似的一对需要约 65 小时;把每句话单独编码成向量再比较余弦(bi-encoder),只要约 5 秒。这个”精确但慢的 cross-encoder vs 快速可索引的 bi-encoder”的取舍,第 7 讲讲重排(reranking)时还会遇到。今天 RAG 系统用的嵌入模型,基本都是这条路线的后代:一个 Transformer 编码器,用大量的”相关文本对”做对比学习(contrastive learning)训练出来。
5. 神经语言模型:从 Bengio 到注意力 · Neural language models
5.1 Bengio et al.(2003):用向量代替符号
回到 §2.5 的两难:n-gram 看得近就不连贯,看得远就没见过。Bengio, Ducharme, Vincent & Jauvin(2003)的《A Neural Probabilistic Language Model》提出了破局的思路:
- 每个词映射到一个可训练的稠密向量(§3 的词向量就是从这里起源的);
- 把前 个词的向量拼起来,送进一个神经网络,输出下一个词的分布;
- 词向量和网络一起训练。
关键在于:模型不再要求”前 个词”这个字符串在训练里原样出现过。“the princess wears a ___” 的前文从没见过也没关系,因为 princess 的向量和 queen 的向量相近,网络对它们的输出也相近。泛化从”相同的字符串”变成了”相似的向量”——这是神经语言模型超越 n-gram 的根本原因。
5.2 RNN 与 LSTM:理论上无限的上下文
Bengio 的模型仍然只看固定的前 个词。循环神经网络(RNN)语言模型(Mikolov et al. 2010)把窗口去掉了:维护一个隐藏状态 ,每读一个词更新一次:
理论上, 可以携带任意远的信息。实际上,梯度在很多步之间连乘,会消失或爆炸,普通 RNN 很难记住 10 步以外的东西。LSTM(Hochreiter & Schmidhuber 1997)用”门”控制信息的写入、保留和读出,缓解了这个问题,在 2010 年代中期成为语言建模和机器翻译的标准结构。
5.3 seq2seq 与信息瓶颈
机器翻译需要把一个序列变成另一个序列。Sutskever, Vinyals & Le(2014)的 seq2seq 用一个 LSTM(编码器)读完原文,把最后的隐藏状态交给另一个 LSTM(解码器)生成译文。问题很明显:整句原文被压进一个固定长度的向量——和 §4 的句向量是同一个瓶颈。句子越长,翻译越差。
5.4 注意力的诞生
Bahdanau, Cho & Bengio(2015)给出了解法:解码器每生成一个词时,回头看编码器在每个位置的隐藏状态,按”和当前需要的信息有多相关”给它们打分,取加权平均作为额外的输入。翻译到某个词时,模型会把注意力集中在原文里对应的那几个词上。
这就是注意力(attention)。它把”把一切压进一个向量”换成了”需要什么,就回头取什么”。三年后,有人问了一个大胆的问题:既然注意力这么好用,还要 RNN 干什么?
5.5 RNN 的两个硬伤
- 串行: 依赖 ,训练时无法在序列长度上并行,GPU 的大量算力用不上。
- 远距离遗忘:即使有 LSTM,信息要经过很多步的”传话”才能从开头到达结尾,每一步都可能丢失。
Transformer 同时解决了这两个问题:每个位置直接看所有其他位置(一步到达),而且所有位置可以同时计算。
6. Transformer · The Transformer
6.1 “Attention Is All You Need”
Vaswani et al.(2017)提出的 Transformer 完全去掉了循环结构,只用注意力和逐位置的前馈网络,在 WMT 2014 英德翻译上取得了当时最好的结果,训练成本却只是之前最好模型的一小部分。今天几乎所有大语言模型都是它的变体。
6.2 自注意力:一个软的字典查找
自注意力(self-attention)让序列里的每个位置都去”查询”所有位置。每个位置的向量 经过三个可学习的线性变换,得到:
- query :我在找什么;
- key :我这里有什么(用来被别人找到);
- value :我能提供什么。
直觉是一个软的字典查找。普通字典”完全匹配才返回”;注意力是”越像,取得越多”,返回所有 value 的加权平均。04 号脚本演示 1 用一个 4 个条目的”字典”展示:query 找”颜色”时,锐利度 β = 1 的权重是 0.40 / 0.15 / 0.15 / 0.30(取回的 value 是一个平均值 23.4),β = 10 时变成 0.95 / 0.00 / 0.00 / 0.05(几乎就是硬查找)。和谁都不像的 query,权重完全平均——注意力不会说”没找到”,它总会返回点什么。
6.3 为什么除以 √d
如果 、 的每一维是均值 0、方差 1 的随机数,点积 是 个随机数之和,标准差是 。不缩放时,维度越高,分数差距越大,softmax 越接近 one-hot(演示 2,16 个 key、各 2000 次):
| 维度 | 不缩放:最大权重 | 不缩放:熵 | 除以 √d:最大权重 | 除以 √d:熵 |
|---|---|---|---|---|
| 4 | 0.435 | 1.74 | 0.237 | 2.38 |
| 64 | 0.835 | 0.43 | 0.243 | 2.36 |
| 1024 | 0.960 | 0.10 | 0.244 | 2.36 |
(均匀分布的熵是 ln 16 = 2.77。)饱和的 softmax 梯度几乎为 0,训练会非常困难。除以 √d 之后,不管 多大,初始的注意力分布都差不多,模型可以通过学习自己把分数拉开。
6.4 多头、位置、残差与 MLP
- 多头(multi-head):把向量切成 段,每段各做一份注意力再拼起来。不同的头可以学会”看”不同的东西(§6.6 会看到一个头找国名、另一个头找关系词)。
- 位置编码:注意力本身对顺序”无感”——把输入打乱,每个位置得到的加权平均只是跟着换了位置。必须额外告诉模型”谁在第几位”。原始 Transformer 用固定的正弦函数,GPT 系列用可学习的位置向量(本讲的迷你 GPT 也是),今天的很多模型用旋转位置编码(RoPE)。
- 残差连接 + LayerNorm:,让信息和梯度可以直接穿过很多层。
- MLP(前馈网络):在每个位置上独立地”加工”注意力取回来的信息。研究表明大量事实性知识存储在 MLP 层里(Geva et al. 2021)。
一层 Transformer = 注意力(在位置之间搬运信息)+ MLP(在每个位置上加工信息)。
6.5 编码器、解码器与因果遮罩
原始 Transformer 是”编码器 + 解码器”,用于翻译。后来分化出两条路线:
- 只用编码器(BERT):每个位置可以看左右两边(双向),适合理解类任务(分类、抽取、做句向量);
- 只用解码器(GPT):每个位置只能看自己和左边,适合生成——因为生成时右边的词还不存在。
“只看左边”由因果遮罩实现:把每个位置右边的分数设成 ,softmax 之后权重恰好是 0(演示 3 打印了一个 6×6 的权重矩阵,右上三角全是 0.00)。它还带来一个训练上的巨大好处:一段 个 token 的文本,可以一次算出全部 个位置的”下一个词”预测,每个位置都不会偷看答案。
对提示工程来说,因果遮罩意味着:提示里的内容只能影响它后面的部分。把指令放在要处理的文本前面,模型读文本时已经”知道”任务;放在后面,模型读文本时还不知道要做什么,只能到最后再回头看。第 2 讲会讨论这类”位置”的影响。
6.6 归纳头:两层注意力的一个电路
注意力能实现什么样的”算法”?Anthropic 的 Olsson et al.(2022)在真实 Transformer 里找到了一个特别重要的电路——归纳头(induction head):看到 ”… A B … A” 时,预测下一个是 B。也就是”照着前文抄”。
它需要两层注意力串起来:
- 前一个词头(第 1 层):每个位置去看它左边那个位置,把”我前面的词是谁”写到自己身上;
- 归纳头(第 2 层):拿”我是谁”去找”哪个位置的前一个词是我”——那个位置紧跟在当前词的上一次出现之后——把那里的词抄回来。
attention_np.py 不训练,直接手工设定权重搭出这个电路(残差流 = 当前词 one-hot | 位置 one-hot | 前一个词)。在”10 个随机词重复两遍”的序列上测试(演示 4,300 条,词表 20 个):
| 模型 | 前一半(没法抄) | 后一半(可以抄) | 正确的词得到的权重 |
|---|---|---|---|
| 完整电路(β = 20) | 0.0% | 100.0% | 1.00 |
| 锐利度 β = 2 | 0.0% | 100.0% | 0.21 |
| 没有位置信息 | 0.0% | 11.1% | 0.23 |
| 只有第 2 层 | 0.0% | 0.9% | 0.07 |
- 没有位置信息,第 1 层就找不到”左边那个位置”,电路失效——这是 Transformer 必须加位置编码的一个具体原因。
- 只有第 2 层也不行:一个头只能做一次查找,“先查前一个词、再按它去匹配”必须两层串起来(Elhage et al. 2021 把这种”后一个头的 key 用到前一个头的输出”叫作 K-composition)。这是”深度”的一个具体用处。
- 锐利度降到 2,准确率仍是 100%,但正确的词只得到 0.21 的权重:准确率看不出的退化,置信度看得出(第 5 讲讲校准时会再遇到)。
(写这个电路时还踩了一个坑:位置 0 左边没有词,因果遮罩下它只能”看自己”,结果在重复的第一个词上,归纳头同时匹配到位置 0 和位置 1,两者的分数只差 ,每次都选错。真实模型用一个开头的特殊 token——如 GPT-2 的 <|endoftext|>——来处理”开头没东西可看”的问题。)
Olsson et al. 发现,归纳头在训练早期突然出现,而它出现的时刻正好和模型”上下文学习”能力的突然提升重合。他们据此认为,归纳头是大模型上下文学习能力的一个重要来源。第 2 讲会回到这个电路。
最后看看我们自己训练的迷你 GPT(演示 5)。给它 96 道问答题 <doc> faq : q : <问题> a :,看它在马上要写答案的位置把注意力放在哪里:第 0 层的头 0 把平均 86% 的注意力放在国名上,头 3 是 72%;头 1 把 72% 放在关系词(capital、of、known……)上。一个头取回”问的是哪个国家”,另一个头取回”问的是什么”——这就是 §6.2 的软查找在一个真实的(虽然很小的)语言模型里的样子。
可这个迷你 GPT 完全不会抄写:把 12 个首都名、人名随机排成一串、重复两遍,它在第二遍的 1100 个位置上一次都没预测对下一个词。它的训练语料里从来没有”重复出现的随机序列”,抄写从来没被奖励过,所以它没有长出归纳头。真实语料里名字、短语反复出现,归纳头才会在训练早期出现。能力从哪里来?——来自训练数据奖励了什么。
6.7 对提示工程意味着什么
- 上下文窗口是模型能看见的全部世界。窗口外的东西对输出没有任何影响(§2.6 是一个极端的例子;今天的窗口长达几十万 token,但”太长的上下文里,中间的信息容易被忽略”仍然是真实的问题,第 7 讲的 “lost in the middle”)。
- 注意力 = 模型”看”提示的方式。好的提示让关键的信息容易被”查到”:清楚的结构、一致的格式、明确的标记。
- 抄写是一种很强的能力。模型会倾向于照着上下文里出现过的模式写——这是少样本示例起作用的一个机制,也是”示例里的错误会被照抄”的原因(第 2 讲)。
7. 预训练范式:先预训练,再微调 · The pre-training era
配套代码:
05_tiny_gpt.py演示 1–2
7.1 ELMo:上下文相关的词向量
§3.7 的多义词问题,在 2018 年被 ELMo(Peters et al. 2018)正面解决:先在大语料上训练一个双向 LSTM 语言模型,然后不再给每个词一个固定的向量,而是把这个语言模型在这句话里每个位置的各层隐藏状态加权组合,作为这个词在这句话里的表示。“river bank” 和 “bank loan” 里的 bank 于是得到两个不同的向量。把 ELMo 向量加进当时各个任务的现成模型,六个基准任务的最好成绩都被刷新了。
ELMo 的意义不止于多义词:它证明了语言模型的内部表示是可以迁移的通用知识。
7.2 ULMFiT 与 GPT-1:预训练 + 整体微调
同一年,ULMFiT(Howard & Ruder 2018)和 OpenAI 的 GPT-1(Radford et al. 2018,《Improving Language Understanding by Generative Pre-Training》)走得更远:不只是借用语言模型的表示,而是把整个预训练好的语言模型拿来,在每个下游任务上继续训练(微调)。
GPT-1 是一个 12 层的 Transformer 解码器(1.17 亿参数),在 BooksCorpus(约 7000 本未出版的书)上做”预测下一个词”的预训练。微调时,它用一个现在看来很有启发性的办法处理不同的任务:把任务的输入改写成一段文字。例如判断两句话是否蕴含,就把它们用一个分隔符拼成一串 token 送进模型。这是”把任务塞进语言模型熟悉的格式”这一思想的雏形。
7.3 BERT:掩码语言模型
几个月后,Google 的 BERT(Devlin et al. 2019)换了一个预训练目标:掩码语言模型(masked LM)——随机遮住 15% 的 token,让模型根据左右两边的上下文去猜(外加一个”判断两句话是不是相邻”的任务)。双向的上下文让 BERT 在理解类任务上远超 GPT-1,刷新了 11 个 NLP 任务的最好成绩(BERT-base 1.1 亿参数,BERT-large 3.4 亿参数)。2019 年前后,“BERT + 微调”是 NLP 的标准做法——这正是 §1.2 的范式 ③。
但 BERT 不擅长生成:它的训练目标是填空,不是从左往右续写。后来的历史证明,正是”会续写”这一点,让 GPT 路线走向了提示。
7.4 T5:一切都是 text-to-text
Google 的 T5(Raffel et al. 2020)把所有 NLP 任务统一成”输入一段文字,输出一段文字”:翻译的输入写成 translate English to German: That is good.,摘要的输入以 summarize: 开头,分类任务直接输出标签的文字。T5 在约 750 GB 的清洗过的网页文本(C4)上预训练,最大的版本有 110 亿参数。
注意那个任务前缀:它是一条写在输入里的、给模型看的”指令”。T5 仍然要在每个任务上训练,但”用输入里的文字告诉模型做什么任务”这个想法,已经离提示只有一步之遥。
7.5 那个年代的”提示”
即使在微调为主的年代,研究者也开始用”提示”去探测和利用预训练模型:
- LAMA(Petroni et al. 2019):用完形填空的句子 “Dante was born in [MASK].” 去问 BERT,发现预训练模型里存着大量事实知识——“语言模型即知识库”。(§8.5 我们用同样的”百科句式”去问迷你 GPT。)
- PET(Schick & Schütze 2021):把分类任务改写成完形填空(”… It was [MASK].”),再把标签映射成词(positive → “great”),在只有几十个样本时大幅超过直接微调。这里出现了两个第 2 讲会反复用到的概念:模板(template)和标签词(verbalizer)。
- AutoPrompt(Shin et al. 2020):用梯度搜索一串”触发 token”,自动找出能让模型输出正确答案的提示——“自动提示”的早期形态(第 10 讲)。
- Prefix-tuning(Li & Liang 2021)与 prompt tuning(Lester et al. 2021):干脆不用真实的词,而是在输入前面加一串可训练的向量(“软提示”),模型参数冻结,只训练这几十个向量。Lester et al. 发现,模型越大,prompt tuning 越接近整体微调的效果。原课 syllabus 早期版本里提到的 “prompt-tuning” 就是指这一类方法。
7.6 在笔记本上预训练一个迷你 GPT
现在我们自己预训练一个。tiny_gpt.py 是一个 GPT 结构的模型(词嵌入 + 位置嵌入 → 2 层因果 Transformer → 输出层,输出层和词嵌入共享权重),在 §0.6 的语料上训练 3000 步(每步 32 段 × 64 个 token,AdamW,学习率预热 + 余弦衰减)。05 号脚本演示 1:
| 训练步数 | 训练损失 | 验证损失 | 验证困惑度 |
|---|---|---|---|
| 500 | 0.681 | 0.694 | 2.00 |
| 1000 | 0.660 | 0.657 | 1.93 |
| 2000 | 0.616 | 0.637 | 1.89 |
| 3000 | 0.585 | 0.636 | 1.89 |
损失从随机猜的 ln 290 ≈ 5.67 降到 0.636。训练只有一个目标——预测下一个词,没有人告诉它什么是首都、什么是褒贬。验证损失和训练损失几乎一样,说明它没有死记训练句子。(第一版语料只有现在的 1/3,模型相当于把语料看了 80 遍,验证损失从第 1000 步起就开始上升——数据太少时模型会背答案。这就是为什么 pretrain_world.py 的配方是现在的大小。)
和真实的 GPT 比一比规模:
| 模型 | 参数 | 层数 | 向量维度 | 上下文 | 训练数据 |
|---|---|---|---|---|---|
| 本讲的迷你 GPT | 258 K | 2 | 96 | 64 | 约 23 万词(玩具语料) |
| GPT-1(2018) | 117 M | 12 | 768 | 512 | BooksCorpus(约 7000 本书) |
| GPT-2(2019) | 1.5 B | 48 | 1600 | 1024 | WebText(约 40 GB 网页文本) |
| GPT-3(2020) | 175 B | 96 | 12288 | 2048 | 约 3000 亿 token |
再按文档类型比较测试困惑度(演示 2,45,179 个测试 token):
| 模型 | article | faq | quiz | reviews | story | 全部 |
|---|---|---|---|---|---|---|
| 3-gram | 3.492 | 2.236 | 2.733 | 2.378 | 2.659 | 2.616 |
| 7-gram | 2.012 | 1.545 | 2.586 | 2.240 | 2.137 | 2.042 |
| 迷你 GPT | 1.506 | 1.480 | 2.256 | 2.047 | 1.968 | 1.821 |
迷你 GPT 在每一类文档上都比最好的 n-gram 好,差距最大的是百科词条(7-gram 的困惑度是它的 1.34 倍):词条里要预测的国名、首都依赖同一篇文章前面的内容,在 64 个 token 的窗口里,迷你 GPT 都看得见,而且不要求这些上下文原样出现过。(这份语料的随机性有一个谁都低不过去的下限,所以总体差距看起来不大;但在”需要远处信息的那几个词”上,差别是决定性的——§8 会专门看这些词。)
8. GPT-2 与 GPT-3:提示的诞生 · The birth of prompting
配套代码:
05_tiny_gpt.py演示 3–5、06_prompt_formats.py
8.1 GPT-2:语言模型是无监督的多任务学习者
2019 年的 GPT-2(Radford et al. 2019)只做了一件事:把 GPT-1 放大十几倍(15 亿参数),在更大、更杂的网页文本上训练。WebText 来自 Reddit 上获得至少 3 个赞的外链网页,去重后约 800 万篇文档、40 GB。
论文的标题就是它的论点:《Language Models are Unsupervised Multitask Learners》。论证是这样的:网页里自然地包含了各种任务的例子——有人在文章后面写 “TL;DR:” 然后给出一句话总结,有人写 “英文句子 = 法文句子” 的对照,有人写问答。一个足够大的语言模型为了把这些文本预测好,就不得不学会摘要、翻译、问答。于是不需要任何微调,只要用一个格式把任务”唤起”:
- 在文章后面加上
TL;DR:,让模型续写,就得到摘要(论文用 top-k = 2 的采样生成 100 个 token); - 给几对 “english sentence = french sentence”,再给一句英文和 ”=“,就得到翻译;
- 给一段文章和问题,再写 “A:“,就得到答案。
这就是零样本(zero-shot)提示。GPT-2 的零样本结果大多远不如专门训练的系统,但方向已经清楚了。(OpenAI 当时担心被滥用,分阶段发布了 GPT-2 的模型权重,这在当时引起了很大争议。)
我们的迷你 GPT 上有一个迷你版的 “TL;DR”(05 号脚本演示 4)。训练语料里,评论页的每条评论后面都跟着 rating : positive/negative。所以只要把一条新评论写成 <doc> reviews : review : the soup was not cold . rating :,比较 positive 和 negative 作为下一个词的概率,就是一个零样本情感分类器:
| 模型 | 简单句 | but 句 | not 句 |
|---|---|---|---|
| 3-gram | 52.4% | 59.6% | 53.4% |
| 5-gram | 100.0% | 100.0% | 17.8% |
| 7-gram | 100.0% | 100.0% | 100.0% |
| 迷你 GPT | 100.0% | 100.0% | 100.0% |
(300 条新评论。)3-gram 只看得见 “rating :“,只能猜;5-gram 看得见最后一个形容词,所以简单句和 but 句(以 but 后面为准,决定性的词正好在最后)都对,not 句却只有 17.8%——“not” 在它的窗口外,它看到 cold 就说 negative。7-gram 的窗口刚好够到 not。在模板化的玩具语言里,“把窗口开大一点”就够了;真实语言里,决定性的词可以在几十个词之外。迷你 GPT 三类全对——而它的”提示”只是模仿训练语料里评论页的格式,没有任何指令。
8.2 GPT-3:少样本学习与”上下文学习”
2020 年的 GPT-3(Brown et al. 2020,《Language Models are Few-Shot Learners》)又放大了 100 多倍:1750 亿参数,约 3000 亿 token 的训练数据,上下文窗口 2048 个 token。论文系统地定义了三种提示方式,全部不更新参数:
- 零样本:只有任务描述(“Translate English to French:“)和输入;
- 单样本:再加一个示例(“sea otter => loutre de mer”);
- 少样本:加若干个示例,能塞进上下文窗口多少就放多少(通常 10–100 个)。
GPT-3 的核心发现是:模型越大,从上下文里的示例中学到的越多——少样本比零样本的提升,随规模增长而变大。在一些任务上,少样本的 GPT-3 接近甚至超过了专门微调的模型。论文把这种能力叫作上下文学习(in-context learning)。原课主页摘录的正是 GPT-3 摘要里的这层意思:不做梯度更新、不微调,任务和示例全靠文字给出——这就是这门课的起点。
示例到底在”教”模型什么?06 号脚本演示 3 给了一个干净的例子。把问答页的 “q :” / “a :” 换成模型没见过的 “question :” / “answer :“,模型看到的是 <unk> : what is the capital of volel ? <unk> :,零样本准确率是 0(§8.4)。现在在前面加几个同样格式的示例:
| 格式 | 0 个示例 | 1 个 | 2 个 | 4 个 |
|---|---|---|---|---|
| question / answer(词表里没有) | 0.0% | 29.2% | 91.7% | 100.0% |
| 问题后面直接跟答案(没有 a :) | 0.0% | 0.0% | 0.0% | 0.0% |
(96 道题;示例取自别的国家。)模型仍然不认识 question、answer 这两个词,但两个示例足以让它看出结构:“<unk> : 问题 ? <unk> : 答案 .”。**示例首先告诉模型的是格式:这是一个问答页,答案写在哪里。**第 2 讲会看到,Min et al.(2022)在真实模型上发现了类似的现象:示例里的标签就算是随机的,少样本提示的效果也只下降一点点——示例的作用很大一部分在于告诉模型输入长什么样、标签有哪些、格式是什么。
第二行同样重要:如果格式要求”问题后面直接写答案”,示例给到 4 个也是 0。在训练语料里,”?” 后面从来不直接出现答案(总是 “a :” 或下一道题),这个预训练形成的习惯强到几个示例扳不过来。Wei et al.(2023)在真实模型上发现,小模型很难用上下文里的示例推翻预训练形成的先验(例如示例里的标签被故意翻转时,小模型仍按自己的先验输出),只有足够大的模型才会跟着示例走。
示例还有成本:它们占用上下文窗口。在 question / answer 格式下,4 个示例的提示平均 62 个 token,有 24/96 个超出了迷你 GPT 64 个 token 的窗口,最前面的部分被截掉了。真实模型的窗口大得多,但”示例越多越好”在成本和长度上都有边界。
8.3 base 模型只是在续写文档
现在可以把 GPT-2 / GPT-3 的经验说得更清楚:一个 base 模型只是在续写文档。它读你的提示时,在隐式地猜”我正在写的是哪一种文档”,然后按那种文档的规律写下去。
06 号脚本演示 1 把同一个问题放在不同”文档”的开头。下表的 P(a) 是”下一个词是 a(开始答题)“的平均概率,P(题) 是”下一个词是 what / who(接着出下一道题)“的平均概率,“答出”指贪心续写的前 8 个词里出现了正确答案:
| 提示 | 问答见过的国家:P(a) / P(题) / 答出 | 只在百科出现的国家:P(a) / P(题) / 答出 |
|---|---|---|
光秃秃的问题 <doc> what is the capital of X ? | 0.46 / 0.47 / 70.3% | 0.00 / 0.89 / 0.0% |
题目列表 <doc> quiz : … | 0.00 / 1.00 / 0.0% | 0.00 / 1.00 / 0.0% |
问答页(只有页眉)<doc> faq : … | 0.92 / 0.08 / 100.0% | 0.33 / 0.67 / 31.2% |
问答页 + q : | 1.00 / 0.00 / 100.0% | 0.99 / 0.01 / 93.8% |
问答页 + q : + a : | — / — / 100.0% | — / — / 93.8% |
- 光秃秃的一个问题,在语料里既可能是问答页里的问题(后面跟答案),也可能是题目列表里的一道题(后面跟下一道题)。模型在两种续写之间分配概率。问 volel 的首都,它写的是
what is the capital of vanda ? what is the capital of …——你问它一个问题,它接着出了五道题。GPT-3 之前的 base 模型都有这个毛病。 - 更有意思的是,这个”文档类型”的猜测取决于问题的内容:问答页里见过的国家,光秃秃的问题有 70.3% 被答出来;只在百科里出现过的国家,它们的问题在训练语料里只出现在题目列表中,模型就一律把它们当成题目列表(0%)。
- 一个小小的
q :标记,比faq :页眉更能说明”这里是问答”(P(a) 从 0.33 跳到 0.99)。
提示工程最早的、也是最核心的技巧由此而来:把提示写成”你想要的回答会自然出现在后面”的那种文档的开头。GPT-3 时代的提示指南里充满了这样的技巧:写 “Q: … A:“,写 “Here is a list of …: 1.”,把问题伪装成一篇”专家访谈”的开头……指令微调(§9、第 3 讲)在很大程度上就是为了让模型不再需要这种”伪装”。
8.4 提示的敏感性
如果模型在”认格式”,那格式上的小改动就可能有大影响。06 号脚本演示 2:
| 模板 | 模型词表里没有的词 | 问答见过的国家 | 只在百科的国家 |
|---|---|---|---|
训练时的格式 <doc> faq : q : … a : | – | 100.0% | 93.8% |
去掉页眉 faq : | – | 100.0% | 96.9% |
再去掉 <doc> | – | 100.0% | 96.9% |
| q / a 改成大写 Q / A | Q, A | 0.0% | 0.0% |
| q / a 改成 question / answer | question, answer | 0.0% | 0.0% |
改用百科的句式 the capital of X is | – | 100.0% | 100.0% |
(指标:续写的第一个词就是答案。)去掉页眉几乎没影响,因为 “q : … ? a :” 的结构本身就说明了”这里要答题”;可只是把 q / a 换成大写,或换成意思完全相同的 question / answer,准确率就从接近 100% 掉到 0——这些词不在迷你模型的词表里。分类任务也一样(演示 4):把 “rating :” 换成没见过的 “sentiment :“,准确率从 100% 掉到 55.3%(接近随机);给 2 个、4 个用 sentiment 写的示例,总体回到 88.7%,可 but 句只有 59.6% 和 68.4%——**示例救回了格式,却没有完全救回判断。**用模型熟悉的触发词,比教它一个新的触发词可靠得多。
真实的大模型词表大得多、见过的格式多得多,不会这么极端;但同样的现象在真实模型上被反复测到。Zhao et al.(2021)发现,GPT-3 的少样本准确率会因为提示格式、示例的选择、甚至示例的顺序,从接近随机变到接近最好。Sclar et al.(2024)只改分隔符、大小写、空格这类”无关紧要”的格式,LLaMA-2-13B 的准确率最多差了 76 个百分点。这就是为什么”这个提示更好”必须靠实验证明(第 2、17 讲)。
8.5 知识:跨格式迁移,却不能反转
05 号脚本演示 3 回答了一个对提示工程至关重要的问题:模型学到的是”字符串”还是”事实”?
(a) 跨格式。用问答格式问 8 个”只在百科里出现过”的国家:
| 7-gram | 迷你 GPT | |
|---|---|---|
| 问答见过的国家(64 题) | 100.0% | 100.0% |
| 只在百科出现的国家(32 题) | 0.0% | 93.8% [79.9, 98.3] |
7-gram 从没见过 “capital of <这个国家> ? a :” 这串字符,所以是 0。迷你 GPT 的首都知识是从百科的句子里学到的,却能用问答的格式取出来。知识被存成了”关于这个国家的事实”,而不是”某个句子”——这正是提示能起作用的前提:同一个知识可以用很多种问法问出来。(Allen-Zhu & Li 2023 发现,这种”可提取性”依赖于预训练数据里同一个知识有多种表述方式;我们的百科词条正是每次随机换措辞的。)
(b) 反转。训练语料里,首都两个方向的说法都有,领导人只有 “the leader of X is L” 一个方向:
问法(前面加 <doc> article :) | 7-gram | 迷你 GPT |
|---|---|---|
首都 · 正着问 the capital of X is | 24/24 | 24/24 |
首都 · 反着问 C is the capital of | 24/24 | 24/24 |
领导人 · 正着问 the leader of X is | 24/24 | 24/24 |
领导人 · 反着问 L is the leader of | 1/24 | 3/24 |
反着问领导人,迷你 GPT 只对了 3/24(随机猜约 1/24),虽然它 21/24 次答的都是某个国名——它知道”该答一个国家”,却不知道是哪一个。首都反着问能答对,只因为语料里本来就有那个方向的句子。
这就是 Berglund et al.(2023)报告的反转诅咒(reversal curse):在 “A 是 B” 上训练的模型,不会自动学会 “B 是 A”。这不只是小模型的问题:他们报告 GPT-4 回答”某位名人的母亲是谁”的正确率是 79%,反过来问”这位母亲的儿子是谁”只有 33%。原因可以从训练目标上理解:模型只被训练”从左往右”预测,“L 后面接 X”这种模式从来没有出现过,没有任何梯度会去建立”从 L 到 X”的联系。
对提示工程的启示:**提问的方向和格式,要和知识在训练数据里出现的方式对得上。**如果反向的问题答不出来,可以把它转化成正向的问题——比如对每个候选国家问一遍 “the leader of X is”,看哪个国家的答案是 L(练习 8)。这已经是”任务分解”(第 9 讲)的思路了。
8.6 规模:scaling laws 与”涌现”
从 GPT-1 到 GPT-3,参数涨了 1500 倍。为什么大家相信”越大越好”?
- Scaling laws:Kaplan et al.(2020)发现,语言模型的测试损失随参数量 、数据量 、计算量 呈幂律下降,跨越了 7 个数量级都很规整。这让”花更多算力能换来多少提升”变得可以预测。Hoffmann et al.(2022,Chinchilla)修正了参数和数据的最佳配比:在固定算力下,应该让数据量和参数量同步增长(大约每个参数 20 个 token),他们 700 亿参数、1.4 万亿 token 的 Chinchilla 超过了 2800 亿参数的 Gopher。
- 涌现:Wei et al.(2022)报告了许多”小模型完全不会、过了某个规模突然会”的能力,少样本的多步算术、思维链推理是典型例子(第 4 讲)。Schaeffer et al.(2023)反驳说,很多”突然出现”来自评估指标的非线性(例如完全匹配),换成连续的指标,提升其实是平滑的。
对提示工程来说,规模带来一个很实际的结论:提示是”针对某个模型”的。在一个模型上有效的技巧,在更小或更大的模型上可能无效甚至有害(§8.2 的 Wei et al. 2023 就是一例)。换模型之后要重新评估你的提示。
8.7 采样与温度
语言模型给出的是一个分布,生成时要从中采样。最常用的控制参数是温度 :把每个 token 的概率变成 再归一化(等价于 logits 除以 )。05 号脚本演示 5,提示 <doc> reviews : review :,每个温度采样 60 次:
| 温度 | 不同的评论数 | 合乎语法 | 评分与内容一致 |
|---|---|---|---|
| 0(贪心) | 1/60 | 100.0% | 100.0% |
| 0.5 | 60/60 | 100.0% | 100.0% |
| 1.0 | 60/60 | 98.3% | 98.3% |
| 1.5 | 60/60 | 66.7% | 65.0% |
| 2.0 | 60/60 | 11.7% | 10.0% |
贪心解码每次都写同一条(the bed was not cheap . rating : negative);温度升高,写出的评论越来越多样,到了 1.5、2.0 就开始胡言乱语——低概率的 token 被抽到的机会变大了。温度是”多样性 vs 可靠性”的旋钮:要稳定的答案(分类、抽取)用低温度;要多样的候选(头脑风暴、第 4 讲 self-consistency 的多次采样)用较高的温度。其他常见的采样参数还有 top-k(只在概率最大的 k 个 token 里采样)和 top-p / nucleus(只在累计概率达到 p 的最小集合里采样)。
9. 从 GPT-3 到 ChatGPT:让模型听指令 · From GPT-3 to ChatGPT
9.1 base 模型的三个问题
GPT-3 很强,但作为一个产品很难用:
- 不听指令:你问它一个问题,它可能接着写五个问题(§8.3);你写”请总结下面的文章”,它可能续写成一篇关于”如何写总结”的文章。用它要靠精心伪装的”文档开头”。
- 会编造:语言模型的目标是写出”像真的”文本,不是”真的”文本。没有见过的事实,它也会写一个最像样的答案——没有”不知道”这个选项(第 5 讲)。
- 会顺着写下去:网页里有偏见、有毒的内容,模型会忠实地学会并在合适的语境里续写它们(第 6 讲)。
9.2 指令微调
指令微调(instruction tuning)的想法很直接:既然模型不习惯”指令 → 回答”这种格式,那就收集大量用自然语言描述的任务,写成”指令 + 输入 → 输出”的样子,在上面微调。
- FLAN(Wei et al. 2022):把 60 多个 NLP 数据集改写成多种自然语言指令模板,微调一个 1370 亿参数的模型。在没见过的任务类型上,它的零样本表现在 25 个数据集里的 20 个上超过了零样本的 GPT-3(1750 亿参数)。
- T0(Sanh et al. 2022):同一时期,用众包写的大量提示模板做多任务训练,得到类似的结论。
指令微调的效果是:模型学会了”指令”这种文档格式。你不再需要把问题伪装成问答页,直接说”回答下面的问题”就行。第 3 讲会在 §7.6 的迷你 GPT 上真的做一次指令微调,看它对同一组提示的反应怎样变化。
9.3 RLHF:InstructGPT
指令微调教会了模型格式,但”什么是一个好的回答”很难用标注数据写清楚。OpenAI 的 InstructGPT(Ouyang et al. 2022)加了一步基于人类反馈的强化学习(RLHF):
- 请标注员针对提示写示范回答,做有监督微调(SFT);
- 让模型对同一个提示生成几个回答,请标注员排序,用排序数据训练一个奖励模型;
- 用强化学习(PPO)优化模型,让它的回答得到更高的奖励。
最惹人注目的结果是:标注员更喜欢 13 亿参数的 InstructGPT 的回答,而不是 1750 亿参数的 GPT-3——对齐带来的提升超过了 100 倍的规模。代价也被记录了下来:在一些公开的 NLP 基准上,RLHF 后的模型表现下降了,论文称之为对齐税(alignment tax),并用混合预训练数据的办法缓解(第 6 讲)。RLHF 还被发现会让模型的校准变差、变得谄媚(第 5、6 讲)。
9.4 ChatGPT 与 chat 模板
2022 年 11 月 30 日,OpenAI 发布了 ChatGPT——一个用类似 InstructGPT 的方法训练的对话模型。据报道,两个月内它就成了当时用户增长最快的消费类应用,“提示工程”随之从研究者的工具变成了一个大众话题(原课主页上 2023 年《华盛顿邮报》那篇”科技业最热门的新工作:AI 耳语者”就是那个时期的产物)。
今天你通过 API 调用的模型几乎都是 chat 模型。它们接收的是一个消息列表:
system: You are a helpful assistant for a travel agency.
user: What is the capital of Varnia?
assistant: (模型从这里开始写)
但在模型内部,这个列表仍然被渲染成一段 token 序列——用一些特殊 token 标出每条消息的角色和边界,大致像这样(具体的特殊 token 因模型而异,这里只是示意):
<|system|>You are a helpful assistant for a travel agency.<|end|>
<|user|>What is the capital of Varnia?<|end|>
<|assistant|>
模型做的事情没有变:它仍然在续写一份文档——只不过这份文档是一段”对话记录”,而指令微调和 RLHF 让它非常擅长写”助手”那一方的台词。理解这一点,很多 chat 模型上的提示技巧就不再神秘:示例可以写成之前几轮的对话;system 提示是这份文档开头的”设定”;在支持的接口里,预先写好助手回答的开头(prefill),能强力地约束它接下来怎么写——不过本课默认的 Claude 模型不支持 prefill,要控制格式得用系统提示或结构化输出(前置讲 P2 §3.4–§3.5 讲调用这类 API 的工程细节)。
9.5 2023–2026:更长、更多模态、会用工具、会”想”
之后的几年变化极快,这里只列和这门课直接相关的几条线(后面各讲展开):
- 更强的通用模型与开放权重模型:GPT-4(2023 年 3 月)之后,各家前沿模型轮番更新;Meta 的 LLaMA(2023 年 2 月)开启了开放权重模型的浪潮,让研究者可以在本地研究提示和微调。
- 更长的上下文:从 GPT-3 的 2048 个 token 到今天常见的几十万甚至上百万 token。可以把整本书、整个代码库放进提示——但”放进去”不等于”用得好”(第 7 讲)。
- 工具使用与函数调用:模型输出结构化的函数调用,由程序执行后把结果返回给模型(第 8 讲);在此之上构建的智能体(第 13–15 讲)。
- 推理模型:OpenAI 的 o1(2024 年 9 月)、DeepSeek-R1(2025 年 1 月)等模型被训练成在回答前先写一段很长的思维链,并能在其中回溯、检查。“一步一步想”从一个提示技巧(第 4 讲)变成了模型训练出来的行为。
9.6 提示工程的变与不变
| 变了的 | 没变的 |
|---|---|
| 不再需要把任务伪装成”文档开头”,直接写指令就行 | 模型仍然在计算 ;提示仍然是在给这个分布设定条件 |
| 少样本示例的必要性下降(但对格式、风格、边界情况仍然有用) | 格式和措辞仍然会显著影响结果,必须靠实验评估 |
| 上下文窗口大了几百倍 | 模型只看得见上下文里的东西;上下文太长时,信息仍会被忽略 |
| 推理模型会自己”想”,CoT 技巧的收益变小 | 知识仍然有格式和方向;模型仍会编造、会谄媚 |
| 多阶段的系统(检索、工具、智能体)成为主流 | “可靠”仍然比”平均准确率高”难得多 |
10. 历史给提示工程的八条直觉 · Eight intuitions
把这一讲浓缩成八条直觉,后面每一讲都会用到:
- **提示 = 给一个条件分布设定条件。**模型的回答是从 里取出来的(§2.1)。你改提示,就是在改这个分布。
- base 模型在续写文档。把提示写成”你想要的回答会自然出现在后面”的那种文档的开头;模型会从提示的内容里猜它在写哪种文档(§8.3)。chat 模型续写的是一份”对话记录”(§9.4)。
- **格式和触发词是任务的开关,不是装饰。**一个小标记(“q :”、“rating :“)能决定模型做不做这个任务;一个没见过的同义词能让它完全失效(§8.4)。
- **示例首先教的是格式和结构。**几个示例就能让模型认出一个陌生的格式;但和预训练习惯冲突的格式,几个示例扳不过来(§8.2)。
- **模型只看得见上下文里的东西,而”看”是一种软查找。**窗口外的内容不存在(§2.6);窗口内的内容靠注意力去”查”,结构清楚、标记一致的提示更容易被查对(§6)。
- **知识有格式,也有方向。**同一个知识可以用不同的格式取出来,但反着问可能取不出来(§8.5)。问法要和知识在训练数据里出现的方式对得上;对不上时,换一种问法或把问题分解。
- **模型给的是一个分布。**温度是多样性和可靠性的旋钮;同一个提示的一次输出只是一个样本(§8.7)。评估提示要看分布,不要看一次。
- **能力来自训练信号。**词向量里有什么,取决于窗口里有什么(§3.7);模型会不会抄写,取决于训练数据有没有奖励抄写(§6.6);会不会听指令,取决于有没有做过指令微调(§9)。所以 base、instruct、推理模型要用不同的方式提示,换模型就要重新评估。
11. 课程地图 · The course map
| 讲次 | 主题 | 建立在本讲的哪些直觉上 |
|---|---|---|
| 2 | 上下文学习:模板、示例的选择与顺序、校准 | 直觉 3、4;§6.6 的归纳头;§8.2、§8.4 |
| 3 | 指令微调 | §9.2;在 §7.6 的迷你 GPT 上做指令微调 |
| 4 | 思维链提示与 self-consistency | 直觉 7(多次采样);§8.6 的涌现;§9.5 的推理模型 |
| 5–6 | 对齐:幻觉与校准;偏见、毒性与谄媚 | §9.1、§9.3;§6.6 的”置信度” |
| 7–8 | 提示增强:RAG、工具使用 | §3、§4 的嵌入与检索;直觉 5(上下文窗口) |
| 9–10 | 任务分解、自动提示与自我验证 | 直觉 6(把反向问题分解成正向问题);§7.5 的 AutoPrompt |
| 11 | 代码生成 | 直觉 2、7 |
| 12 | 对抗性提示与护栏 | 直觉 2(模型续写的是”整个上下文”,不分哪部分是你写的) |
| 13–15 | 智能体与多智能体 | §9.5;直觉 5 |
| 16 | 超级对齐 | 直觉 8 |
| 17 | 评估提示系统与课程项目 | 直觉 3、7;§1.3 |
12. 小结 · Summary
- 语言模型是 。从 Shannon 的猜字母游戏到 GPT,定义没有变过;提示就是那个”前文”。
- n-gram 用数数估计这个概率,被困在”看得近就不连贯、看得远就没见过”的两难里;它的窗口外的提示写了等于没写。
- 词向量用分布式假设把词变成稠密向量,学到的”相似”是可替换性;静态词向量分不开分布孪生词、多义词和反义词——表示里有什么,取决于训练信号里有什么。
- 句向量把一句话压成一个向量。词袋丢掉了顺序和否定;上下文相关的表示(Transformer 的隐藏状态)把它们找了回来。
- 注意力是软的字典查找;Transformer 用它让每个位置直接看见所有位置,并且可以并行训练。√d 缩放、位置编码、因果遮罩各有用处;两层注意力能组成”照着前文抄”的归纳头。
- 预训练 + 微调(ELMo、GPT-1、BERT、T5)证明了语言模型的知识可以迁移;那个年代已经出现了任务前缀、完形填空提示和软提示。
- GPT-2 / GPT-3 证明了足够大的语言模型可以只靠提示完成任务:零样本靠”唤起任务的格式”,少样本靠示例。base 模型在续写文档,对格式极其敏感;它学到的知识可以跨格式提取,却不能反转。
- 指令微调 + RLHF 让模型学会了”指令”和”对话”这种文档,ChatGPT 把提示工程带给了所有人;推理模型把”一步一步想”训练进了模型。提示工程的形式在变,背后的直觉没有变。
13. 常见误区 · Common pitfalls
- “模型先理解问题,再思考答案。” 模型做的是一个 token 一个 token 地计算条件分布。它内部确实形成了有用的表示(§4.3、§6.6),但提示的形式会直接改变这个分布——同一个问题换个格式,答案可以从对变成错(§8.4)。
- “知道一个事实,就能用任何方式问出来。” 知识有格式也有方向。反转诅咒在 GPT-4 上也存在(§8.5)。
- “词向量相似 = 意思相同。” 分布相似首先是”可以出现在同样的位置”:反义词、同一类别的不同实体都会很相似(§3.6–§3.7)。用嵌入做检索时尤其要记住这一点。
- “注意力权重就是模型的解释。” 注意力只告诉你信息从哪里被取过来,不告诉你取来之后怎么用(那部分很多在 MLP 里);不同的头、不同的层看的东西也不同(§6.6)。
- “示例越多越好。” 收益递减,占用上下文,可能把前面的内容挤出去;和模型先验冲突的格式,示例也救不回来(§8.2)。
- “温度 0 就能得到确定的、最好的答案。” 贪心解码取的是每一步的众数,不一定是整体最好的回答;在真实的 API 上,温度 0 的输出也不一定逐字相同(服务端的批处理会带来细微的浮点差异)。要评估一个提示,看的应该是多次采样下的表现(§8.7、第 17 讲)。
- “在一个模型上调好的提示,换个模型也好用。” 提示是针对模型的。规模、是否经过指令微调、是不是推理模型,都会改变最好的提示(§8.6、§9)。
- “提示工程就是找到那句神奇的话。” 这门课的立场是:提示工程是构造可靠的系统——要分解任务、要做对照实验、要关心失败的方式,而不是收集”万能咒语”(§1.3)。
14. 练习 · Exercises
练习 1(困惑度)。 一个语言模型在一段 4 个 token 的测试文本上,给每个真实 token 的概率依次是 0.5、0.25、0.5、0.125。它在这段文本上的困惑度是多少?如果另一个模型给每个 token 的概率都是 0.3,谁更好?
参考答案:平均负对数概率 ,困惑度 。第二个模型的困惑度 ,略好一点。注意第一个模型有时很自信、有时很没把握,困惑度是”几何平均意义上的犹豫程度”。
练习 2(窗口)。 对提示 q : what is the capital of X ? a : 和 q : what is X known for ? a :,n-gram 至少要几阶,才能在预测答案时同时看见”国名”和”问的是什么”?这和 01 号脚本演示 1 的结果一致吗?
参考答案:第一个问题要看见
capital of X ? a :这 6 个词,第二个要看见X known for ? a :这 6 个词,所以都需要 7-gram(窗口 6)。和演示 1 一致:5-gram 只看见X ? a :,分不清问的是首都、语言还是领导人(export 的问题甚至连国名都看不见);7-gram 才答对。
练习 3(平滑,动手)。 在 01 号脚本里,把 Kneser–Ney 的折扣 discount 从 0.75 改成 0.1、0.5、0.95,分别看 3-gram 和 7-gram 的测试困惑度。哪一个对折扣更敏感?为什么?
参考答案:编写时的结果:3-gram 在 d = 0.1 / 0.5 / 0.75 / 0.95 时分别是 2.600 / 2.608 / 2.616 / 2.624,几乎不变;7-gram 是 2.425 / 2.096 / 2.042 / 2.041,d 太小时明显变差。原因:3-gram 的上下文大多出现过很多次,计数大,扣掉 0.1 还是 0.95 影响不大;7-gram 的上下文大多只出现过一两次,d 太小意味着几乎不给没见过的组合留概率,一旦测试里出现没见过的组合(9.6% 的位置),就会被罚得很重。
练习 4(分布孪生词)。 在 §3.6 的性别类比里,如果”分布孪生词”之间随机选一个,“完全答对”的期望值为什么是 54/90?(提示:10 个女性人物词按”王室 / 年轻 / 家庭”三个特征分成几组?)
参考答案:女性词按特征分成 {queen, lady}(王室 + 成年)、{princess}、{woman}、{girl}、{daughter}、{mother, sister, aunt, wife}(家庭 + 成年)。每个女性词作为答案出现在 9 道题里。答案在 2 人组里的 2 × 9 = 18 题,期望答对一半,9 题;答案在单人组里的 4 × 9 = 36 题,全对;答案在 4 人组里的 4 × 9 = 36 题,期望答对 1/4,9 题。合计 9 + 36 + 9 = 54。实际结果 54–55 题,说明词向量本身几乎完美,错误几乎全来自”数据里根本没有区分它们的信息”。
练习 5(手算注意力)。 两个位置的 key 是 、,value 是 、。query ,不缩放。注意力权重和输出是多少?如果把 query 乘以 3 呢?
参考答案:分数是 ,权重 ,输出 。query 乘以 3 后分数是 ,权重 ,输出 ——更接近”硬查找”。这就是 §6.2 的锐利度 β。
练习 6(归纳头)。 在 attention_np.py 的手工电路里,如果去掉第 1 层(layers=1),第 2 层的 key 是什么?为什么准确率会掉到接近 0?
参考答案:第 2 层的 key 取自”前一个词”子空间,而这个子空间是第 1 层写进去的。没有第 1 层,所有位置的 key 都是全 0 向量,所有分数相等,注意力平均分给所有能看的位置,输出是”前面所有词的平均”,argmax 几乎从来不是正确答案(演示 4:0.9%)。归纳头必须依赖前一层的输出——这就是”两个头串起来”(K-composition)。
练习 7(为迷你 GPT 写提示)。 你想知道”只在百科出现过”的国家 volel 的领导人是谁。下面四个提示,哪个最可能成功?为什么?
(a) <doc> who leads volel ? (b) <doc> quiz : who leads volel ? (c) <doc> faq : q : who leads volel ? a : (d) <doc> article : the leader of volel is
参考答案:(d) 最稳:这是知识在训练语料里出现的原样格式(§8.5 正着问 24/24)。(c) 也很可能成功:问答格式能跨格式取出百科里的知识(只在百科的国家 93.8%)。(a) 和 (b) 几乎一定失败:volel 的问题在训练语料里只出现在题目列表中,模型会接着出下一道题(§8.3,0%)。可以运行
generate(model, tok, 提示)自己验证。
练习 8(绕过反转诅咒)。 不改训练数据,怎样让迷你 GPT 回答”kiro 是哪个国家的领导人”?写出思路,并估计它需要调用模型几次。
参考答案:把一个反向问题转化成 24 个正向问题:对每个国家 X,计算 ,取概率最大的 X。需要调用 24 次。编写时的实验:这个办法对全部 24 个领导人都找对了(24/24),而直接反着问只对 3/24。这是”任务分解”(第 9 讲)和”用多次调用换可靠性”的一个最小例子;代价是调用次数随候选数线性增长。
练习 9(温度,动手)。 修改 05 号脚本的演示 5,把提示换成 <doc> faq : q : what is the capital of volel ? a :,在温度 0.5、1.0、2.0 下各采样 60 次,统计答对的比例。和评论任务相比,温度对”单个词的答案”影响更大还是更小?
参考答案:只需要看第一个生成的 token。编写时的结果:迷你 GPT 给正确答案 grorken 的概率是 0.83;温度 0.5 时 59/60 答对,1.0 时 50/60,2.0 时 14/60。注意温度 1.0 时的答对率(≈ 0.83)正好等于模型给正确答案的概率——温度 1 就是”按模型自己的分布采样”。温度 0.5 把分布变尖,几乎总选概率最大的词;温度 2.0 把分布拉平,错误答案被抽中的机会大增。评论要连续写十几个 token,每一步的小偏差会累积,所以同样的温度对长文本伤害更大——这是第 4 讲”错误累积”和 self-consistency 的伏笔。
练习 10(chat 模板)。 把下面这段三轮对话按 §9.4 的示意格式”渲染”成一段文本,并指出模型要从哪里开始写。为什么说”把之前几轮写成示例”是一种少样本提示?
system: Answer with a single word.
user: Capital of Varnia? assistant: Lorn
user: Capital of Dulmar?
参考答案:
<|system|>Answer with a single word.<|end|> <|user|>Capital of Varnia?<|end|> <|assistant|>Lorn<|end|> <|user|>Capital of Dulmar?<|end|> <|assistant|>模型从最后一个
<|assistant|>之后开始写。之前那一轮”问首都 → 只答一个词”就是一个示例:它告诉模型这份”对话记录”里助手的回答格式是什么,和 GPT-3 的少样本示例是同一个机制,只是包装成了对话。
15. 延伸阅读 · Further reading
综述与教材
- Liu, P. et al. (2023). Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing. ACM Computing Surveys.(§1.2 的四个范式)
- Jurafsky, D. & Martin, J. H. Speech and Language Processing(第 3 版草稿,网上免费):n-gram 语言模型、词向量、Transformer 与大语言模型的几章。
- 本仓库的 CS224N 讲义(词向量、RNN、注意力、Transformer、预训练的详细推导)。
语言模型与 n-gram
- Shannon, C. E. (1951). Prediction and Entropy of Printed English. Bell System Technical Journal.
- Chen, S. F. & Goodman, J. (1998). An Empirical Study of Smoothing Techniques for Language Modeling.
- Brants, T. et al. (2007). Large Language Models in Machine Translation. EMNLP.
词向量与句向量
- Mikolov, T. et al. (2013). Distributed Representations of Words and Phrases and their Compositionality. NeurIPS.
- Pennington, J., Socher, R. & Manning, C. (2014). GloVe. EMNLP.
- Levy, O. & Goldberg, Y. (2014). Neural Word Embedding as Implicit Matrix Factorization. NeurIPS.
- Levy, O., Goldberg, Y. & Dagan, I. (2015). Improving Distributional Similarity with Lessons Learned from Word Embeddings. TACL.
- Arora, S. et al. (2018). Linear Algebraic Structure of Word Senses, with Applications to Polysemy. TACL.
- Reimers, N. & Gurevych, I. (2019). Sentence-BERT. EMNLP.
神经语言模型与 Transformer
- Bengio, Y. et al. (2003). A Neural Probabilistic Language Model. JMLR.
- Bahdanau, D., Cho, K. & Bengio, Y. (2015). Neural Machine Translation by Jointly Learning to Align and Translate. ICLR.
- Vaswani, A. et al. (2017). Attention Is All You Need. NeurIPS.
- Elhage, N. et al. (2021). A Mathematical Framework for Transformer Circuits; Olsson, C. et al. (2022). In-context Learning and Induction Heads.(Anthropic,transformer-circuits.pub)
预训练与提示的诞生
- Peters, M. et al. (2018). Deep Contextualized Word Representations (ELMo). NAACL.
- Radford, A. et al. (2018). Improving Language Understanding by Generative Pre-Training (GPT-1).
- Devlin, J. et al. (2019). BERT. NAACL.
- Radford, A. et al. (2019). Language Models are Unsupervised Multitask Learners (GPT-2).
- Raffel, C. et al. (2020). Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer (T5). JMLR.
- Brown, T. et al. (2020). Language Models are Few-Shot Learners (GPT-3). NeurIPS.
- Zhao, Z. et al. (2021). Calibrate Before Use: Improving Few-Shot Performance of Language Models. ICML.(第 2 讲的核心阅读)
- Sclar, M. et al. (2024). Quantifying Language Models’ Sensitivity to Spurious Features in Prompt Design. ICLR.
- Berglund, L. et al. (2023). The Reversal Curse: LLMs trained on “A is B” fail to learn “B is A”.
- Allen-Zhu, Z. & Li, Y. (2023). Physics of Language Models: Part 3.1, Knowledge Storage and Extraction.
规模与对齐
- Kaplan, J. et al. (2020). Scaling Laws for Neural Language Models; Hoffmann, J. et al. (2022). Training Compute-Optimal Large Language Models (Chinchilla).
- Wei, J. et al. (2022). Emergent Abilities of Large Language Models. TMLR;Schaeffer, R. et al. (2023). Are Emergent Abilities of Large Language Models a Mirage? NeurIPS.
- Wei, J. et al. (2022). Finetuned Language Models Are Zero-Shot Learners (FLAN). ICLR.
- Ouyang, L. et al. (2022). Training Language Models to Follow Instructions with Human Feedback (InstructGPT). NeurIPS.
16. 下一讲预告 · Next lecture
第 2 讲进入原课第 2 周的第一个主题:上下文学习(In-Context Learning)。这一讲我们看到,示例能让模型认出一个陌生的格式、格式上的小改动能让准确率大起大落。第 2 讲会把这些现象系统化:
- 零样本、单样本、少样本,以及提示模板和标签词(verbalizer)的设计;
- 为什么示例的选择和顺序能让准确率从接近随机变到接近最好,模型的”多数标签偏差""近因偏差""常见词偏差”从哪里来,怎样用上下文校准(Zhao et al. 2021)修正它们;
- 示例到底教了模型什么:标签对不对没那么重要(Min et al. 2022),可大模型又能被翻转的标签带偏(Wei et al. 2023);
- 回到 §6.6 的归纳头:让一个迷你模型在合适的数据上真的长出它,看上下文学习能力怎样随之出现;
- 原课作业 1 的对应练习:在一个文本分类任务上,用对照实验比较指令、模板和示例。