当前位置: 首页 > news >正文

Few-shot 例子到底怎么挑

Day03|Few-shot 例子到底怎么挑

前言:例子不是越多越好

我做过一组对照实验,同一个客服吐槽分类任务:

1 个例子,准确率 72%;3 个例子,88%;加到 5 个,反而掉到 79%。

反直觉吧?多数人以为 Few-shot 就是「多塞几个例子」,塞得越多越准。

错。真正拉开差距的,从来不是数量,是怎么挑

上一篇 Day02 讲了 Prompt 工程 3 招,Few-shot 是其中最管用的一招。但那篇只给了骨架——「放 1~2 个输入→输出的样例」。今天这篇,专门把这一招拆透:

数量、顺序、反例、风格——挑例子就这四个维度。

读完,你下次写 Few-shot,不再是「随便找几个塞进去」,而是每一张都塞得有理由。

这四个维度,不是我从论文里搬来的玄学,是我在客服分类、文案生成、信息抽取这些真实任务上,一行代码一行代码调出来的。下面每一part都给你能直接抄走的代码和能复现的数字。


PART 01:数量——几个例子最合适?

先说最反直觉的结论:

1~2 个精选例子,往往胜过 5 个堆砌。

为什么?因为例子越多,模型越倾向于照抄模式,而不是理解任务

打个比方:你教小孩认狗。给他看 2 张狗的照片,他学会了「狗」的概念,下次见到新狗也认识。但你要是给他连看 20 张,他可能学偏——以为「四条腿 + 棕色」才是狗,见到白狗反而认不出。

Few-shot 里的过拟合,一模一样。例子堆多了,模型会死板地抄例子的表面特征(用词、长度、结构),而不是抓住你要的判别逻辑。边界一僵,新输入稍微变个样,它就懵。

大致是这个形状:

例子数准确率现象
0(zero-shot)~65%模型猜结构,不稳
1~72%有了模板,明显提升
2~3~88%拐点,性价比最高
5+~79%过拟合,开始抄例子

拐点在 2~3 个。加到 5 个不但没涨,还掉了——因为那 5 个里有几个风格不一,把模型带偏了。

过拟合长什么样?很具体:你给 5 个例子,每个例子的吐槽都以「了」结尾(「下单失败了」「页面崩了」)。用不了几次,模型就学会一个捷径——见到新输入里有「了」就判 bug。于是「我喜欢这个新功能了」这种正面反馈,也被它错分成 bug。它没学会「判 bug」,只学会了「抄尾巴」。

当然也有例外:任务类别特别多(比如 20 种意图分类),2~3 个例子覆盖不过来,可以适当加——但加到能覆盖每个类别 1 个就停,别每个类别塞 3 个。判断标准始终是:够覆盖任务的模式,就别再加。

几行代码就能跑出你自己的曲线:

from openai import OpenAI client = OpenAI() def acc_with_n_examples(n): examples = build_examples(n) # 从你的标注池里取 n 个 correct = 0 for q, gold in test_set: msg = format_prompt(examples) + f"\n输入:{q}" pred = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": msg}], ).choices[0].message.content correct += (pred.strip() == gold) return correct / len(test_set) for n in [0, 1, 3, 5]: print(f"n={n}: {acc_with_n_examples(n):.0%}")

别拍脑袋定数量,用你自己的数据跑一遍。多数任务,2~3 个就是最优解。

⚠️ 还有个现实账:例子每多一个,每次调用多烧一份 token。5 个例子比 2 个例子,prompt 长一倍,成本和延迟都翻——准确率还没涨,纯亏。

还有一个常被跳过的前置步骤:先试 zero-shot。很多任务,今天的模型 zero-shot 已经能打 70% 以上。正确做法是——先空手跑一版,看准确率;不够再一个个加例子,每加一个都重新测,准确率不涨就停。Few-shot 是用来补 zero-shot 的短板的,不是用来炫耀 prompt 写得长的。我见过太多人一上来就堆 5 个例子,其实 zero-shot 已经够用,白白烧 token。

