Lecture 2 · 上下文学习 · In-Context Learning

Prompt Engineering · Lecture 02。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:CMU 17-630 Prompt Engineering(Prof. Travis Breaux),第 2 周的第一个主题 · In-Context Learning。原课 Schedule 对这一讲的说明(意译):模型怎样在推理时从类比和示例中学会想要的任务;提示模板;模型对提示、示例顺序和示例选择的敏感性;以及”非对称学习”(asymmetric learning)。 依据说明:原课的 slides 和阅读清单不公开。Schedule 点名的四个概念——推理时从示例中学习、提示模板、对顺序与选择的敏感性、非对称学习——在本讲里分别对应 §1–§3、§4、§7 和 §6。其中”非对称学习”原课没有进一步解释,讲义把它理解为”示例里的不同信息被模型不对称地使用”:格式和标签空间被大量使用,输入到标签的映射却常常被忽略;预训练形成的先验和示例给出的映射之间,谁压倒谁取决于模型(Min et al. 2022;Wei et al. 2023;Kossen et al. 2024)。这是讲义的解读,不代表课堂上的讲法。具体材料来自 Brown et al.(2020)、Zhao et al.(2021)、Lu et al.(2022)、Min et al.(2022)、Liu et al.(2022)、Wei et al.(2023)、Olsson et al.(2022)、Xie et al.(2022)、Chan et al.(2022)等论文;引用时按原文的说法转述结论,具体数字以原文为准。全部实验是本讲义自己设计的。 配套代码 Code:code/lecture02/(PyTorch CPU;1 个模块 + 7 个脚本;不需要 API key;第一次运行会训练一个对照用的迷你 GPT,约 50 秒,之后读缓存;每个脚本都在 1 分钟以内) 预计阅读时间:4–5 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 说清楚上下文学习(in-context learning,ICL)是什么、“学习”发生在哪里(不更新参数),以及零样本 / 单样本 / 少样本三种提示方式的区别。
  2. 把一个分类提示拆成模板(输入格式、标签字段)和标签词(verbalizer),并解释为什么换一个”意思相同”的词,准确率就可能大起大落。
  3. 用方差分解、按因素分组的方法,量化一个提示对模板、示例选择、示例顺序的敏感性;知道为什么”挑开发集第一名”需要留出集来验证。
  4. 解释多数标签偏差、近因偏差、常见词偏差从哪里来,会用上下文校准和批量校准修正它们,也知道它们什么时候会失效。
  5. 讲出”示例到底教了什么”的两个经典发现——随机标签影响不大(Min et al. 2022)、大模型会被翻转的标签带走(Wei et al. 2023)——以及两者为什么不矛盾。
  6. 了解挑示例(相似度检索)和排顺序(熵准则)的方法,并知道”一个方法有没有用,取决于模型怎样使用示例”。
  7. 用三种视角解释上下文学习的来源:归纳头、隐式贝叶斯推断、预训练数据的分布特性;并在一个玩具任务上亲眼看到归纳头的形成。
  8. 设计一个合格的提示对照实验(原课作业 1:文本分类上的指令、模板与示例)。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲会用到、但只给直觉 Previewed
第 1 讲:base 模型在续写文档;迷你 GPT 和它的虚构世界;归纳头的直觉(§6.6)指令微调后的模型为什么零样本就很强(第 3 讲)
前置讲 P1:token 与上下文窗口思维链示例(第 4 讲)、检索示例的工程细节(第 7 讲)
前置讲 P3:Wilson 区间、McNemar 配对检验、赢家诅咒、“把示例当作随机因素”自动搜索提示(第 10 讲)
条件概率、对数、熵的定义注意力的数学(第 1 讲 §6 已讲)

0.3 本讲地图 Map

§1  什么是上下文学习:GPT-3 的三种提示方式;"学习"发生在哪里;本讲的三个问题
§2  零样本与少样本:示例首先教的是格式                                        ← 代码 01(演示 1)
§3  提示模板与标签词:表面形式、概率质量、示例的成本                            ← 代码 01(演示 2–3)
§4  敏感性:模板、示例、顺序各占多少;顺序效应从哪里来;挑选与赢家诅咒           ← 代码 02
§5  偏差与校准:多数、近因、常见词;上下文校准与批量校准                         ← 代码 03
§6  示例到底教了什么:随机标签、翻转标签、语义无关标签;"非对称学习"             ← 代码 04
§7  挑示例与排顺序:相似度检索(KATE)与熵准则(Lu et al.)                     ← 代码 05
§8  上下文学习从哪里来:归纳头、隐式贝叶斯推断、数据的分布                       ← 代码 06
§9  在真实的大模型上                                                          ← 代码 07(可选)
§10 原课作业 1 对照:怎样做一个文本分类的提示实验
§11 小结   §12 常见误区   §13 练习   §14 延伸阅读   §15 下一讲预告

0.4 记号与术语 Notation & terms

术语本讲义里的意思
示例(demonstration / in-context example / shot)放在提示里的”输入 + 标签”对。kk 个示例的提示叫 kk-shot
模板(template / pattern)把一个输入写进提示的格式,例如 review : {x} . sentiment : {y} .
标签词(verbalizer)每个类别用哪个词”说出来”,例如好评 → positive。这个术语来自 Schick & Schütze(2021)的 PET
标签空间(label space)所有可能的标签词的集合
无内容输入(content-free input)不带任何任务信息的输入,例如 N/A,用来测量模型”不看输入时”的偏向
校准(calibration)本讲专指 Zhao et al.(2021)意义上的”修正标签偏向”。第 5 讲的”校准”是另一个意思:置信度和正确率是否一致
P(y∣prompt)P(y \mid \text{prompt})模型在提示后面写出标签词 yy 的概率;本讲的分类都是”比较两个标签词的概率”

0.5 本讲的实验装置 · The setup

本讲的任务是第 1 讲虚构世界里的评论情感二分类:一条评论(如 the soup was not cold)是好评还是差评。评论有三种难度:simple(同极性的一两个分句,看形容词就够)、but(“A but B”,以 B 为准)、not(带否定)。测试集是第 1 讲 §8 用过的同一批 300 条(138 条好评);示例从另外 400 条里抽,为了塞进 64 个 token 的上下文窗口,一般只用一个分句的短评论(§3.3)。

我们有三个”模型”:

  1. base:第 1 讲训练的迷你 GPT。它的预训练语料里,评论页的格式是 review : … . rating : positive .,同一页上的几条评论互相独立。
  2. bursty:本讲新训练的对照模型。语料、结构、超参数、训练步数都和 base 一样,只改了一处:每个评论页属于一家”好店”或”差店”,好店的评论 85% 是好评,差店 85% 是差评。于是在这份语料里,前面几条评论的评分是猜下一条评分的有用线索。(“bursty”——同类的东西成簇出现——这个词借自 Chan et al. 2022。)两个模型的对比回答一个问题:模型怎样使用示例,是不是由预训练数据决定的?
  3. SimICL:一个用公式代替神经网络的”大模型”玩具模型(§5.5、§6.3)。迷你 GPT 只有两层,有些只在大模型上出现的现象它表现不出来;SimICL 把这些现象的一种可能机制写成旋钮。它能把论证变具体,但不能证明任何关于真实模型的事。

分类的做法和第 1 讲 §8.1 一样:不让模型自由生成,而是比较两个标签词作为”下一个词”的概率(icl_lab.label_probs)。这是 Zhao et al.、Min et al. 等论文的标准做法;真实的 API 大多不返回概率,07 号脚本改为看模型写了哪个词。


1. 什么是上下文学习 · What is in-context learning

1.1 一个定义

GPT-3 论文(Brown et al. 2020)把”不更新任何参数、只靠提示里的文字让模型完成任务”分成三种:

  • 零样本(zero-shot):只有任务描述和输入;
  • 单样本(one-shot):再加一个示例;
  • 少样本(few-shot):加若干个示例,论文里通常是 10 到 100 个,以塞满 2048 个 token 的窗口为限。

写成公式,一个 kk-shot 的分类提示是

y^=arg⁡max⁡y∈YPθ(v(y)  ∣  x1,v(y1),…,xk,v(yk)⏟示例,  x)\hat y = \arg\max_{y \in \mathcal{Y}} P_\theta\big(v(y) \;\big|\; \underbrace{x_1, v(y_1), \ldots, x_k, v(y_k)}_{\text{示例}},\; x\big)

其中 θ\theta 是冻结的模型参数,v(⋅)v(\cdot) 是标签词,Y\mathcal{Y} 是类别集合。和第 1 讲 §1.1 的微调对比:微调用 {(xi,yi)}\{(x_i, y_i)\} 去改 θ\theta;上下文学习把同样的数据写进条件里,θ\theta 一点不动。

GPT-3 论文把这种能力叫作上下文学习,核心发现是:模型越大,少样本相对零样本的提升越大。可它没有回答一个更基本的问题——参数没有变,“学习”到底发生在哪里?

1.2 “学习”发生在哪里

直觉上可以这样想。第 1 讲说过,base 模型在续写文档:它读提示时,在隐式地猜”我在写的是哪一种文档”,然后按那种文档的规律写下去。几个示例让这个猜测更准:

  • 它们告诉模型这是什么格式的文档(一个评论页?一个问答页?);
  • 它们告诉模型答案写在哪里、可以写哪些词;
  • 在能力足够强的模型里,它们还告诉模型输入和答案之间的对应关系。

