上下文学习
不改一个参数,只靠提示里的几个例子就学会新任务
In-Context Learning · ICL · 情境学习 · few-shot 学习
- 是什么——zero-shot / few-shot 到底指什么,「不训练就学会」是什么意思。
- 与微调的区别——同样是「学新任务」,改不改权重带来了哪些根本差异。
- 凭什么——一个只会猜下一个词的模型,看几个例子就会照做,机制上说得通吗。
- 反直觉发现——为什么示例的标签「对不对」,没你想的那么重要。
- 怎么用好——示例的数量、选择、顺序、格式,各自怎么影响效果。
- 边界——它这么方便,为什么还需要微调;它的天花板在哪。
- 把几个示例写进提示,模型读完就照做——这就是上下文学习(zero / one / few-shot)。(§1)
- 它和微调的根本区别是「改不改权重」:临时适配 vs 永久改造。(§2)
- 它之所以成立,是因为「预测下一个词」逼模型学会了「识别模式并接着写」,示例把它拨到对的任务频道。(§3)
- 所以示例主要在演示「任务的样子」(格式/标签集合/输入分布),而非灌输正确知识——标签个别错配影响不大。(§4)
- 因此给示例讲究:数量适度、选相似且覆盖全、打散顺序、格式一致。(§5)
- 把示例的「样子」换成带推理过程,就顺手得到 few-shot 思维链。(§6)
- 但它受窗口所限、不稳定、不持久、超出示例范围易崩——这划出了它与微调的分界。(§7)
1什么是上下文学习直觉
上下文学习要回答的是一个看似矛盾的问题:模型没有经过任何额外训练,怎么可能在一次请求里「学会」一个它从未专门练过的新任务?办法出奇简单——把任务的几个示例直接写进提示里,模型读完就照着做。
先看输入与输出。一次上下文学习的输入由三部分构成:一段任务说明、零个或多个「输入 → 输出」示例,以及一个新的查询。输出是模型按照提示中临时呈现的规律生成的答案。整个过程里模型只做一次前向计算,利用示例来约束生成,参数不更新。这一点的直接后果是:结果只说明当前这一次请求成功遵循了示例给出的映射;删掉示例、或换一次请求,这个约定不会自动保留。
按提示里给几个示例,可以分成三档:
| 档位 | 提示里给几个示例 | 例子 |
|---|---|---|
| zero-shot(零示例) | 0 个,只给任务描述 | 「判断下面评论的情感:又贵又难吃」 |
| one-shot(单示例) | 1 个 | 给 1 组「评论 → 情感」,再问新的 |
| few-shot(少示例) | 几个 | 就是下面图 1 那个最小演示 |
few-shot 提示的构成就是几组示例加一个查询。它的骨架是:前面几行用「输入 → 输出」的配对演示任务,最后一行只给输入、把输出留空。例如前两行给出「太棒了 → 正面」「又贵又难吃 → 负面」,最后一行是「服务很周到 → 」。模型要做的,还是它唯一会做的事——预测下一个 token,而此刻「最可能的下一个 token」正好是「正面」。
运行示例:连标签含义也由上下文临时定义
把熟悉的「正面/负面」换成两个任意标签 zorp / blip,能更清楚地看到模型究竟从提示里恢复了什么。给出这样的提示:
- 「太棒了」 → zorp
- 「又贵又难吃」 → blip
- 「服务很周到」 → ?
模型能从第一行提取出 zorp 与正向情感相关联,从第二行提取出 blip 与负向情感相关联;沿着这条临时映射,第三行应输出 zorp。
需要注意,这不是把 zorp 的新含义永久写入权重,而是在当前 token 序列里利用示例建立临时映射。删掉前两行之后,模型没有理由在下一次请求中继续按这个约定回答:这条映射只存在于这段序列的推理过程里,不会沉淀为任何可复用的参数变化。
因此「学习」二字要小心使用。它并没有像人那样把知识「记住」:这里的「学」是当场照着示例办,全程不改一个权重;对话一结束就忘,下次还得把示例重新给一遍。这个「临时约定、不改参数、用完即散」的特性,正是它与微调等传统训练方式的根本分野,后面的章节会把这层含义说透。
| 档位 | 提示里给几个示例 | 例子 |
|---|---|---|
| zero-shot(零示例) | 0 个,只给任务描述 | 「判断下面评论的情感:又贵又难吃」 |
| one-shot(单示例) | 1 个 | 给 1 组「评论 → 情感」,再问新的 |
| few-shot(少示例) | 几个 | 就是上面那个最小例子 |
| 提示行 | 模型可提取的信息 |
|---|---|
| “太棒了” → zorp | zorp 与正向情感关联 |
| “又贵又难吃” → blip | blip 与负向情感关联 |
| “服务很周到” → ? | 沿临时映射应输出 zorp |
2它和微调的根本区别直觉工程
「让模型学会新任务」这件事,不是已经有微调(fine-tuning)了吗?两者差在哪?差在一件根本的事:改不改权重。微调是在训练阶段用数据永久改造模型的参数;上下文学习是在使用阶段靠一段提示临时适配,参数一动不动。
顺着这条主线,两类方案在输入、机制和适用场景上逐项分化。比较的输入是任务稳定性、样本量、延迟、调用规模和维护约束;输出则是两种方案之一:临时提示适配,或参数训练方案。上下文学习每次把示例作为 token 参与前向计算,微调则用损失和梯度更新权重。前者适合快速试验,后者适合长期固化,而且两者并不是互斥的,也可以组合使用。
微调是一次「改造模型」的工程:结果写进权重,对之后所有请求都生效;上下文学习是一次「临时借用」:只在这一段提示里有效,请求一结束就没了。把几条关键维度放在一起看:
把分工浓缩成一句话:上下文学习是运行时的临时适配,微调是训练时的永久改造。任务少、要立刻用、经常变,选前者;任务固定、样本多、要长期稳定省成本,选后者。
| 上下文学习 | 微调 | |
|---|---|---|
| 改权重吗 | 不改 | 改 |
| 要什么 | 一段带示例的提示 | 一批标注数据 + 一次训练 |
| 多快见效 | 即时 | 要训练,慢 |
| 能持续多久 | 一次性,用完就忘 | 永久,之后请求都带着 |
| 容量 | 受上下文窗口限制,示例塞不多 | 可用海量样本 |
| 成本结构 | 每次请求都为示例付 token | 训练一次贵,之后每次请求省 |
3它凭什么成立直觉
这是最需要较真的一环:一个训练目标只是「预测下一个词」的模型,凭什么看几个示例就会照着做,而从来没有谁教过它「学习」?先把疑点摆明:模型从未被显式训练过「读示例、归纳规律、应用到新输入」这套流程。上下文学习是一种没被直接教、却自己冒出来的能力。它从哪来?
线索藏在预训练数据里。互联网文本中充满了重复的模式:一份格式统一的列表、一串「问:… 答:…」、一段一问一答的对话、一张「英文—中文」对照表。要把这些文本的下一个词猜准,模型别无选择,必须学会一件事——识别当前正在进行的模式,并接着这个模式往下写。看到前面是「A → 甲、B → 乙」,要续写「C → ___」,最可能的下一个 token 当然是按同样的映射规则 C 所对应的那个。few-shot 提示正是人为造出这样一个模式:给几组「输入 → 输出」,模型识别出「哦,现在在做 X → Y 的映射」,于是对新输入照做。
所以上下文学习不是一种凭空出现的新能力,而是「预测下一个词」这个训练目标被逼出来的副产品——和推理、翻译一样。模型早就见过无数情感分类式的文本;few-shot 示例真正的作用,可能不是「从零教你这个任务」,而是告诉模型:在你预训练学过的成千上万种模式里,现在要调用的是哪一种。示例把它「拨」到那个频道上。
从机制视角看,输入是带重复格式和映射关系的 token 序列,输出是符合该局部模式的下一个 token 分布:预训练让模型练习补全过大量列表、问答和对照表,因此示例可以帮助它定位当前任务模式。这个「任务定位」的视角也能解释下一节那个反直觉的发现。
不过要诚实说一句:上下文学习内部到底在发生什么,仍是活跃的研究课题,存在多种解释——任务定位、在前向传播里隐式地做类似梯度下降的更新等——尚无定论。上面给出的是一种解释力较强、站得住也够用的工作模型,而不是已经证明的唯一内部算法,更不是盖棺结论。
4反直觉:示例标签「对不对」没那么重要数学工程
既然模型是靠着示例在「学」,那示例的标签是不是必须全对?研究者的做法是做对照实验:拿原始 few-shot 提示,再分别造出打乱标签、打乱格式、换掉标签集合、换掉输入分布的版本,让模型各跑一遍任务,比较每次改变带来的性能下降幅度。下降小,表示这一项在当前任务里影响较弱——注意,这只是当前任务的结论,并不表示它在所有任务里都不重要;专业任务与新知识映射仍可能高度依赖正确的示例。
实验结果让人意外。把 few-shot 示例的标签故意打乱、随机配对——比如给出「太棒了 → 负面」这种错配——模型的性能常常只掉一点点。但动另外三样东西,性能会大幅下滑:
把这些结果放在一起,结论是:few-shot 示例主要在演示任务的「样子」——输入长什么样、答案什么格式、标签有哪几种——而不是在给模型灌输一条条正确知识。这正好印证了「任务定位」的视角:示例是把模型拨到对的频道上,而正确的判断力它早在预训练里就有了。
但别把这条结论误读成「示例可以乱写」。它说的是标签个别错配影响不大,不是鼓励你给错误示例:格式、标签集合、输入分布都要对且一致,而在更难、更专业的任务上,示例本身的正确性依然重要。更稳妥的理解是:把示例当成「给模板」,而不是「随便给」。
| 动什么 | 对性能的影响 | 说明什么 |
|---|---|---|
| 把示例标签打乱(错配) | 影响较小 | 示例不是靠「提供正确知识」起作用 |
| 破坏输出格式(分隔符、排版乱) | 影响大 | 示例在演示「答案长什么样」 |
| 换掉标签集合(正/负 → 无关词) | 影响大 | 示例在圈定「可能的答案范围」 |
| 用无关的输入分布 | 影响大 | 示例在演示「输入大概是什么样」 |
5怎么把示例给好工程
效果既然几乎全压在这几个示例上,那到底怎么给才最有效?示例选择这件事,输入是候选示例、当前查询和 token 预算,输出是一条数量适中、类别覆盖、顺序稳定且格式统一的提示。可以先保证正确与一致,再优先选与查询相似且覆盖边界的样本,并用多个顺序复测;判断结果时应看目标切片上的稳定收益,而不是只看某一次排列。具体拆成四件事。
数量:从 0 个到 1 个、再到几个,提升最明显;再往上边际递减,而且每个示例都占上下文窗口的预算。示例不是越多越好,0 → 1 → 2 通常提升最大,再加就趋平,挑得准比堆得多更重要。
选择:挑和当前查询相似、质量高的示例;若是分类任务,尽量覆盖各个类别,别让模型误以为答案只有一种。
顺序:模型对示例顺序敏感,还常有「离查询越近影响越大」的近因偏置。别把同类示例堆在一起,打散排列更稳。
格式:用一致、清晰的分隔符和排版,比如统一的「输入:… 输出:…」。第 4 节的对照实验已经说明,格式一致往往比标签个别正确更影响结果。
这四件事属于更大的一层:往上下文窗口里放哪些示例、怎么排,本质是上下文工程的一部分;给单条提示配示例,则是提示工程里最常用的一招。上下文学习,就是这招之所以有效的原理。
6一种特殊的用法:让示例演示「怎么想」直觉
前面的示例只给了「输入 → 答案」。如果示例里连「怎么一步步想出答案」也写出来,会怎样?模型就会照着「先推理、再给答案」这个模式来答新问题——这就是 few-shot 思维链。
few-shot 思维链的输入是包含中间推理格式的示例加上一个新问题,输出是模型仿照该格式生成的步骤与答案。它的机制是模式续写:你没有教模型推理,只是用示例设定了「输出里应该包含推理步骤」这一模式,模型便照办,并通过这种续写诱导出更长的中间计算。需要注意,生成出来的步骤看着合理,不等于事实正确;验收时应以最终答案、可验证的中间结果和任务边界共同判断。
它依然是上下文学习。思维链之所以能靠几个示例触发,正因为上下文学习让模型「照着示例的样子续写」:当示例的样子是「带推理过程的解答」,模型续写出来的,也就是带推理过程的解答。
7边界与代价工程直觉
上下文学习这么方便、又不用训练,那为什么还需要微调?它的天花板在哪?做这个判断时,输入是窗口容量、提示敏感性、知识新颖度、请求规模和稳定性要求,输出是继续用上下文学习、转向微调、或两者组合的决策。一个实用的信号是:若增加示例只提高 token 成本却不再改善分桶质量,或者换个顺序就越过业务阈值,就说明临时适配已经接近实用边界。它的限制集中在四个方面。
受窗口限制:示例都挤在上下文窗口里,塞不了太多;示例一多,又慢又贵,还可能触发「中间迷失」——长上下文里中段信息被忽略。
不稳定:对示例的措辞、顺序、格式都敏感,换个排列结果就变,难复现、难保证。
不持久、不更新知识:什么都没写进权重,每次请求都得把示例重放一遍;它也不会因此真正「记住」新知识。
不等于真掌握:一旦查询超出示例覆盖的范围,就容易崩。任务复杂、专有、样本多时,微调通常更稳,长期看每次请求还更省 token。
把两类场景的取舍并列来看:
务实的做法是把两者看作阶梯,而不是对立:先用上下文学习快速验证「这事模型能不能做」,跑通、量上来了、要长期稳定省成本,再考虑用微调把它固化下来。
| 更适合上下文学习 | 更适合微调 |
|---|---|
| 任务多变、要立刻试 | 任务固定、长期跑 |
| 只有几个示例 | 有大量标注样本 |
| 不想训练、不想维护模型 | 要稳定、可复现、低单次成本 |
| 原型验证、临时需求 | 规模化、对格式和风格要求严 |
8把整条因果链连起来综合
把整页内容串成一条链,逐环检查每个结论从何而来。
起点是:把几个示例写进提示,模型读完就照做——这就是上下文学习,按示例个数分为 zero-shot、one-shot、few-shot。它和微调的根本区别在于「改不改权重」:上下文学习是运行时的临时适配,微调是训练时的永久改造。
它之所以成立,是因为「预测下一个词」这个训练目标逼模型学会了「识别当前模式并接着写」;few-shot 示例就是人为造出一个模式,把模型拨到对的任务频道上。由此推出第 4 节的发现:示例主要在演示「任务的样子」——格式、标签集合、输入分布——而不是在灌输一条条正确知识,所以标签个别错配影响不大。
顺着这条结论,给示例就有了讲究:数量适度、选与查询相似且覆盖各类别、打散顺序、格式一致。而如果把示例的「样子」换成带推理过程的解答,就顺手得到了 few-shot 思维链——它仍是上下文学习,只是把要续写的模式换成了「先推理、再给答案」。
链的最后一环是边界:受窗口所限、对措辞顺序敏感、不持久、超出示例覆盖范围就容易崩。这些代价划出了它与微调的分界:先靠示例快速验证,再按需用微调固化。能讲清「为什么一个只会猜下一个词的模型能靠几个示例学会新任务」,并说出「示例主要在演示任务的样子、而不是灌输知识」,就抓住了上下文学习的内核。
11概念依赖与延伸学习路线
沿着学习层级看,本页的概念各有其依赖与去向。
先修概念奠定了本页的机制底座:上下文学习建立在自回归生成「预测下一个 token」的目标之上,few-shot 示例正是对这一目标的利用。紧邻延伸则是本页概念的直接去处——给提示配示例属于提示工程,往窗口里安排示例属于上下文工程,few-shot 思维链把「示例演示什么」推进到「演示怎么想」,而上下文窗口的容量又划定了示例数量的边界。更远一层,示例能不能被检索来的资料替换指向检索增强生成,上下文学习随模型规模显现的能力则与缩放定律和涌现能力之争相连。
| 学习层级 | 涉及概念 |
|---|---|
| 先修 | 大语言模型、预测下一个 token、自回归生成、提示 |
| 本页核心 | zero/one/few-shot、任务定位、示例的格式与顺序、与微调的对比 |
| 紧邻延伸 | 提示工程、上下文工程、思维链 CoT、上下文窗口、微调 |
| 更远 | 缩放定律、检索增强生成 RAG(把「示例/资料」换成检索来的)、涌现能力之争 |
- Language Models are Few-Shot Learners:大模型 few-shot 上下文学习的系统实验。
- Rethinking the Role of Demonstrations:标签、格式与输入分布在示例中的作用。
- An Explanation of In-context Learning as Implicit Bayesian Inference:上下文学习机制的一种理论解释。