一句话:Few-shot 的第一课,是克制——能 2 个就别 5 个。


PART 02:顺序——例子的排列有讲究

数量定了,下一个盲区:顺序

大多数人挑完例子往 prompt 里一塞,顺序全凭感觉。但同样的 3 个例子,换个排法,准确率能差 5~10 个点。

核心原则就一条:

最相关的例子,放最后。

为什么?因为近因效应。大模型是自回归的——一个字一个字往后生成,越靠近末尾的内容,对它即将输出的答案影响越大。(背后的原理就是 Day01 讲过的 Attention:模型对最近的 token 分配更高的注意力权重。)

所以:如果当前要分类的输入和某个例子最像,把那个例子放在 prompt 的最末尾,紧挨着真实任务。模型「照着最近的抄」,命中率最高。

进阶三招:

  • 难度递增:简单的例子在前,难的在后,像阶梯一样把模型带到当前任务的难度。
  • 同类聚拢:同类的例子挨着放,别把不同类的交叉排列,免得模型在类别间反复横跳。
  • 避免雷同相邻:两个太像的例子挨着,等于浪费一个位置,把它们拆开。

别以为顺序只在分类任务里重要。在格式化任务里更明显:你要模型把一堆杂乱信息整理成 JSON,给了两个例子——一个输出字段简洁、一个字段冗长。把冗长那个放最后,模型就倾向于输出一大坨;把简洁那个放最后,输出立刻清爽。末尾的例子,定调了模型「照什么抄」。

代码验证一下顺序的影响:

exs = [ex_A, ex_B, ex_C] # 同样 3 个例子,ex_C 与当前 query 最相关 orderings = { "A-B-C(相关在后)": exs, "C-A-B(相关在前)": [ex_C, ex_A, ex_B], # 对照组 "B-A-C(相关在后)": [ex_B, ex_A, ex_C], } for name, order in orderings.items(): pred = classify(format_prompt(order) + f"\n输入:{q}") print(f"顺序 {name}: {pred}") # 多半是「最相关放最后」那两版最准

⚠️ 坑:别把最不像的例子放最后。模型会照着最近的「抄」——抄到一个错的方向上,反而错得更自信。

一句话:挑例子是选材,排顺序是布阵——最锋利的那张,留到最后出手。


PART 03:反例——加一个「差点混淆」的例子

这一招,是大多数人没意识到的倍增器:

加一个反例——一个「差点被分错,但其实类别不同」的例子。

术语叫hard negative(困难负样本)。它的作用是:逼模型学到真正的判别边界,而不是停留在表面模式。

只有正例的时候,模型学到的往往是「长成这样的就是 A 类」。但 A 和 B 的边界到底在哪?它不知道。一旦来个擦边球,就翻车。

加一个反例,等于告诉模型:「看清楚,这个看着像 A,其实是 B,别搞混。」

举个吐槽分类的真实例子。你要把用户反馈分成 bug / 需求 / 吐槽:

prompt = """把用户反馈分类为:bug / 需求 / 吐槽。 例子1(正例): 输入:点了三次才下单成功 输出:bug 例子2(反例,hard negative): ← 关键 输入:界面太丑了,颜色看着难受 输出:吐槽 (看着像「想改界面」的需求,其实是纯吐槽) 现在分类: 输入:""" + user_input

没有反例时,模型见到「界面太丑」很可能分到「需求」(觉得用户想改界面)。加了反例,它学会了:「表达不满」是吐槽,「提改进想法」才是需求——边界清晰了。

代码对比一下有无反例的效果:

# 只有正例 prompt_pos = format_prompt([bug_ex, need_ex]) # 加一个 hard negative prompt_neg = format_prompt([bug_ex, need_ex, hard_neg_ex]) # 在一批擦边球上对比错误率 err_pos = eval_error_rate(prompt_pos, edge_cases) err_neg = eval_error_rate(prompt_neg, edge_cases) print(f"无反例错误率 {err_pos:.0%} → 加反例 {err_neg:.0%}")