所以”学习”发生在前向计算里:模型在读示例时,把”这份文档的规律”表示在了内部的激活中,再用它来预测最后那个标签。这一讲的大部分篇幅都在问:上面三层信息,模型究竟用了哪几层?

1.3 本讲的三个问题

  1. 示例在做什么?(§2、§3、§6)格式?标签空间?映射?
  2. 为什么这么不稳定?(§4、§5、§7)换示例、换顺序、换个词,准确率就变;能修吗?
  3. 这种能力从哪里来?(§8)为什么有的模型会、有的不会?

每个问题都会先在我们自己训练的迷你模型上做实验,再对照论文里真实模型的发现。迷你模型和 GPT-3 差了六个数量级,它们的行为不能直接外推;但它们的好处是:我们知道它们的全部训练数据,于是可以问”为什么”。


2. 零样本与少样本:示例首先教的是格式 · Zero-shot vs few-shot

配套代码:01_templates_shots.py 演示 1

2.1 实验

同一个情感分类任务,用五种模板去问 base 模型,给 0 / 1 / 2 / 4 个示例(两类各半、顺序随机;有示例时换 10 组示例,报平均 [最低, 最高];标签词都是 positive / negative):

模板词表外的词0 个示例1 个2 个4 个
review : {x} . rating : {y} .(和训练语料一样)–100.0%100.0%100.0%100.0%
review : {x} . sentiment : {y} .sentiment55.3%58.3%85.5% [82.7, 86.3]89.7% [89.3, 90.0]
q : {x} ? a : {y} .(借问答页的格式)–92.3%90.6%87.6%91.5%
{x} => {y} .=>54.0%54.0%54.0%54.0%
{x} . {y} .(没有任何标记)–46.0%46.0%46.0%44.7%

(测试集 300 条,好评占 46%:全猜差评得 54%,全猜好评得 46%。)

五行讲了五件事:

  1. 熟悉的格式零样本就会。rating 模板就是训练语料里评论页的样子,模型不需要任何示例。
  2. 示例能把一个陌生的格式”教”会。sentiment 模板只把 rating 换成了一个词表外的词(进模型就是 <unk>),零样本跌到 55.3%——模型给 positive 的平均概率只有 0.04,几乎全猜差评。2 个示例 85.5%,4 个 89.7%。示例告诉了模型:<unk> : 后面写的是 positive / negative,和 rating : 一样。
  3. 示例不总是帮忙。qa 模板零样本 92.3%,加示例后在 87.6%–91.5% 之间,没有一次比零样本好。
  4. 有些格式怎么教也教不会。在训练语料里,评论后面从来不直接跟 => 或标签,加 4 个示例也是”全猜一类”。这和第 1 讲 §8.2 的发现一样:预训练形成的习惯,几个示例扳不过来。
  5. 同一个模板里,换哪几个示例几乎无所谓。10 组示例之间最大的差别是 bare 模板 4 个示例时的 6.3 个百分点,其次是 sentiment 模板 2 个示例时的 3.7,其余都不超过 1 个百分点。记住这一点——GPT-3 不是这样的(§4)。

2.2 示例教会了什么,没教会什么

把 sentiment 模板按难度拆开:

简单句(180 条)but 句(57 条)not 句(63 条)
0 个示例52.4%59.6%58.9%
1 个52.4%59.6%71.2%
2 个98.1%59.6%76.3%
4 个100.0%60.0%88.9%

示例最先救回的是简单句:一旦模型知道”这里要写一个评分”,看形容词就能判对。but 句却始终停在 60% 左右——同一个模型在熟悉的 rating 模板下 but 句是 100%(第 1 讲 §8.1),可见它会处理 but;只是在陌生的模板下,它没能把这种能力调出来。

这给”少样本”一个更准确的描述:示例主要在帮模型认出任务和格式(Pan et al. 2023 把这叫作任务识别,task recognition),而不是在教模型一项新技能(任务学习,task learning)。Pan et al. 发现,任务识别在小模型上就有,任务学习要到很大的模型才明显出现。Reynolds & McDonell(2021)更早就提出过类似的看法:少样本示例的主要作用是”定位”预训练里已经学会的任务;在他们的实验里,写得好的零样本提示可以追平甚至超过少样本提示。

2.3 这对提示工程意味着什么

  • 先找模型熟悉的格式。能用零样本解决的,不必加示例(加了还占窗口、花钱,§3.3)。
  • 示例的第一价值是规定格式:答案写在哪里、可选的词有哪些、长度多长。今天的 chat 模型经过指令微调(第 3 讲),零样本已经很强,但”给一两个示例来规定输出格式”仍然是最常用的技巧之一。
  • 示例不保证有用(qa 模板),要测。

3. 提示模板与标签词 · Templates and verbalizers

配套代码:01_templates_shots.py 演示 2–3

3.1 一个分类提示的三个零件

一个分类提示可以拆成三部分:

  1. 输入格式:输入怎么写(前面加不加 review :、结尾加不加句号);
  2. 标签字段:答案前面的那个”引导”(rating :、sentiment :、a :、Answer:);
  3. 标签词:每个类别用哪个词说出来。

有了指令微调的 chat 模型之后,还有第四部分——指令(“Classify the sentiment as positive or negative.”)。第 3 讲专门讲它。这一讲的迷你模型没见过指令,所以只看前三部分。

3.2 标签词:意思一样,效果可以完全不同

保持模板不变,只换标签词(base 模型;4 个示例时是 10 组的平均 [最低, 最高]):

模板标签词(好评 / 差评)0 个示例4 个示例
ratingpositive / negative100.0%100.0%
ratinggreat / awful70.0%70.9% [70.0, 72.0]
ratingawful / great(反着用)30.0%27.2% [25.3, 29.7]
ratingcopper / wheat(和情感无关)61.0%58.3% [56.0, 60.0]
sentimentpositive / negative55.3%89.7% [89.3, 90.0]
sentimentgreat / awful54.3%71.8% [66.7, 79.0]
sentimentawful / great45.7%24.1% [19.0, 30.7]

几个要点:

  • great / awful 和 positive / negative 意思一样,rating 模板下却从 100% 掉到 70%。训练语料里 rating : 后面只出现过 positive 和 negative;great 和 awful 在这个位置上的概率都只有百万分之一左右(04 号脚本演示 2 打印了它们的概率之和)。比较两个极小的数,结果很不稳定。
  • 标签词反着用(好评写 awful),示例也救不回来:4 个示例明明反复演示”好评 → awful”,准确率反而从 30.0% 降到 27.2%。模型在用标签词本来的意思回答,而不是示例给的映射(§6 详谈)。
  • 示例的选择在”模型没把握”时最要紧:sentiment · great/awful 的 10 组示例之间差了 12.3 个百分点(66.7%–79.0%),而 positive/negative 只差 0.7。

这些现象在真实模型上都有对应。Zhao et al.(2021)把”偏向在预训练里常见的标签词”叫作常见词偏差(common token bias)。Holtzman et al.(2021)指出了一个更一般的问题——表面形式竞争(surface form competition):同一个意思有很多种说法(“computer”、“PC”、“laptop”),它们分摊了概率;一个标签词的概率低,可能只是因为它”不常被这样说”,不是因为模型认为它错。他们提出用”这个词在这里本来有多常见”去归一化,思路和 §5 的校准一样。

实践建议:标签词要选模型在这个位置上本来就会说的词。对 chat 模型,最稳的做法是在指令里明确列出可选的词,并检查输出里”不属于任何一个标签”的比例(07 号脚本的”答了别的词”一列)。

3.3 示例的成本:上下文窗口

示例占地方。sentiment 模板下(base 模型):

示例数示例来源平均 token 数超出 64 个 token 的比例平均准确率
0–15.20%55.3%
2短示例37.20%85.5%
4短示例59.20%89.7%
4任意长度70.281.9%87.9%
6短示例81.2100%88.3%
8短示例103.2100%88.3%

迷你 GPT 的窗口只有 64 个 token。超出时,tiny_gpt 只保留最后 64 个,最前面的页眉和示例被截掉,准确率不再上升。这就是为什么本讲的实验用短示例、最多 4 个。

真实模型的窗口有几十万个 token,“塞不下”很少见了,但成本还在:每个示例都按输入 token 收费、都增加延迟(P2 §6)。GPT-3 时代的论文受 2048 个 token 的限制,示例通常只有几个到十几个(Lu et al. 2022 主要用 4 个)。窗口变长之后,Agarwal et al.(2024)在 Gemini 1.5 Pro 上把示例加到几百、几千个(many-shot ICL),发现很多任务还在变好,甚至能部分推翻预训练形成的偏向。所以”放几个示例”不只是窗口问题,也是一个收益与成本的权衡,要和”写一个更好的零样本指令""微调”一起比较。


4. 敏感性:模板、示例、顺序各占多少 · Sensitivity

配套代码:02_sensitivity.py

4.1 背景:GPT-3 上的观察

