Few-shot 示例
"光说不如给个例子"——这句老话在提示词里成立到令人吃惊的程度。但关于"给几个例子"这件事,中文互联网上流传的答案和提出这个词的那篇论文,差了整整一个数量级。"Few-shot 就是给三五个例子"这句话你一定听过。而 GPT-3 那篇《Language Models are Few-Shot Learners》里,few-shot 的示例数原文写的是 "typically 10 to 100"——10 到 100 个。三五个是当代工程实践的建议(Anthropic 官方给的正是 3 到 5 个),跟论文的做法不是一回事。这一节要做的第一件事,就是把这两个数字彻底分开;然后讲清 in-context learning 到底是怎么回事、示例该怎么挑、以及论文自己诚实列出的失效清单。
你要教一个从没进过厨房的人包饺子。
方式一 · 纯讲:"取一张皮,放适量馅,对折捏合,边缘要压紧,捏出褶子。"每个字他都懂,可"适量"是多少?"褶子"往哪个方向捏?捏出来的东西大概像个小笼包,也可能像个馄饨。
方式二 · 包一个给他看:你不说话,当着他的面包三个,摆在案板上。第一个是标准的,第二个馅少了一点、你顺手把它捏成月牙形补救,第三个皮破了、你演示怎么用另一张皮补。
他看完那三个,比听你讲十分钟管用得多。因为示例同时传递了三层信息:包成什么样(目标形态)、怎么包(操作过程)、遇到意外怎么处理(边缘情况)。这三层里,后两层几乎无法用语言精确描述。
Few-shot 提示就是"包几个给它看"。你不描述规则,你直接给出几对「输入 → 输出」,让模型自己从中提取规律。这一招之所以有效,是因为很多要求你自己也说不清楚,但你一眼能认出对不对。
三个词先分清:zero-shot、one-shot、few-shot
这三个词都出自 GPT-3 那篇论文,而且论文给了非常明确的定义。它们共同的前提最关键,也最常被忽略:三者都不做梯度更新、不做微调。
先把"梯度更新"这个词翻译一下。梯度更新(gradient update)说白了就是"改动模型内部的参数"——那是训练干的事,要显卡、要时间。而这三种设定全都在"模型一个参数都不改"的前提下进行,差别只在于你在提示词里放了几个示例。这一点决定了它们的性质:这不是学习,这是"看着办"。
| 设定 | 论文里的定义 | 你在提示词里放什么 | 要不要改模型参数 |
|---|---|---|---|
| zero-shot 零样本 | 不给任何示例,只给一段自然语言的任务说明 | "把下面这句英文翻译成中文:……" | 不改 |
| one-shot 单样本 | 只给一个示例 | 任务说明 + 一组「输入 → 输出」 | 不改 |
| few-shot 少样本 | 在上下文窗口容量允许的范围内,给尽可能多的示例 | 任务说明 + 若干组「输入 → 输出」,论文里通常 10 到 100 个 | 不改 |
注意 few-shot 那一行的定义。论文的原意不是"给少量几个",而是"在窗口装得下的前提下,能给多少给多少"。那个 "few"(少)是相对于传统机器学习来说的——传统方法要几千上万条标注数据才能学一个任务,而这里只要几十条放在提示词里就行,所以叫"少"。"少"是相对训练数据的"少",不是相对你的直觉的"少"。这是整个混淆的源头。
GPT-3 那篇论文的准确信息
既然本节要靠这篇论文立论,先把它的身份信息核准:
- 论文标题《Language Models are Few-Shot Learners》(语言模型是少样本学习者)
- 第一作者与团队Brown 等,共 31 位作者,末位作者是 Dario Amodei(后来创立 Anthropic 的那位)
- arXiv 编号与日期2005.14165,2020 年 5 月 28 日
- 发表情况NeurIPS 2020 收录。网上常说它拿了"最佳论文奖"——这一点没有取得证据,本节不采用这个说法。收录本身已经足够说明分量,不必替它加封号
- 模型规模GPT-3,1750 亿参数。换算成可感知的量:如果把每个参数写在一张 A4 纸上,纸摞起来大约有 17 公里高——比珠峰的两倍还高
- 论文用的核心术语in-context learning(上下文内学习)。论文用它指 meta-learning(元学习)的内循环
"in-context learning"这个词值得当场翻译。in-context 就是"在上下文里",也就是在你那段提示词里;learning 是学习。合起来就是"模型在读你这段提示词的过程中,临时摸清了这个任务该怎么做"——读完这次请求,它什么都不记得,下次得重新看一遍示例。
为什么论文管它叫"元学习的内循环"?元学习(meta-learning,"学习怎么学习")分两层:外循环是漫长的预训练,模型在里面见过无数种任务模式;内循环是每一次具体推理,模型在提示词里认出"哦,这是那类任务",然后调用已有的模式。打个比方:外循环是一个人上了十几年学、见过各种题型;内循环是他考试时看了两道例题,立刻明白这张卷子考什么。他不是在考场上学会了新知识,他是认出了该用哪套已有的本事。
★★ 最容易写错的一条:10 到 100,不是 3 到 5
这是本节最有价值的澄清点,值得单开一节反复说。
GPT-3 论文里,few-shot 的示例数原文写的是 "typically 10 to 100"——通常 10 到 100 个。这个数字受限于模型的上下文窗口能装多少。
而中文技术文章里几乎一致地写着"few-shot 就是给 3 到 5 个例子"。这句话并不是凭空编的——它来自当代工程实践,而且有权威出处:Anthropic 官方文档建议给 3 到 5 个示例。
两个数字都是对的,但它们回答的是两个不同的问题。混着用,就会在两个场合同时判断失误。
| GPT-3 论文(2020) | 当代工程实践(Anthropic 官方建议) | |
|---|---|---|
| 示例数 | 通常 10 到 100 个 | 3 到 5 个 |
| 要回答的问题 | 不做任何微调,纯靠上下文,模型的能力上限在哪儿? | 在真实产品里,性价比最高的示例数是多少? |
| 为什么是这个数 | 为了把 in-context learning 的能力推到极致,窗口能塞多少塞多少 | 够用即可。示例占 token、占钱、占窗口,而且当代模型的指令遵循能力远强于 2020 年 |
| 对示例的要求 | 从数据集里采样,追求数量与覆盖 | 相关、多样、有结构;贴近真实用例、覆盖边缘情况 |
| 使用场合 | 读论文、理解基准测试设定、比较模型能力 | 写产品提示词、控制成本 |
为什么工程实践能把数字降这么多?两个原因。第一,模型本身变了。2020 年的 GPT-3 是一个纯粹的"续写机器",你得用大量示例把它"扳"到任务上;今天的模型经过了指令微调和 RLHF(Reinforcement Learning from Human Feedback,"基于人类反馈的强化学习"),你直接说要求它就懂,示例只是用来消除歧义、对齐细节。第二,示例是要花钱的。每个示例都占 token,都要按输入价计费,还要挤占上下文窗口。100 个示例可能占掉几千个 token,每次请求都要重发一遍。
换成大白话:论文在实验室里测"最多能做到多好",所以不计成本地堆示例;产品里问"花最少的钱做到够好",所以三五个就收手。好比测汽车极速要在专业赛道上把油门踩到底,而日常通勤没人这么开。拿赛道数据去指导日常驾驶,或者拿通勤经验去质疑赛道成绩,两种做法都不对。
论文的实测数据:从 zero 到 few 到底涨多少
光讲定义太虚,看论文给的两组具体数字。这两组都可以放心引用:
| 数据集 | 指标 | zero-shot | one-shot | few-shot | zero → few 的差 |
|---|---|---|---|---|---|
| CoQA(对话式问答) | F1 | 81.5 | 84.0 | 85.0 | +3.5 |
| TriviaQA(常识问答) | 准确率 | 64.3% | 68.0% | 71.2% | +6.9 个百分点 |
看这两行有两个值得注意的地方。
第一,从 zero 到 one 的那一跳,往往比从 one 到 few 的那一跳更大。CoQA 是 81.5 → 84.0 → 85.0,第一步涨 2.5,第二步只涨 1.0。TriviaQA 是 64.3 → 68.0 → 71.2,第一步涨 3.7,第二步涨 3.2。这个规律的实用含义很直接:如果你现在一个示例都没给,那么加第一个示例的性价比最高。好比教人认路,第一次带他走一遍效果最大,第二次第三次的边际收益就小了。
第二,涨幅是几个百分点的量级,不是翻倍。Few-shot 是有效的手段,但它不是魔法。有些文章把 few-shot 说成"能让模型能力质变",那是夸大。真正会出现质变级提升的是 §10.3 要讲的思维链——那里有一组 17.7% 到 78.7% 的数字,那才叫质变。
论文最核心的那张图讲了什么
如果只能从 GPT-3 那篇论文里记住一个结论,应该是它 Figure 1.3 表达的那件事,而不是任何一个具体分数:
随着模型规模变大:zero-shot 的表现平稳提升,而 few-shot 的提升更快。
换句话说,模型越大,越擅长 in-context learning——大模型不只是"知道得更多",它更擅长"看几个例子就明白你要什么"。这个"从例子里领会意图"的能力,本身是随规模涌现出来的。
这个结论比任何一个分数都重要,因为它解释了一件你可能观察到的现象:同样一份带示例的提示词,在小模型上几乎没用,在大模型上效果明显。不是你的示例写得不好,是小模型"领会力"不够。这就像带学徒:老师傅示范一遍就能上手的活儿,新手看十遍还是不会——不是示范得不清楚,是接收端的能力不同。
你家装修,要贴一面很特别的墙砖:横竖交错、每三块换一次色、边角要留特定的缝。
做法一 · 写规范书:你熬夜写了两千字的施工说明,画了示意图,标了尺寸。师傅读完,皱着眉说"我大概明白了"——然后贴出来的东西跟你想的不一样。因为"错缝要错多少""换色的节奏从哪块开始"这些事,文字怎么写都有歧义。
做法二 · 贴一块样板:你自己在角落贴出 1 平米,让他照着来。他看两眼就懂了,连你没说出口的"哦原来横向要对齐"都一起懂了。
做法三 · 贴三块样板:一块标准区、一块阴角处怎么收边、一块遇到插座怎么绕。这三块把典型情况和麻烦情况都覆盖了,师傅贴一整面墙都不会来问你。
这就是 few-shot 的全部道理。做法一是纯指令,做法二是 one-shot,做法三是精心策展的 few-shot。注意做法三的关键不是"多",而是三块样板各自负责一类情况——这正是 Anthropic 说的"相关、多样、有结构"。而如果你贴的三块样板全是标准区,那第二块第三块就是白贴,这也解释了为什么示例的多样性比数量重要。
★ 示例怎么挑:官方给的三个词
Anthropic 官方文档对示例的要求浓缩成三个词:相关(relevant)、多样(diverse)、有结构(structured)。加上两条补充要求:贴近你真实的用例,以及覆盖边缘情况。
| 要求 | 说白了是什么意思 | 反例(常见错误) |
|---|---|---|
| 相关 | 示例和你真正要处理的输入长得像——同样的领域、同样的句式、同样的数据形态 | 你要处理的是客服聊天记录,示例却是正式的书面公文 |
| 多样 | 几个示例之间不重复,各自覆盖一类不同情况 | 给了五个例子,全是"正面评价 → 好评"这一种,模型学不到怎么处理负面和中性 |
| 有结构 | 每个示例的输入和输出边界清楚、格式统一,最好用标签包裹 | 示例和指令混在一堆文字里,模型分不清哪段是示例、哪段是要它做的事 |
| 贴近真实用例 | 直接从你的真实数据里挑,不要自己编"干净漂亮"的假例子 | 示例都是格式规整的短句,实际输入却是带错别字、带表情、带换行的真实用户留言 |
| 覆盖边缘情况 | 把那几种"最容易出错"的情形各放一个进去 | 只给了正常情况,模型遇到空输入、超长输入、多种意图混在一起时就乱了 |
关于"有结构",官方明确建议用 <example> 标签把每个示例包裹起来。这个做法看着琐碎,收益很实在——它让"示例区"和"指令区""待处理数据区"之间有了明确的墙。§10.4 会把这套标签体系讲透,这里先看它长什么样:
请把用户反馈归入四类之一:功能缺陷 / 体验问题 / 功能建议 / 无效反馈。
<examples>
<example>
输入:点进去就闪退,我都装了三遍了
输出:功能缺陷
</example>
<example>
输入:这个按钮藏太深了吧,找了半天
输出:体验问题
</example>
<example>
输入:希望能支持导出 Excel
输出:功能建议
</example>
<example>
输入:啊啊啊啊
输出:无效反馈
</example>
<example>
输入:闪退问题什么时候修?另外能不能加个夜间模式
输出:功能缺陷
备注:一条反馈含多个意图时,取更严重的那一类
</example>
</examples>
现在请处理这一条:
输入:更新完之后字变得特别小,看不清
数一下这里给了几个示例:五个,正好在 3 到 5 个这个建议区间的上限。而且这五个不是随便挑的——前四个各占一类(多样),第五个专门处理"一条反馈含多个意图"这种边缘情况,还顺手用"备注"字段把规则明确写了出来。这个"备注"是个好技巧:当示例本身可能引起疑问时,在示例内部把判断依据写清,比在指令区加一条抽象规则更有效。
★ 不要堆砌边缘情况:官方明确反对的做法
刚说完"要覆盖边缘情况",紧接着必须补一条反向的警告,否则很容易过头。
Anthropic 官方对此有一句非常直白的表态:团队常常会把一长串边缘情况(a laundry list of edge cases)塞进提示词里——"We do not recommend this."(我们不推荐这样做。)
官方给出的替代方案是:策展一组多样的、典型的(canonical)示例。canonical 这个词值得翻译一下——它指"最标准、最能代表一类情况的那一个"。也就是说,与其列举二十种可能出错的情形,不如挑出五种最有代表性的,各给一个示例。
为什么堆砌边缘情况反而不好?三个原因:
- 提示词会变得脆弱每加一条特例,就多一条可能和其他条冲突的规则。加到第二十条时,你自己都说不清它们有没有互相矛盾。这本质上是把复杂的 if-else 逻辑硬编码进了自然语言里——而自然语言没有编译器帮你检查冲突。
- 维护成本失控业务一变,你得回头逐条审二十条特例哪些还成立。而且改动任何一条都可能影响其他情形的表现,每次都要重新全量测试。
- 模型的泛化能力被浪费了给几个典型示例,模型能自己推广到没见过的情况;给一堆特例,它反而倾向于逐条对号入座,遇到清单外的情况就懵。好比教司机开车,教会他"看清路况、留出余量"比给他背一本"遇到这种路口该怎么办"的两百条手册更管用。
这条原则和 §10.4 要讲的"正确的高度"(right altitude)是同一件事的两面:提示词既不该是硬编码的规则堆,也不该是笼统到没有信号的空话。而官方特别强调的一句是——"minimal 不等于 short"(精简不等于短)。精简是指"没有冗余、没有互相打架的规则",不是指字数少。一份三千字但每一段都在提供不同信号的提示词,比一份五百字但反复强调同一件事的提示词更"精简"。
★ 论文诚实列出的失效清单
这一段能显著提升你对 few-shot 的判断力,因为它来自论文自己的坦白。
GPT-3 那篇论文明确列出了 few-shot 效果不佳的任务类别:
| 任务类别 | 代表数据集 | 这类任务在考什么 |
|---|---|---|
| 自然语言推理 | ANLI 等 | 判断两句话之间是"蕴含 / 矛盾 / 无关"。要的是逻辑关系判断,不是知识 |
| 阅读理解 | RACE、QuAC 等 | 读一段较长的文章后回答问题,常需要跨句子整合和多步推理 |
这份清单的价值在于它划出了 few-shot 的能力边界。Few-shot 擅长的是"教会模型输出格式和判断口味",不擅长的是"给模型它本来不具备的推理能力"。
换成大白话:你给几个例子,能让它明白"哦,你要的是这种风格、这种格式、这种粒度的判断";但如果这道题本身需要它做三步逻辑推演而它做不到,那给一百个例子也变不出来。好比你给学生看三道解得漂亮的证明题,他能学会答题的书写格式和排版,但如果他压根不掌握那个定理,例题再多也证不出新题。
顺带说,这也解释了为什么 CoT(思维链)在 few-shot 之后才被发现是个大突破——因为它攻的正是 few-shot 攻不下的那块阵地:让模型把推理过程一步步写出来。这是下一节的主题。
论文愿意把自己方法的失效场景写进去,这件事本身值得学。在技术判断上,一个方法的边界比它的战绩更有用——战绩告诉你它能干什么,边界告诉你什么时候别用它、该换别的招。
示例的顺序、数量、格式:几个实操细节
下面这些是工程实践里反复被验证的经验,不是论文结论,如实标注为经验做法:
- 格式一致性比内容更重要所有示例的输入输出格式必须完全统一——同样的标签、同样的分隔符、同样的字段顺序。格式不统一会直接抵消示例的作用,因为模型不知道该学哪种。
- 示例里的标签分布要留意如果你给的五个示例里四个都是"通过"、一个"不通过",模型可能会倾向于输出"通过"。分类任务里让各类别数量尽量均衡。
- 顺序有影响,但方向不好预测示例的排列顺序会影响结果,这在实践中被反复观察到。但哪种顺序更好没有普适规律,只能测。§10.5 会讲怎么测。
- 示例放在指令之后、待处理数据之前这是最常见也最稳的排法:先说要干什么,再给示范,最后给真正要处理的东西。
- 示例区和数据区一定要有明确的墙用
<examples>和<input>这类标签隔开。最常见的一类失败是模型把你的示例当成了要处理的数据,或者把待处理的数据当成了新示例继续往下编。 - 输出侧也要给示范示例的价值一半在输入、一半在输出。如果你的输出要带某种口吻、某种结构、某种精确到标点的格式,示例里就要如实体现出来。
什么时候该用 few-shot,什么时候不必
给一张判断表。因为示例是要花钱的,不该无条件加。
| 情形 | 建议 | 理由 |
|---|---|---|
| 输出格式复杂、用文字很难描述清楚 | 强烈建议用 | 示例是描述格式最高效的手段,一个例子胜过三段说明 |
| 判断标准带主观口味(什么算"重要"、什么算"合格") | 强烈建议用 | 口味没法定义,只能示范。这是 few-shot 最不可替代的场景 |
| 要做批量分类、抽取,且类别边界模糊 | 建议用 | 示例能把边界钉住,尤其是那几个容易混的类别之间 |
| 任务本身极其常见(翻译、摘要、改写) | 可以先不用 | 模型见过海量此类数据,zero-shot 通常够好。先测再决定要不要加 |
| 需要的是深度多步推理 | 不要指望 few-shot | 论文自己列了 ANLI、RACE、QuAC 这类的失效情况。该用 CoT(§10.3) |
| 上下文预算非常紧张 | 谨慎用,先算账 | 示例每次请求都要重发。如果示例固定不变,把它放提示词最前面吃前缀缓存折扣(见 §8.1) |
| 已经有几千条标注数据 | 考虑微调 | 数据量到了这个级别,微调的效果和长期成本通常都优于把示例塞进每次请求 |
最后一行值得多说一句。Few-shot 和微调是同一个坡上的两个位置。示例三五个,用 few-shot;标注数据几千条,用微调。中间那一大段灰色区域,判断依据是这份提示词要被调用多少次——调用一万次,那几千个示例 token 就被付了一万遍,微调的一次性成本反而便宜。好比租房和买房:住三个月租,住十年买,中间那几年得算账。
Few-shot 到底"学"到了什么:一个值得警惕的细节
这一段稍微深一点,但它能让你对示例的作用有个更准的判断。
直觉上我们会以为:模型从示例里"学到了任务的规则"。但更贴近实情的说法可能是——示例主要在做三件事:告清任务是哪一类、钉住输出的格式、划定标签的取值范围。至于"每一条输入到输出的映射关系",模型很大程度上是靠预训练里已有的能力去补的。
这件事的实用含义:如果一个任务是模型压根没见过、也无法从常识推导的(比如你们公司内部一套自创的编码规则),那么给三五个示例往往不够,模型会"照着格式套,但内容全靠猜"。这种时候更该做的是把规则明确写出来,而不是指望示例把规则传递过去。
好比教人用一台洗衣机:如果这机器的按钮布局符合常规,你演示一次他就全会了;但如果这台机器有个"必须先按住电源三秒才能选程序"的怪脾气,你演示十次他也未必注意到那三秒——你得直接说出来。示例传递的是"看得见的形态",说不出的怪规则得靠文字明说。
反过来也成立:凡是"你自己也说不清、但一眼能认出对不对"的要求,就必须靠示例。文案的调性、摘要的详略、代码注释的粒度、翻译里哪些词该保留英文——这些东西写成规则会变成一堆自相矛盾的形容词,给三个例子反而一目了然。说白了:能说清的用文字,说不清的用例子,两样都不省。
常见误解一次澄清
| 常听到的说法 | 实际情况 |
|---|---|
| "few-shot 就是 3 到 5 个,这是论文定义" | 混了两件事。GPT-3 论文写的是通常 10 到 100 个;3 到 5 个是当代工程实践建议(Anthropic 官方口径) |
| "few-shot 是一种训练/微调" | 不是。论文明确前提是不做梯度更新、不做微调——模型参数一个都不改,全部发生在提示词里 |
| "示例越多越准" | 边际收益递减,且要花钱占窗口。论文数据显示 zero→one 那一跳最大,one→few 明显变缓 |
| "few-shot 能解决推理题" | 论文自己列了失效清单:ANLI 等自然语言推理、RACE 与 QuAC 等阅读理解。推理题该用 CoT |
| "边缘情况列得越全越好" | 官方明确反对堆砌一长串边缘情况(原话 "We do not recommend this"),应改为策展一组典型示例 |
| "精简就是写短" | 官方特别强调 minimal 不等于 short。精简指没有冗余与冲突,不指字数少 |
| "GPT-3 那篇论文拿了 NeurIPS 最佳论文奖" | 这一点没有取得证据。可确认的是 NeurIPS 2020 收录。不要给论文加没核实的封号 |
| "示例格式随便,模型能看懂" | 格式不统一会直接削弱示例作用。所有示例必须同标签、同分隔符、同字段顺序 |
最后一个提醒:示例是「样品」,不是「说明书」
很多人给示例时会不自觉地写成教程,一条示例后面跟三行解释。这其实搞错了示例的作用。
打个裁缝店的比方:你想做一件衬衫,最有效的办法不是给裁缝写一篇《衬衫工艺说明》,而是带一件你满意的衬衫过去,说「照这个做,领子改成立领」。样品传递的信息量,远远超过同等篇幅的文字描述。
相当于在照相馆里,你说「拍得自然一点」摄影师未必懂,但你翻出一张手机里存的照片说「像这张」,他立刻就知道了。说白了——示例的价值在于它是一个可以直接比对的成品,不是一段需要理解的规则。
所以写示例时,把力气花在让示例本身足够典型上,而不是花在给示例加注释上。如果你发现必须写一大段解释才能让示例说得通,那通常说明这条示例选得不够好,该换一条,而不是该加注释。
Few-shot 就是"包几个饺子给它看"——不描述规则,直接给几对「输入 → 输出」,让它自己提取规律。三种设定的共同前提是一个模型参数都不改:zero-shot 不给示例只给说明,one-shot 给一个,few-shot 在窗口装得下的前提下尽量多给。论文管这个能力叫 in-context learning,并把它定位为元学习的内循环。
本节最该记住的澄清:GPT-3 论文里 few-shot 通常是 10 到 100 个示例;Anthropic 官方给工程实践的建议是 3 到 5 个。两个数字回答的是不同问题,混着用会两头判断失误。论文的实测数字是 CoQA 的 81.5 / 84.0 / 85.0 与 TriviaQA 的 64.3% / 68.0% / 71.2%——涨幅是几个百分点,不是翻倍,而且从零到一那一跳性价比最高。核心论点是:模型越大,越擅长从例子里领会你要什么。
挑示例要"相关、多样、有结构",用 <example> 标签包好。但不要堆砌一长串边缘情况——官方原话是"We do not recommend this",应改为策展一组典型示例;并且记住 minimal 不等于 short。最后,论文诚实列出了 few-shot 的失效清单:ANLI 等自然语言推理、RACE 与 QuAC 等阅读理解。它能教格式和口味,教不出本来不具备的推理能力。
而那块攻不下的阵地,正是下一节的主题——让模型把推理过程一步一步写出来。那里的提升幅度是从 17.7% 到 78.7%,真正的质变。顺带还要拆一个流传更广的传说:"深呼吸,一步步想"这句咒语究竟是谁想出来的。