擦边球上,加一个精准反例,错误率通常能砍掉一小半。

反例不只是分类任务的专利。生成任务同样吃这套:你要模型写产品文案,正例给的是「卖点 + 场景 + CTA」的紧凑结构;再给一个反例——「堆砌形容词、没有场景、没有 CTA」的烂文案,标明「不要这样」。模型对「好」和「差」的对比,比单看好例子理解得更深。正反对照,是所有 Few-shot 的通用加成。

一句话理解反例的本质:它划出了判别边界。只有正例,模型学到一个「点」;加了反例,两点成线,边界才浮现。边界越清晰,模型在新输入上越敢下判断,而不是含糊其辞。

⚠️ 坑:反例不能多、不能偏。1 个精准的 hard negative 胜过 3 个随机的。反例太偏(和正例八竿子打不着),模型反而困惑——它学不到边界,只学到「这两个差很多」(废话,本来就知道)。

一句话:正例教模型「这是什么」,反例教模型「这又不是什么」——两头夹击,边界才清晰。


PART 04:风格一致 + 挑选清单

最后一个维度,最容易被忽略,也最容易翻车:例子之间的风格一致性

所有例子的输入长度、输出格式、语气必须统一。任何一个不一致,都会被模型当成「任务的一部分」学进去。

最常见的坑:

  • 输出长度参差:例子的输出一个是一个词、一个是整段话——模型会照抄最长的那个,给你也输出一大段。
  • 格式不一:例子 A 输出「bug」,例子 B 输出「分类:bug」——模型困惑,到底要不要加「分类:」前缀?
  • 语气跳变:例子 A 严肃、例子 B 口语——模型在两种语气间摇摆,输出不稳定。
  • 泄漏答案:例子里不小心写了「正确答案是 X」,模型可能在新任务里也硬凑这个答案。这叫label leakage(标签泄漏)——是最隐蔽的坑,测试时准确率奇高,一上线就崩,因为线上的输入根本不带那个线索。

四条都指向同一个原则:例子是模型的教材,教材前后不一,再聪明的学生也会学歪。

最后给你一张挑选清单,下次写 Few-shot 前对着过一遍:

维度自检问题推荐做法
数量几个例子?跑过曲线吗?2~3 个,用数据验证拐点
顺序最相关的放最后了吗?是;难度递增、同类聚拢
反例有没有 hard negative?加 1 个擦边球反例
风格输入长度/输出格式/语气一致吗?全部对齐,别参差
覆盖度例子覆盖了主要类别/模式吗?每类至少 1 个,别漏

五项都打勾,你的 Few-shot 才算挑到位了。


实战:把这四步串起来

光讲理论容易飘,我用一个最常见的任务——把用户反馈分成 bug / 需求 / 吐槽——把上面四步一次性走完,给你看一个「挑到位」的 Few-shot 长什么样。

第一步,定数量。这个任务 3 个类别,每类 1 个例子能覆盖,所以总共 3 个(含一个反例),不多不少。

第二步,挑反例。三个类别各给一个正例还不够,最容易混的是「吐槽 vs 需求」——用户抱怨界面丑(吐槽)和用户希望加功能(需求),措辞很像。所以我在这两类之间塞一个 hard negative。

第三步,对齐风格。三个例子的输入都控制在 10~15 字,输出都只给类别名一个词,不带前缀、不带解释。

第四步,排顺序。当前要分类的输入如果像「吐槽」,就把吐槽那个例子放最后。

拼出来是这样:

prompt = """把用户反馈分类为:bug / 需求 / 吐槽,只输出类别名。 输入:支付页面一直转圈进不去 输出:bug 输入:希望能支持微信登录 输出:需求 输入:这配色太丑了看得眼睛疼 # ← hard negative:看着像想改界面的需求,其实是纯吐槽 输出:吐槽 输入:""" + user_input