Zhao et al.(2021)系统地记录了 GPT-3 少样本提示的不稳定:同一个任务,换一组训练示例、换示例的顺序、换提示格式,准确率都可以从接近随机变到接近最好。Lu et al.(2022)专门研究了顺序:同样 4 个示例的 24 种排列,有的接近最好、有的接近随机;这个现象在不同规模的模型上都存在,而且一个模型上的”好顺序”换到另一个模型上未必还好。Sclar et al.(2024)只改分隔符、大小写、空格这类”无关紧要”的格式,LLaMA-2-13B 在某些任务上的准确率最多差了 76 个百分点(第 1 讲 §8.4)。

这些发现意味着:**“提示 A 比提示 B 好”这个结论,可能只是因为 A 碰巧用了一组好示例或一个好顺序。**我们来看在迷你模型上,这几种因素各占多少。

4.2 方差分解

4 种设计(模板 · 标签词)× 5 组示例(每组 2 条好评 + 2 条差评)× 每组 24 种顺序 = 480 个提示,每个提示在 300 条评论上测准确率。把 480 个准确率的总方差按全方差公式拆开:

Var⁡(a)=Var⁡(aˉ设计)⏟换设计+E[Var⁡(aˉ示例组∣设计)]⏟换示例+E[Var⁡(a∣设计,示例组)]⏟换顺序\operatorname{Var}(a) = \underbrace{\operatorname{Var}\big(\bar a_{\text{设计}}\big)}_{\text{换设计}} + \underbrace{\mathbb{E}\big[\operatorname{Var}(\bar a_{\text{示例组}} \mid \text{设计})\big]}_{\text{换示例}} + \underbrace{\mathbb{E}\big[\operatorname{Var}(a \mid \text{设计}, \text{示例组})\big]}_{\text{换顺序}}

(每一项都用”除以 nn“的总体方差,三项才严格加起来等于总方差。)

base 模型bursty 模型
换模板 / 标签词90.4%89.9%
同一模板内换示例0.1%0.2%
同一组示例换顺序9.5%9.9%
总方差(百分点²)95.2164.9

再看每种设计内部 120 个提示的波动:

设计base:平均(最低–最高),标准差bursty:平均(最低–最高),标准差
sentiment · positive/negative89.8%(89.3–90.0),0.1894.1%(89.0–99.0),4.41
sentiment · great/awful73.2%(66.0–80.3),5.9281.6%(72.3–90.3),6.07
rating · great/awful71.4%(69.3–74.3),1.1466.3%(65.3–70.3),0.87
qa · positive/negative91.7%(91.0–92.7),0.4397.3%(92.0–100.0),3.06

三个结论:

  1. 对这两个迷你模型,模板和标签词是最大的因素(约 90% 的方差);换哪组示例几乎无所谓(0.1%–0.2%)。
  2. “平均来说不敏感”不等于”这个提示不敏感”。base 模型在 sentiment · great/awful 上,同样 4 条示例只换顺序,准确率就在 66.0%–80.3% 之间——14.3 个百分点。敏感性是提示设计的属性,不只是模型的属性。
  3. 越会读上下文的模型,对顺序越敏感。bursty 模型(§0.5)在四种设计里的三种上波动比 base 大。§5、§6 会看到,它比 base 更多地使用示例里的标签——上下文学习能力和对示例的敏感性,是同一枚硬币的两面。这也解释了为什么 GPT-3 这种强的上下文学习者比我们的迷你模型更”敏感”。

4.3 顺序效应从哪里来

把 120 个提示按”最后一个示例是好评还是差评”分成两组:

模型 · 设计最后是好评最后是差评120 个的极差分组后的组内极差
base · sentiment · great/awful67.3%79.1%14.32.3
bursty · sentiment · positive/negative98.3%89.8%10.03.8
bursty · sentiment · great/awful87.4%75.8%18.07.7
bursty · qa · positive/negative99.8%94.8%8.05.2

base 模型那一行最干净:分组之后,组内只剩 2.3 个百分点的差别——这个设计的顺序效应几乎全部来自”最后一个示例写的是哪个标签词”。有趣的是方向:最后一个示例写了 great,base 模型给 great 的平均概率反而更低(0.54 vs 0.58)。它的训练语料里同一页的评分互相独立,它没有学会”照抄上一个标签”。

bursty 模型正好反过来:最后一个示例是好评,它就更倾向好评。它的语料里同一页的评分相关,它学会了”下一个评分像上一个”——这就是近因偏差(§5)。同样大小的偏差,往两个方向推,对准确率的影响也不对称:bursty 对差评本来很有把握,往好评那边推不动;往差评那边推,一部分把握不大的好评就被推错了。

带走的经验:顺序效应不是神秘的噪声。先按”最后一个示例的标签”之类的简单因素分组看看,常常能解释大部分波动。

4.4 挑开发集上最好的那个,它在新题上还那么好吗?

面对这么多候选,一个自然的做法是”在开发集上全部试一遍,挑最好的”。把 300 道题随机分成开发集和留出集各 150 道(base 模型):

在哪里挑第一名开发集留出集 [95% CI]全体平均:开发 / 留出
全部 480 个qa · positive/negative92.7%92.7% [87.3, 95.9]82.6% / 80.4%
sentiment · great/awful 的 120 个第 4 组 · 顺序 PNPN80.7%78.0% [70.7, 83.9]75.0% / 71.3%
sentiment · positive/negative 的 120 个第 4 组 · 顺序 NPPN90.0%90.0% [84.2, 93.8]89.5% / 90.0%
  • 在全部 480 个里挑,第一名在留出集上没有回落:设计之间的差别(约 20 个百分点)是真的,远大于 150 道题的抽样误差。开发集第一名和最后一名在留出集上的配对检验 p ≈ 5×10⁻⁹。
  • 在 great/awful 里挑,第一名回落了 2.7 个百分点,但仍比平均好——它挑中的是”最后一个示例是差评”那一类(PNPN),这个效应是真的。
  • 在 positive/negative 里挑,120 个提示的预测几乎一模一样,没有东西可挑,也就没有”诅咒”。

前置讲 P3 §5 的赢家诅咒在这里只露出了一点点,因为迷你模型的候选要么差别很大、要么几乎相同。真实的提示工程常常处在最危险的中间地带:很多个候选、真实水平差不多、各自错在不同的题上(P3 的模拟里,20 个一样好的版本、50 道开发题时,第一名平均虚高 7.8 个百分点)。规矩不变:

  1. 在开发集上挑,在没用来挑选的留出集上报告;
  2. 比较两个提示用同一组题做配对检验(McNemar);
  3. 把示例的选择和顺序当作随机因素:换几组、换几种顺序,报告平均和波动(P3 §6.3),而不是只报最好的那一个。

5. 偏差与校准 · Label biases and calibration

配套代码:03_bias_calibration.py

5.1 Zhao et al. 的三种偏差

Zhao et al.(2021)的论文标题就是建议:《Calibrate Before Use》(先校准,再使用)。他们把 GPT-3 少样本提示的不稳定归结为模型对某些标签的系统性偏向:

  • 多数标签偏差(majority label bias):示例里哪个标签多,就偏向哪个;
  • 近因偏差(recency bias):偏向最后几个示例的标签;
  • 常见词偏差(common token bias):偏向在预训练里常见的标签词。

这三种偏差有一个共同点:它们是整体的——不管输入是什么,都往同一个方向推。这正是可以修正的那种偏差。

5.2 偏差从哪里来:一个”合理推断”的视角

为什么模型会有多数和近因偏差?一个直觉的解释来自预训练数据。真实网页里,同一页上的东西是相关的:同一家店的评论多半一样好或一样差,同一份试卷的答案分布不均匀,最近的几条最能代表当前的话题。一个把”下一个词”预测得很好的模型,应该利用这种相关性:前面好评多,下一条更可能是好评;刚出现的那个标签,下一个更可能还是它。

Xie et al.(2022)把这个想法形式化成隐式贝叶斯推断:预训练文档由某个隐含的”概念”生成,模型在读提示时隐式地推断这个概念,再按它预测下一个词:

P(y∣prompt)=∫P(y∣概念,prompt) P(概念∣prompt) d(概念)P(y \mid \text{prompt}) = \int P(y \mid \text{概念}, \text{prompt})\, P(\text{概念} \mid \text{prompt})\, d(\text{概念})

少样本提示里的示例是推断”概念”的证据——包括”这个页面上的评分偏向哪边”。在我们的 bursty 语料里,“概念”就是”这是好店还是差店”。

所以多数和近因偏差不是 Transformer 天生的毛病,而是模型从数据里学到的合理推断,只是被用在了一个它不再合理的场合:少样本提示里,示例的标签分布是我们随手定的,和测试输入毫无关系。

5.3 直接测量偏差:无内容输入

怎样看到一个模型的偏差?Zhao et al. 的办法是:把测试输入换成一个无内容的输入(N/A),放进同一个提示,看模型给每个标签的概率。理想情况下应该是 50:50;偏离多少,就是偏差有多大。

迷你 GPT 的词表里没有 N/A,它进来就是 <unk>,我们直接用 <unk>。sentiment · positive/negative 模板下,P(positive | 无内容输入) 随示例标签的变化(10 组示例的平均):

示例的标签顺序好 : 差basebursty
(无示例)0 : 00.080.51
PP2 : 00.600.53
NN0 : 20.670.31
PPPP4 : 00.730.77
NNNN0 : 40.710.24
PPNN2 : 20.700.26
NNPP2 : 20.740.71
PPPN3 : 10.690.29
NNNP1 : 30.750.65
  • bursty 模型有很强的近因偏差:PPPN(多数是好评、最后一个是差评)给出 0.29,NNNP 给出 0.65;PPNN 0.26、NNPP 0.71。当多数和”最后一个”冲突时,最后一个赢。
  • base 模型几乎不看标签:PPPP 0.73、NNNN 0.71。它对无内容输入的判断主要取决于”有没有至少两个示例”(0、1 个示例时 0.05–0.08,2 个以上 0.60–0.75)。

同样的结构、同样的训练步数,只改了预训练数据里”同一页评分是否相关”,近因偏差就从无到有。这是 §5.2 那个解释最直接的证据。

零样本时的整体偏向也很明显(03 号脚本演示 1):sentiment · positive/negative 模板下,base 只把 1.3% 的评论判成好评(真实比例 46%);sentiment · great/awful 模板下,两个模型都几乎全判差评(0.3% 和 1.3%)。格式越陌生,模型越没把握,“默认答案”的影响就越大。

5.4 上下文校准与批量校准

如果偏差是”给某个标签整体加了一个分”,修正就很简单:测出这个分,减掉它。上下文校准(contextual calibration;Zhao et al. 2021)的公式是

p^(y∣x)  ∝  p(y∣prompt,x)p(y∣prompt,N/A)\hat{p}(y \mid x) \;\propto\; \frac{p(y \mid \text{prompt}, x)}{p(y \mid \text{prompt}, \text{N/A})}

也就是每个标签的概率除以它在无内容输入上的概率,再归一化。等价地,在对数空间里减掉 log⁡p(y∣N/A)\log p(y \mid \text{N/A})。Zhao et al. 把它写成仿射变换 q^=softmax⁡(Wp+b)\hat{\mathbf q} = \operatorname{softmax}(W \mathbf p + b),取 W=diag⁡(pN/A)−1W = \operatorname{diag}(\mathbf p_{\text{N/A}})^{-1}、b=0b = 0。它不需要任何标注数据,只多一次模型调用。

批量校准(batch calibration;Zhou et al. 2024)换了一个参考:不用无内容输入,而用整批测试输入的平均预测:

p^(y∣x)  ∝  p(y∣prompt,x)1n∑jp(y∣prompt,xj)\hat{p}(y \mid x) \;\propto\; \frac{p(y \mid \text{prompt}, x)}{\tfrac{1}{n}\sum_{j} p(y \mid \text{prompt}, x_j)}

它的假设是”平均来看,模型不该偏向任何一类”。

在两个迷你模型 × 4 种设计 × 0 / 2 / 4 个示例上(10 组示例的平均,节选):

模型 · 设计 · 示例数不校准上下文校准批量校准
base · rating · great/awful · 470.9% [70.0, 72.0]82.6% [61.0, 94.0]94.8% [94.0, 95.7]
base · rating · great/awful · 272.9% [70.0, 74.0]79.3% [46.0, 94.7]88.4% [81.0, 91.7]
base · sentiment · positive/negative · 055.3%63.3%66.0%
base · sentiment · positive/negative · 285.5% [82.7, 86.3]71.5% [68.0, 76.3]86.3%
base · sentiment · great/awful · 054.3%12.3%42.0%
bursty · sentiment · great/awful · 482.5% [74.7, 89.0]76.3% [66.3, 91.3]90.9% [90.0, 91.7]
bursty · rating · great/awful · 277.1% [71.7, 80.0]67.9% [64.0, 70.7]94.9% [91.7, 96.7]
bursty · sentiment · positive/negative · 089.0%89.0%81.7%

24 种情形的总账:上下文校准 9 种变好、3 种差不多、12 种变差;批量校准 15 种变好、5 种差不多、4 种变差。

  • 校准救得最多的情形,是”模型看懂了输入,只是整体偏向一边”:base 的 rating · great/awful,4 个示例时从 70.9% 到批量校准的 94.8%。
  • 上下文校准可以帮倒忙:base 的 sentiment · great/awful 零样本,从 54.3% 变成 12.3%。<unk> 上的概率很极端,除法把它放大成了反方向的偏差。
  • 上下文校准的结果本身不稳定:它的参考值来自一个无内容输入;10 组示例之间的波动常常比不校准还大([70.0, 72.0] → [61.0, 94.0])。批量校准用 300 条输入的平均做参考,稳定得多。
  • 批量校准也有前提:bursty 模型零样本时本来就几乎不偏(判成好评 51.7%),批量校准把平均预测强行拉到 50:50,反而从 89.0% 掉到 81.7%。测试集严重不平衡时(例如 90% 是差评),这一步会出更大的错。

5.5 “无内容”输入真的没有内容吗

03 号脚本演示 3 只换无内容输入(base,sentiment · positive/negative,零样本):

无内容输入它的 P(positive)校准后
<unk>0.07563.3%
the0.09263.0%
the food was0.01977.3%
the soup was <unk>0.02676.7%
(批量:测试输入的平均)0.03666.0%

同一个模型、同一个提示,只换”无内容”输入,校准后的准确率就在 63.0%–77.3% 之间。the food was 这种半句话,模型会把它补成一条评论来判断,根本不是无内容的。Zhao et al. 自己就用 N/A、空字符串、[MASK] 三个取平均来减轻这个问题;Fei et al.(2023)发现这些输入本身也带偏向,改用随机抽取的领域内词语构造参考输入(领域上下文校准);批量校准则干脆不用人造的输入。

5.6 SimICL:如果偏差真是加性的

为什么校准在 GPT-3 上很有效,在我们的迷你模型上却好坏参半?SimICL 把一种可能的答案写成了公式。它给第一个标签词的分数是

Δ=S(k)⋅e(x)  +  u(x)⋅B(示例)  +  b模板+b常见词\Delta = S(k)\cdot e(x) \;+\; u(x)\cdot B(\text{示例}) \;+\; b_{\text{模板}} + b_{\text{常见词}}
  • e(x)e(x):输入本身的证据(好评为正、差评为负;simple 句最清楚,but 句最难);
  • S(k)S(k):“看输入”的强度,随示例数增加——示例让模型认出任务、学到映射(§6.3);
  • B(示例)B(\text{示例}):多数标签 + 近因 + 一个由”这串示例的这个顺序”决定的随机项(代表 Lu et al. 观察到、但机制不明的顺序效应);
  • u(x)u(x):每条输入对 BB 的”易感度”,在 1 附近浮动;
  • b模板b_{\text{模板}}、b常见词b_{\text{常见词}}:与输入无关的常数偏向。

除了 u(x)u(x) 那一点点,所有偏差都是”加在分数上的常数”——正是校准能减掉的那种。SimICL-large 上(10 组随机示例,不保证两类平衡,和 Zhao et al. 一样):

示例数不校准:均值 ± 标准差(最低)上下文校准批量校准
072.0%95.5%98.0%
177.5% ± 9.0(62.5)89.4% ± 3.086.5% ± 4.8
494.1% ± 5.6(79.5)94.6% ± 0.9(93.0)95.5% ± 2.8
896.1% ± 2.1(90.5)95.0% ± 0.496.5% ± 1.2
1692.8% ± 4.7(84.0)94.5% ± 0.994.2% ± 2.8

这张表的形状和 Zhao et al. 报告的 GPT-3 结果相似:不校准时,零样本被常数偏向拖累,少样本的平均尚可但组间波动大、最差的一组很差;校准后均值上升、波动大幅缩小。Zhao et al. 报告上下文校准让 GPT-3 和 GPT-2 的平均准确率最多提高了 30 个百分点,并减小了不同提示之间的波动。(SimICL-medium 的 1 个示例甚至比 0 个还差:72.3% vs 87.0%——只有一个示例时,多数和近因两项偏差全部来自那一个标签。)

但请记住这张表是怎么来的:偏差是加性的,因为我们把它写成了加性的。SimICL 的作用是把”偏差是加性的,所以能被减掉”这个论证变具体;它说明了校准在什么条件下有效,而真实的迷你模型告诉我们,这个条件不总是成立。

5.7 实践建议

  1. 先测偏差:看零样本和少样本时各类的预测比例,和真实比例比较;用一两个无内容输入看看偏向。
  2. 偏差明显、而模型”看懂了输入”时,校准往往很有效。优先试批量校准(稳定、不需要人造输入),但要确认测试集不会严重不平衡。
  3. 校准是提示设计的一部分,要在开发集上验证——它可能帮倒忙。
  4. chat API 通常不给概率。这时没法直接做校准,但可以用同样的思路:平衡示例的标签、避免让同一个标签出现在最后(或者换几种顺序取多数),并检查输出的类别分布。

6. 示例到底教了什么 · What do demonstrations teach

配套代码:04_what_demos_teach.py

6.1 Min et al.(2022):标签对不对,没那么重要

Min et al. 在 12 个模型(包括 GPT-3)上做了一个”拆示例”的实验:把示例里的标签换成随机的,准确率大多只下降几个百分点。他们进一步发现,示例真正起作用的是三样东西:标签空间(可以写哪些词)、输入的分布(输入长什么样)、整体格式;“这个输入对应这个标签”的信息反而用得不多。

我们在两个迷你模型上做同样的拆解(4 个示例,10 组的平均):