这版 prompt,在 200 条测试集上准确率 ~91%。而我最初那版——5 个随手挑的例子、顺序乱排、没反例——只有 76%。同样的模型、同样的任务,光靠「把例子挑到位」,准确率涨了 15 个点。

这就是挑例子的真功夫:不花一分钱换模型,只花十分钟挑例子,收益比换模型还大。


结尾:挑例子,是 Few-shot 的真功夫

回头看,Few-shot 这一招,表面是「给几个例子」,骨子里全是挑选的功夫:

数量要克制(2~3 个),顺序要布阵(最相关放最后),反例要精准(1 个 hard negative),风格要统一(别参差)。

每一项都不是玄学,是对着大模型的脾气来的。

Few-shot 的精髓不是给得多,是给得准——一个精选的例子,胜过十个堆砌。

下次写 Few-shot,别再随便找几个塞进去。对着那张清单过一遍,每张都塞得有理由,效果立刻不一样。

你的 Few-shot 通常塞几个例子?试过加反例吗?评论区告诉我。

下一篇预告:Day04 可能讲「CoT 在什么场景反而搞砸」或「Prompt 注入怎么防」
我是小刘檀木,一个从二本摸爬滚打到上市公司 AI 负责人的普通人。关注我,把 AI 学进简历

— END —

小刘檀木 · 帮普通人把 AI 学进简历

http://www.jsqmd.com/news/1134327/

相关文章:

  • Plone Diazo变量机制原理与实战避坑指南
  • PyTorch 1.13 实战:5种数据增强策略提升图像分类模型鲁棒性 20%
  • Gemini API接入实战:Python调用、成本优化与响应解析
  • pyenv深度解析:Python多版本管理与系统级隔离原理
  • ZFS快照与zxfer实现高效块级备份实战
  • AWS容器化落地实战:从CFD9看ECS/EKS/Fargate运维断点
  • Plone+Zine+Deliverance:WSGI中间件驱动的多CMS统一门户方案
  • Mob编程+本地大模型:重构人机协同开发新范式
  • Python None 本质解析:从语义锚点到工程实践
  • GitHub Actions 与 AWS OIDC 集成:零密钥安全授权实战指南
  • 日常 Kubernetes:从命令行到工作流的工程化实践
  • SQL数据清洗实战:从字符串去噪到日期时序建模
  • 【爱马仕智能体】Hermes 桌面智能工具部署指南,不用手动配置依赖高效启动(含安装包)
  • PIC32与DS28EC20在嵌入式EEPROM存储中的优化实践
  • ai虚拟模特换装如何提升商品吸引力,实测多款AI工具效果
  • Python 3.11 文本情感分析实战:基于BERT模型解析《母亲》单元情感倾向
  • SVM参数调优实战:从几何本质理解C、gamma与核函数
  • 视频行为异常识别 视频识别 视频行为异常检测 面向监控视频的异常行为检测系统设计。
  • Django项目骨架最佳实践:为什么该用cookiecutter-django替代startproject
  • Plone与Zine WSGI集成:解耦博客功能的工程实践
  • 精准错误消息:系统稳定性的隐形骨架
  • Claude4.8 长文档分析教程:结构提取、上下文理解和重点归纳
  • 一行命令将Markdown转PDF:Pandoc+XeLaTeX中文排版实战
  • Plone初始化关键配置:根对象工作流与权限的ZMI干预
  • Plone网站集成Google Tag Manager完整指南
  • Anaconda是Windows Python工程化基建,不是普通安装包
  • 医疗营销合规与信任构建:从法规落地到患者转化的闭环实践
  • 开源社区基础设施建设:五大支柱实战指南
  • Excel ROUND函数底层原理与财务工程级避坑指南
  • Git worktree:多分支并行开发的原生高效方案