模型 · 设计零样本标签正确标签随机全部翻转只有输入只有标签输入换成无关句子
base · sentiment · pos/neg55.3%89.7%89.7%89.6%88.1%90.7%90.6%
base · sentiment · great/awful54.3%71.8%74.2%75.9%61.9%59.2%81.0%
bursty · sentiment · pos/neg89.0%95.2%95.0%95.2%94.2%98.3%97.4%
bursty · sentiment · great/awful55.3%82.5%81.6%85.0%85.8%89.3%78.7%

(“只有输入”:示例只写 review : x .,没有标签字段;“只有标签”:review : . sentiment : positive .;“无关句子”:输入换成故事里的句子,如 the queen lives in the palace,标签照旧。)

  • 标签正确、随机、全部翻转,三者几乎一样。这是 Min et al. 发现的极端版本:两个迷你模型都完全没有使用”输入 → 标签”的对应关系。
  • 在 positive/negative 设计下,“半个示例”也差不多够用:只要提示看起来像”一页评论、这里要填评分”就行。
  • 在 great/awful 设计下,完整的示例更要紧:base 只有输入 61.9%、只有标签 59.2%,都明显低于完整示例的 71.8%。标签词陌生时,模型需要同时看到”评论 + 标签词”的完整格式。
  • “输入换成无关句子”并没有一致地变差(Min et al. 在大模型上发现它会明显变差),两个模型上有升有降。在这个模型最没把握的设计里,示例的细节能左右结果,但方向难以预测。

6.2 Wei et al.(2023):大模型会被翻转的标签带走

Min et al. 的结论容易被读成”模型根本不看标签”。Wei et al.(2023)用两个更极端的设定检验了它:

  • 翻转标签(flipped-label ICL):示例里好评都标 negative、差评都标 positive。模型要么听标签词本来的意思(语义先验),要么跟着示例反着答(输入—标签映射)。
  • 语义无关标签(semantically-unrelated label ICL,SUL):标签换成 foo / bar 这样和任务无关的词,模型只能从示例里学映射。

他们在 GPT-3、InstructGPT、Codex、PaLM、Flan-PaLM 等模型上发现:小模型几乎不受翻转标签影响(它们靠语义先验),足够大的模型会跟着翻转的标签走,准确率远低于随机;大模型在语义无关标签上也能学会映射,小模型不行。他们还发现,指令微调会让模型更依赖语义先验(更不容易被翻转的标签带走),同时也增强了它学习语义无关映射的能力。

我们的迷你模型站在”小模型”那一端(rating 模板;括号里是两个标签词的概率之和):

模型标签词(好评 / 差评)0 个示例1 个2 个4 个
basepositive / negative100.0%(1)100.0%100.0%100.0%
baseawful / great30.0%(1e−6)30.7%27.1%27.2%
basecopper / wheat61.0%(6e−7)74.0%76.4%58.3%
burstyawful / great3.7%(3e−6)4.3%18.8%29.7%
burstycopper / wheat71.7%(1e−6)67.9%67.5%65.8%
  • 标签词反着用时,base 完全不跟示例;bursty 往示例给的映射那边挪了一点(3.7% → 29.7%),但仍远低于 50%。两个模型基本都按 awful、great 本来的意思回答——语义先验压倒了示例。bursty 挪动的那一点,和它”更会读上下文”一致。
  • copper / wheat 的数字没有意义。这两个词在 rating : 后面的概率只有百万分之一,模型根本不打算写它们;比较两个几乎为零的概率,结果取决于这两个词的向量和模型内部”好评 / 差评方向”偶然对得上多少。great / awful 本来就和好评、差评一起出现,所以对得上(70% 或反过来的 30%);copper / wheat 对不上,于是 base 随示例数在 58%–76% 之间乱跳,没有任何”学会新映射”的趋势。实践上:看一眼标签词的概率之和(或生成式输出里”不是任何一个标签”的比例),它很小时,准确率数字不可信。

6.3 两个发现并不矛盾

SimICL 把两种信息源写成公式里的两项:apriora_{\text{prior}}(多相信标签词本来的意思)和 aicla_{\text{icl}}(多会从示例里学映射)。三种”规模”只改这两个旋钮(8 个示例,10 组的平均):

规模apriora_{\text{prior}}, aicla_{\text{icl}}0% 翻转25%50%75%100%
small1.6, 0.387.9%85.5%83.2%86.9%85.0%
medium1.3, 1.291.3%86.5%79.8%69.7%55.2%
large1.0, 3.096.6%88.6%72.9%39.1%16.6%

语义无关的标签词(foo / bar):small 加到 16 个示例也只有 60.1%;large 从 54.5% 升到 88.2%。

  • small 像我们的迷你 GPT:翻多少都几乎不变。large 像 Wei et al. 的大模型:100% 翻转时系统性地反着答。
  • 50% 翻转(≈ 随机标签)时,small 只比 0% 低 4.6 个百分点,large 低 23.8 个。**Min et al. 的”随机标签影响不大”,前提是模型主要靠先验;Wei et al. 描述的是映射那一项变大之后的情形。**两者是同一个公式里两项的相对大小。

这也是本讲对原课”非对称学习”的解读:示例里的信息不是被平等使用的。格式和标签空间几乎总被用上;输入—标签映射只在 aicla_{\text{icl}} 足够大时才被用上;而当示例和预训练先验冲突时,谁赢取决于模型。Kossen et al.(2024)在真实的大模型上得出相近的结论:上下文学习确实会利用示例里的标签关系,但它不像传统的学习算法那样平等地对待所有上下文信息,也很难完全克服预训练形成的预测偏好。

6.4 对提示工程意味着什么

  • 示例里的标签要对——对强的模型,错标签会被学进去;对弱的模型,它至少不会帮忙。
  • 但不要指望示例能”教会”模型一个和它的先验相反的规则(例如”在这个任务里,‘不错’算差评”)。这种情况下应该用指令把规则写清楚,或者换一个与规则一致的标签词(第 3 讲)。
  • 示例最可靠的作用是规定格式和标签空间。所以示例的质量要求,首先是格式一致、标签词一致、覆盖所有类别。

7. 挑示例与排顺序 · Selecting and ordering demonstrations

配套代码:05_selection_order.py

7.1 按相似度检索示例

既然示例的选择会影响结果,能不能为每道题挑最合适的示例?Liu et al.(2022)提出 KATE:用一个句向量模型,从训练集里找和测试输入最相似的 kk 个样本当示例。他们在 GPT-3 上报告,这在情感分类、表格到文本生成、开放域问答上都明显优于随机示例。后续工作把”相似”换成学出来的检索器(Rubin et al. 2022 的 EPR)、考虑示例之间的多样性和覆盖面,等等。

我们用最简单的词袋余弦相似度(共享的词越多越像),在两个迷你模型上比较三种挑法:随机、最相似、最不相似(示例池 400 条任意长度的评论;节选):

模型 · 设计 · k随机最相似最不相似随机 vs 最相似(McNemar)
base · sentiment · pos/neg · 163.0%61.7%67.7%p = 0.50
base · sentiment · great/awful · 272.0%68.3%78.3%p = 0.11
bursty · sentiment · pos/neg · 184.3%82.7%74.3%p = 0.57
bursty · sentiment · pos/neg · 292.7%96.3%100.0%p = 0.019
bursty · sentiment · great/awful · 282.3%82.3%88.7%p = 1.00
  • 8 种情形里,最相似和随机只有 1 种有显著差别(bursty、k = 2,最相似更好)。检索相似示例没有稳定地帮到这两个迷你模型。
  • 最相似的示例和题目同标签的比例是 71%–72%,比随机的约 50% 高——如果模型会”照抄相似示例的标签”,这本该有帮助。它没有帮助,和 §6 的结论一致:这两个模型不读”输入 → 标签”的对应关系。
  • 意外的是,最不相似的示例在 7/8 种情形下最好或并列最好。词袋意义下最不相似的,往往是比较长、有两个分句的评论;也许对这两个小模型,示例的长度和句式比”像不像”更要紧——这只是一个猜测,这里没有验证(练习 6)。

一种挑示例的方法好不好,取决于模型怎样使用示例。KATE 在 GPT-3 上有效,一个合理的解释是大模型会从相似的示例里”借”答案的模式;对不这样做的模型,相似性就不是一个有用的信号。

7.2 不用标注数据挑顺序:熵准则

Lu et al.(2022)的问题是:没有开发集时,怎样从 24 种排列里挑一个好的?他们的观察是:坏的顺序往往让模型在所有输入上都偏向同一个标签。于是:

  1. 让模型自己生成一批”探测”输入(不需要标注);
  2. 对每种顺序,看模型在探测集上的预测:
    • GlobalE:预测出的标签分布的熵 H(1m∑j1[y^j=c])H\big(\tfrac{1}{m}\sum_j \mathbb{1}[\hat y_j = c]\big)——越大说明预测越均衡;
    • LocalE:每条预测本身的概率分布的熵的平均——越大说明模型越”犹豫”、越不被顺序推着走;
  3. 选熵最大的那几种顺序。

他们在 GPT 系列模型、11 个文本分类任务上报告,这带来约 13% 的相对提升。

我们在 sentiment · great/awful 上试(每组 4 个示例的 24 种顺序 × 5 组;探测集是模型自己从 <doc> reviews : review : 开始写的 60 条评论,例如 the music was warm but the staff was bland):

模型最差顺序24 种的平均最好顺序GlobalE 前 4 名LocalE 前 4 名
base66.4%73.2%79.5%66.9%67.2%
bursty74.5%81.6%89.3%88.8%76.8%
SimICL-large(对照)82.3%94.2%97.6%95.6%93.4%

(迷你模型是 5 组的平均;SimICL 用示例池里 60 条不带标签的评论当探测集。)

  • 两个迷你模型上,GlobalE 挑中的都是”最后一个示例是好评”的顺序。§4.3 发现,这类顺序对 bursty 更好、对 base 更差——于是同一个准则、同样的选择,bursty 几乎追上了最好的顺序,base 却接近最差。
  • SimICL 的顺序效应是”加在分数上的偏差”,正是 GlobalE 能检测到的那种:它避开了最差的几种,比平均好一点,但离最好还有距离。LocalE 在三个模型上都不如 GlobalE。

带走的经验:无监督的挑选准则都有自己的假设(这里是”好的顺序 = 不偏的顺序”)。它符合你的模型时有效,不符合时可能帮倒忙。用一小份带标注的开发集验证一下,比相信任何一个准则都可靠。

7.3 一份实用的清单

  • 示例的格式和标签词比”选哪几个”更重要,先把它们定下来(§2、§3)。
  • 让示例覆盖所有类别,数量大致平衡;不要让同一个标签总在最后(§4.3、§5)。
  • 想用检索示例时,在开发集上和随机示例配对比较;检索器、相似度的定义都是要调的超参数。
  • 报告结果时,把”哪几个示例、什么顺序”当作随机因素:至少换几组,报平均和波动(P3 §6.3)。

8. 上下文学习从哪里来 · Where does ICL come from

配套代码:06_induction_heads.py;bursty 模型见 icl_lab.py

8.1 三种视角

上下文学习是怎样从”预测下一个词”的训练里长出来的?目前有三类互补的解释:

  1. 机制视角:归纳头(Olsson et al. 2022)。一个两层的注意力电路:第一层的”前一位头”让每个位置记住它前面是哪个词;第二层的”归纳头”在当前词是 A 时,去找”前面是 A”的位置,把那里的词抄过来——“A B … A → B”。Olsson et al. 发现,归纳头在训练中突然形成,形成的那一小段训练里,模型的上下文学习能力(用前文降低后文损失的能力)也突然变强;他们提出(并承认证据是初步的、间接的),大模型里很大一部分上下文学习可能是由归纳头完成的。
  2. 统计视角:隐式贝叶斯推断(Xie et al. 2022;§5.2)。预训练文档有隐含的”概念”,模型学会了从前文推断概念;少样本示例只是推断的证据。
  3. 数据视角:分布特性(Chan et al. 2022)。他们在受控实验里发现,训练数据要有某些特性,上下文学习能力才会出现:同类的东西成簇出现(burstiness)、有大量很少出现的类别、类别分布有长尾(Zipf)。在很规整、很均匀的数据上,模型更倾向于把知识存进参数(“权重内学习”),而不是学会从上下文里读。

此外还有一条理论线:在线性回归之类的简单任务上,Transformer 的上下文学习可以精确地对应到某种学习算法,例如一步梯度下降(von Oswald et al. 2023;Akyürek et al. 2023;Garg et al. 2022)。这些结果说明”在前向计算里实现一个学习算法”是可能的,但它们和真实大模型的关系仍在研究中。

本讲的实验正好对应前两种视角和第三种视角的一个小例子。

8.2 一个干净的玩具任务:每条序列一条新的马尔可夫链

设计一个只能靠上下文学习的任务:每条训练序列来自一条新的、随机的马尔可夫链——3 个符号,转移概率矩阵的每一行都从均匀分布(Dirichlet(1,1,1))里重新抽。模型不可能把转移规律背进参数里,因为每条序列的规律都不一样;它只能从这条序列的前文里估计。这正是上下文学习:参数固定,规律从上下文里读出来。Edelman et al.(2024)系统地研究过同一个任务。

对这个任务,有三条”理论线”:

  • 均匀猜:ln⁡3=1.0986\ln 3 = 1.0986;
  • 一元统计(只数前文里每个符号出现几次):P(j)=cj+1t+1+KP(j) = \frac{c_j + 1}{t + 1 + K},平均损失 1.0212;
  • 二元统计(数前文里”当前符号 ii 后面跟了 jj“的次数):P(j∣i)=cij+1ci⋅+KP(j \mid i) = \frac{c_{ij} + 1}{c_{i\cdot} + K},平均损失 0.9080。这是贝叶斯最优的预测——而要算它,模型必须”找到前文里和当前符号相同的位置,看它们后面是什么”,这正是归纳头的两步。

8.3 训练曲线:先数频率,再看前后

从零训练两个 TinyGPT(4 个头、d_model = 64),2 层(104,384 个参数)和 1 层(54,400 个参数),各 1500 步:

训练步数2 层模型1 层模型
11.0854(≈ 均匀)1.1083(≈ 均匀)
501.0202(≈ 一元)1.0260(≈ 一元)
2001.0024(≈ 一元)1.0193(≈ 一元)
3000.96671.0121
4000.9426(≈ 二元)1.0088
10000.91501.0067
15000.91211.0020
  • 2 层模型先在约第 50 步走到一元统计那条线,在那里停了一百多步,然后在第 250–400 步之间跌到二元统计的水平;结束时 0.9121,离贝叶斯最优只差 0.004。这就是 Edelman et al. 描述的阶段性,也是 Olsson et al. 在真实语言模型里看到的”相变”的一个缩影。
  • 1 层模型始终停在一元统计附近(1.0020)。
  • 注意那个一元阶段:模型的策略是”前文里哪个多,就猜哪个”。放到少样本提示里,这就是多数标签偏差。二元阶段的”照着前文抄”,放到提示里就是从示例里复制格式和标签。上下文学习的能力和它的偏差,来自同一套机制。

8.4 打开模型:前一位头与归纳头

对每个头算两个得分(64 条验证序列上的平均):前一位得分——位置 tt 放在 t−1t-1 上的注意力;归纳得分——位置 tt 放在”前一个符号和当前符号相同”的那些位置上的注意力之和。

头前一位得分归纳得分
2 层模型 · 第 1 层 · 头 30.6350.461
2 层模型 · 第 1 层 · 头 40.5990.457
2 层模型 · 第 2 层 · 头 1–40.050–0.0550.701–0.886
1 层模型 · 4 个头0.062–0.0700.396–0.402
(对照:注意力在前文上均匀分布)0.0520.395

这就是第 1 讲 §6.6 手工搭出来的那个电路——这一次,它是梯度下降自己找到的:第 1 层有两个前一位头,把”我前面是谁”抄到每个位置上;第 2 层的四个头都变成了归纳头,去找”前面是当前符号”的位置。1 层模型的四个头和均匀注意力没有区别:没有第 1 层先把”前一个符号”抄过去,单独一层做不到。

8.5 上下文学习曲线

每个位置上的平均损失(看到的符号越多,越能估计这条链):

已看到的符号数2 层模型1 层模型一元统计二元统计
11.1011.1011.1671.099
90.9901.0261.0720.989
170.8960.9690.9980.892
640.8450.9750.9770.831

2 层模型的损失随位置一路下降,紧贴二元统计;1 层模型贴着一元统计。这是”示例越多越好”最简单的版本:上下文越长,模型对”这份文档的规律”估计得越准。

8.6 回到语言:数据决定了模型学会什么

两个迷你 GPT 有没有归纳头?06 号脚本演示 4 把 12 个随机词连写两遍,看模型在第二遍里能不能预测下一个词:base 和 bursty 都是 0.0%。它们的训练语料是模板化的句子,没有”同一段任意内容重复出现”的结构,归纳头没有理由长出来——这和 §6 的发现一致:它们的少样本能力靠的是认格式,不是从示例里复制。

把本讲的实验放在一起:

  • 只改”同一页评分是否相关”,模型就学会了读前面的标签(bursty:零样本 sentiment 模板 89.0% vs base 55.3%)——也学会了近因偏差(§5.3);
  • 在”每条序列一套新规律”的数据上,两层模型长出了归纳头,学会了从上下文里估计规律——也先经过了一个”数频率”的阶段;
  • 在没有重复结构的数据上,同样的结构什么都不抄。

**上下文学习能力——以及它的偏差——由预训练数据的结构决定。**真实的网页文本里,重复和成簇无处不在:名字反复出现、代码里的变量、表格的列、同一个话题的帖子。这也许正是大模型普遍”会”上下文学习的原因。


9. 在真实的大模型上 · On real models

配套代码:07_real_llm.py(可选;--toy 不联网)

07 号脚本在真实的 Claude 上跑一个小实验:3 个英文模板 × 5 种条件(零样本;同一组 4 个示例 × 标签正确 / 全部翻转 × 最后一个是好评 / 差评)× 16 条评论 = 240 次短调用(默认 claude-opus-5-5、effort = low,预算上限 1 美元;--model claude-haiku-5-5 更便宜)。提示长这样:

Classify the sentiment of each review as positive or negative.

Review: The room was great.
Sentiment: negative

Review: The bread was noisy.
Sentiment: positive
...

Review: The bread was lovely and the waiter was great.
Sentiment:

(这是”翻转标签”条件:指令说分成 positive / negative,示例却全部标反了。)

几点说明:

  • 真实的 chat API 不返回标签词的概率(P2 §3.6),所以只能看模型写了哪个词;这也意味着没法直接做 §5 的校准。
  • 默认模型不接受 temperature,同一个请求两次的结果可能不同;缓存(cache/)会把第一次的结果固定下来,第二次运行不再花钱。
  • 16 条评论的区间很宽(一条就是 6.25 个百分点)。要下结论,加大题数,并用 stats_kit.mcnemar_exact 做配对检验。
  • 这个实验里指令和示例互相矛盾。一个经过指令微调的模型面对这种矛盾时听谁的,正是 Wei et al.(2023)研究的问题,也是第 3 讲的话题。
  • 编写这份讲义的环境里没有安装 anthropic SDK,所以没有真实模型的结果可以报告;--toy 模式(SimICL-large)的输出只用来检查代码能跑通:标签正确时 6 种(模板 × 顺序)的平均 91.7%,翻转后 19.8%——这些数字由旋钮决定,不代表任何真实模型。

今天的 chat 模型和 GPT-3 有几个重要的不同,读论文时要注意:

  1. 零样本强得多。指令微调(第 3 讲)让模型直接听懂任务描述,少样本相对零样本的提升通常变小;示例更多是在规定格式和风格。
  2. 对示例格式的敏感性降低了,但没有消失。Sclar et al.(2024)的格式敏感性实验里,指令微调过的模型也有明显的波动。
  3. 窗口大到可以放几百个示例(many-shot ICL,Agarwal et al. 2024),这改变了”少样本 vs 微调”的权衡。
  4. 思考型模型会先在内部推理,示例对它的影响方式可能又不一样(第 4 讲)。

所以本讲的实验方法——拆开因素、配对比较、把示例当随机因素、检查偏差——比任何一个具体结论都更持久。


10. 原课作业 1 对照:怎样做一个文本分类的提示实验 · Assignment 1

原课作业 1 要求在一个文本分类任务上展示指令写法、模板使用和示例选择的效果。一份合格的实验报告大致是这样:

  1. 数据:分成开发集和测试集。提示的一切选择只在开发集上做,测试集只在最后用一次(P3 §5)。
  2. 因素:明确你在比较什么——指令的写法(第 3 讲)、模板(§3.1)、标签词(§3.2)、示例数(§2)、示例的挑法(§7.1)、顺序(§4.3)、是否校准(§5)。一次只改一个因素。
  3. 把示例当随机因素:每个设置至少换 5 组示例(或 5 种顺序),报告平均和标准差;比较两个设置时,每组示例上做配对比较(P3 §6.3)。
  4. 统计:单个准确率报 Wilson 区间;两个设置在同一组题上比较用 McNemar;比较了很多个设置时用 Holm 校正(P3 §3–§5)。
  5. 诊断:报告各类的预测比例(偏差)、“答了别的词”的比例(标签词是否可用)、按难度或类别拆开的准确率(哪里错了)。
  6. 成本:每个设置的平均输入 / 输出 token 和费用(P2 §6)。示例更多的提示,要更好多少才值得?
  7. 结论的范围:写清楚模型名和版本、日期、温度(如果能设)。提示的效果是”提示 × 模型”的属性,换模型要重测。

本讲的 02–05 号脚本就是这套流程的缩影,可以直接改成你的作业框架:把 icl_lab.label_probs 换成 P2 的 llm_api 调用,把评论换成你的数据集。


11. 小结 · Summary

  1. 上下文学习:参数冻结,把示例写进条件里。“学习”发生在前向计算中——模型在读示例时推断”这份文档的规律”。
  2. 示例首先教格式:在迷你模型上,示例把一个陌生的模板从 55.3% 救到 89.7%,却没教会 but 句;文献里叫”任务识别”而不是”任务学习”。
  3. 模板和标签词是最大的因素:意思相同的标签词可以差 30 个百分点;标签词要选模型在这个位置上”本来就会说”的。
  4. 敏感性是”提示 × 模型”的属性:平均不敏感的模型,在某个设计上换顺序也能差 14 个百分点;越会读上下文的模型,越敏感。顺序效应常常能用”最后一个示例的标签”解释。
  5. 偏差来自合理推断:多数和近因偏差是模型从”同页内容相关”的数据里学到的;只改这一点,近因偏差就从无到有。
  6. 校准修正整体的偏向:上下文校准和批量校准在”模型看懂了、只是偏了”时很有效,否则可能帮倒忙;要在开发集上验证。
  7. 示例的信息被不对称地使用:小模型只用格式和标签空间(随机、翻转标签都无所谓),大模型还会用映射(会被翻转的标签带走)。Min et al. 和 Wei et al. 描述的是同一个公式里两项的相对大小。
  8. 挑选方法都有假设:相似度检索、熵准则在一个模型上有效,在另一个上可能无效甚至有害。
  9. 能力来自数据:在”每条序列一套新规律”的数据上,两层模型先学会数频率(多数偏差的来源),再长出归纳头;在没有重复结构的数据上,同样的结构什么都不抄。

12. 常见误区 · Common pitfalls

误区实际情况
“示例越多越好”示例占窗口、花钱;对熟悉的格式,零样本已经最好(qa 模板加示例反而变差);超出窗口时最前面的内容被截掉
“示例里的标签对不对无所谓”对靠先验的模型几乎无所谓;对会学映射的大模型,错标签会被学进去(Wei et al. 2023)
“标签词只要意思对就行”great/awful 和 positive/negative 意思一样,可以差 30 个百分点;看一眼标签词的概率之和
“我试了一种顺序,结果是 X%”换顺序可能差十几个百分点;至少换几种顺序、几组示例,报告平均和波动
“在开发集上挑最好的提示,报它的成绩”挑选会抬高开发集成绩;要在没用来挑选的留出集上报告
“上下文校准总能提高准确率”它修正的是整体偏向;无内容输入本身有偏向,结果可能更差(54.3% → 12.3%)
“检索最相似的示例一定更好”取决于模型是否从相似示例里借答案;在不这样做的模型上,相似度不是有用的信号
“近因偏差是 Transformer 的缺陷”它是从”同页内容相关”的数据里学到的合理推断,被用在了不合理的场合
“迷你模型 / 模拟器上的结论适用于 GPT”迷你模型说明机制,SimICL 说明论证;真实模型的结论要在真实模型上测

13. 练习 · Exercises

练习 1(概念) 零样本、单样本、少样本提示的区别是什么?它们和”微调”在使用标注数据的方式上有什么根本不同?

参考答案:三者的区别只在提示里放几个示例(0、1、若干个);共同点是模型参数都不更新。微调用标注数据计算梯度、改变参数,效果”写进”了模型;上下文学习把标注数据写进提示里作为条件,效果只存在于这一次前向计算,换一个提示就没有了。代价也不同:微调要训练一次、之后每次调用便宜;上下文学习不用训练,但每次调用都要为示例的 token 付钱。

练习 2(读表) §2.1 里 sentiment 模板零样本只有 55.3%,模型给 positive 的平均概率是 0.04。(a)“全猜差评”能得多少分?(b)为什么说这个模型”其实看得懂评论”的可能性很大?可以用什么方法验证?

参考答案:(a)测试集 162/300 是差评,全猜差评得 54.0%;55.3% 只比它好一点。(b)同一个模型在 rating 模板下是 100%,说明它的内部表示里有情感信息;sentiment 模板只是让它整体偏向了 negative。验证:做校准——如果偏差是整体的,除掉偏向后准确率应该明显上升。03 号脚本里批量校准把它提到 66.0%、用 the food was 做参考的上下文校准提到 77.3%,说明确实有一部分”看懂了只是偏了”,但不是全部(2 个示例时可以到 85% 以上)。

练习 3(计算) 一个模型在某个提示下,对无内容输入给出 P(positive) = 0.8、P(negative) = 0.2。对一条测试评论,它给出 P(positive) = 0.6、P(negative) = 0.4。上下文校准后,这条评论被判成什么?

参考答案:0.6/0.8=0.750.6/0.8 = 0.75,0.4/0.2=2.00.4/0.2 = 2.0;归一化后 P(positive) = 0.75/2.75 ≈ 0.27,P(negative) ≈ 0.73。校准前判好评,校准后判差评:模型对这条评论的”好评倾向”(0.6)比它对任何输入的默认倾向(0.8)还低,所以相对来说它更像差评。

练习 4(设计) 你在一个三分类任务(正面 / 中性 / 负面)上用 4 个示例做少样本提示。请设计一个实验,区分”多数标签偏差”和”近因偏差”。

参考答案:用无内容输入(或一批测试输入的平均预测)测量预测分布,构造标签顺序不同、但多数和最后一个标签”冲突”的示例组,例如 [正, 正, 正, 负] 与 [负, 负, 负, 正](多数和最后一个相反),[正, 正, 负, 负] 与 [负, 负, 正, 正](多数相同、最后两个不同)。每种顺序用几组不同的示例内容重复(避免内容和顺序混在一起),比较预测分布。如果 [正正正负] 更偏负面,说明近因压过了多数(03 号脚本里 bursty 模型就是这样:PPPN 0.29 vs NNNP 0.65)。

练习 5(解释) Min et al.(2022)说随机标签几乎不影响准确率,Wei et al.(2023)说大模型会被翻转的标签带着走。用 §6.3 的公式解释为什么两者不矛盾,并说出一个能同时检验两个结论的实验。

参考答案:把模型的打分写成”语义先验 × 输入证据 + 示例映射 × 输入证据 + 偏差”。随机标签让映射项接近 0,准确率退回”只靠先验”的水平——对先验强的模型,这个水平本来就高,所以几乎没损失(Min et al.)。全部翻转让映射项变成负的;只有映射项的系数够大,它才能压过先验,让模型反着答(Wei et al. 的大模型)。实验:同一个模型、同样的示例,把翻转比例从 0% 调到 100%(04 号脚本演示 3 的做法),画出准确率曲线;先验强的模型曲线平坦,映射强的模型曲线下降、在 100% 时远低于随机。在多个规模的模型上重复,就同时检验了两个结论。

练习 6(动手) 05 号脚本发现”最不相似的示例”在迷你模型上反而常常最好,讲义猜测原因是这些示例更长、有两个分句。设计并实现一个实验检验这个猜测。

参考答案:把”相似度”和”长度 / 句式”两个因素拆开:(1)只从两个分句的评论里随机抽示例,和只从一个分句的评论里随机抽比较(控制相似度为随机、只改长度);(2)在同样长度的评论里比较最相似和最不相似(控制长度、只改相似度)。如果(1)里两分句的示例明显更好、(2)里差别消失,就支持”长度 / 句式”的解释。注意用同一组测试题做 McNemar 配对检验,并换几组示例。实现时可以复用 icl_lab.render 和 label_probs,按 len(x.split()) 或 " and " in x 过滤示例池。

练习 7(机制) 06 号脚本里,1 层模型停在”一元统计”的水平。解释为什么一层注意力能实现一元统计,却实现不了二元统计。

参考答案:一元统计只需要”前文里每个符号出现几次”:一个在前文上均匀分布注意力的头,把每个位置的符号嵌入平均起来,就得到了符号频率的向量,输出层据此预测——一层就够。二元统计需要”在当前符号 ii 之后出现过什么”:要找的位置是”前一个符号是 ii“的位置。可在一层模型里,每个位置的表示只有它自己的符号和位置,没有”我前面是谁”这个信息,注意力没法按”前一个符号”去匹配。两层模型的第 1 层先用前一位头把”前一个符号”写进每个位置,第 2 层才能按它匹配(演示 2 里正好看到这两种头)。

练习 8(批判) bursty 模型比 base 更会利用示例(sentiment 模板零样本 89.0% vs 55.3%),但也更受顺序影响、有更强的近因偏差。如果你要为一个产品选模型,你会怎样权衡?这对”选更大、更会上下文学习的模型”有什么启示?

参考答案:更会利用上下文意味着:给对的示例时效果更好,给偏的示例时也偏得更多。权衡时要看你能否控制示例:如果示例是固定的、可以在开发集上调好(平衡标签、验证顺序、校准),更强的上下文学习者通常更好;如果示例来自用户输入或检索结果、不可控,它的敏感性就是风险。对”选更大的模型”的启示:大模型零样本更强、上下文学习更强,但不能假设它”不敏感”——Lu et al. 发现顺序敏感性在各种规模上都存在。换模型之后,要重新做敏感性分析和偏差检查。

练习 9(作业对照) 按 §10 的清单,为原课作业 1 写一份实验计划(不超过一页):数据集划分、要比较的因素、每个因素的水平、重复次数、统计方法、要报告的诊断指标和成本。估算一下总的调用次数和费用。

参考答案(一种可能):数据 500 条,开发 200 / 测试 300。因素:指令(无 / 简短 / 详细)× 标签词(2 种)× 示例数(0 / 4 / 8)× 校准(有 / 无,只在能拿到概率时);每个有示例的设置换 5 组示例。开发集上的调用次数:3 × 2 ×(1 + 2 × 5)× 200 = 13,200 次;挑出 3 个候选后在测试集上跑 3 × 5 × 300 = 4,500 次。按每次约 400 个输入 token、5 个输出 token、某模型每百万 token 输入 1 美元 / 输出 5 美元估算,约 18,000 次 × (400 × 1 + 5 × 5)/10⁶ ≈ 7.7 美元(加上缓存后重跑不再花钱)。统计:Wilson 区间、McNemar 配对检验、Holm 校正;诊断:各类预测比例、非标签输出比例、按类别的准确率;成本:每个设置的平均 token 和费用。


14. 延伸阅读 · Further reading

少样本提示与敏感性

  • Brown, T. et al. (2020). Language Models are Few-Shot Learners (GPT-3). NeurIPS.
  • Zhao, Z., Wallace, E., Feng, S., Klein, D., & Singh, S. (2021). Calibrate Before Use: Improving Few-Shot Performance of Language Models. ICML.
  • Lu, Y., Bartolo, M., Moore, A., Riedel, S., & Stenetorp, P. (2022). Fantastically Ordered Prompts and Where to Find Them: Overcoming Few-Shot Prompt Order Sensitivity. ACL.
  • Sclar, M., Choi, Y., Tsvetkov, Y., & Suhr, A. (2024). Quantifying Language Models’ Sensitivity to Spurious Features in Prompt Design. ICLR.
  • Holtzman, A., West, P., Shwartz, V., Choi, Y., & Zettlemoyer, L. (2021). Surface Form Competition: Why the Highest Probability Answer Isn’t Always Right. EMNLP.

校准

  • Fei, Y., Hou, Y., Chen, Z., & Bosselut, A. (2023). Mitigating Label Biases for In-context Learning. ACL.
  • Zhou, H., Wan, X., Proleev, L., Mincu, D., Chen, J., Heller, K., & Roy, S. (2024). Batch Calibration: Rethinking Calibration for In-Context Learning and Prompt Engineering. ICLR.

示例教了什么

  • Min, S. et al. (2022). Rethinking the Role of Demonstrations: What Makes In-Context Learning Work? EMNLP.
  • Wei, J. et al. (2023). Larger Language Models Do In-Context Learning Differently. arXiv:2303.03846.
  • Pan, J., Gao, T., Chen, H., & Chen, D. (2023). What In-Context Learning “Learns” In-Context: Disentangling Task Recognition and Task Learning. Findings of ACL.
  • Kossen, J., Gal, Y., & Rainforth, T. (2024). In-Context Learning Learns Label Relationships but Is Not Conventional Learning. ICLR.
  • Reynolds, L., & McDonell, K. (2021). Prompt Programming for Large Language Models: Beyond the Few-Shot Paradigm. CHI Extended Abstracts.
  • Schick, T., & Schütze, H. (2021). Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference (PET). EACL.

挑选示例

  • Liu, J. et al. (2022). What Makes Good In-Context Examples for GPT-3? DeeLIO Workshop.
  • Rubin, O., Herzig, J., & Berant, J. (2022). Learning To Retrieve Prompts for In-Context Learning. NAACL.
  • Agarwal, R. et al. (2024). Many-Shot In-Context Learning. NeurIPS.

上下文学习从哪里来

  • Olsson, C. et al. (2022). In-context Learning and Induction Heads. Transformer Circuits Thread.
  • Xie, S. M., Raghunathan, A., Liang, P., & Ma, T. (2022). An Explanation of In-context Learning as Implicit Bayesian Inference. ICLR.
  • Chan, S. C. Y. et al. (2022). Data Distributional Properties Drive Emergent In-Context Learning in Transformers. NeurIPS.
  • Edelman, E., Tsilivis, N., Edelman, B. L., Malach, E., & Goel, S. (2024). The Evolution of Statistical Induction Heads: In-Context Learning Markov Chains. NeurIPS.
  • Garg, S., Tsipras, D., Liang, P., & Valiant, G. (2022). What Can Transformers Learn In-Context? A Case Study of Simple Function Classes. NeurIPS.
  • von Oswald, J. et al. (2023). Transformers Learn In-Context by Gradient Descent. ICML.
  • Akyürek, E., Schuurmans, D., Andreas, J., Ma, T., & Zhou, D. (2023). What Learning Algorithm is In-Context Learning? Investigations with Linear Models. ICLR.
  • Dong, Q. et al. (2024). A Survey on In-context Learning. EMNLP.(综述,适合查漏补缺)

15. 下一讲预告 · Next lecture

这一讲里,base 模型只会”认格式”:提示必须伪装成它熟悉的文档,标签词必须是它在那个位置上会说的词,几个示例扳不动它的习惯。第 3 讲进入原课第 2 周的第二个主题:指令微调(Instruction Tuning)——让模型直接听懂”把这条评论分成好评或差评”。

  • 为什么 base 模型不听指令;FLAN、T0、Super-NaturalInstructions 怎样把几十上千个任务改写成指令数据;任务的数量、模板的多样性、混入少样本和思维链数据各起什么作用;
  • InstructGPT 的监督微调与 RLHF(13 亿参数的模型在人类评价中胜过 1750 亿参数的 GPT-3);用模型自己生成指令数据(Self-Instruct、Alpaca);“少而精”(LIMA)与”表面对齐假说”;
  • chat 模板与 system prompt;指令微调怎样改变了本讲的结论(零样本变强、少样本收益变小、对语义先验更依赖);
  • 动手:在第 1 讲的迷你 GPT 上做一次真正的指令微调——扩展词表、只在回答上算损失——比较微调前后它对同一组提示的反应,并做”任务多样性 vs 数据量”的消融。
← 返回课程目录