Lecture 10 · 自动提示与自我验证 · Auto-Prompting and Self-Verification

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

课程源码 ZIP ↓

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

本篇目录

对应 Maps to:CMU 17-630 Prompt Engineering(Prof. Travis Breaux),第 6 周的第二个主题 · Auto-Prompting。原课 Schedule 对这一讲的说明(意译):各种”自我提示”的技术,包括指令归纳、自我纠正、验证链和 LLM-as-a-judge。 依据说明:原课的 slides 和阅读清单不公开。Schedule 点名的四项——指令归纳(§2)、自我纠正(§5)、验证链(§7)、LLM-as-a-judge(§8)——各占一节;APE 与 OPRO(§3)、提示优化里的过拟合(§4)、Self-Refine 与反馈的质量(§6)是讲义补充的。材料来自 Honovich et al.(2023,Instruction Induction)、Zhou et al.(2023,APE)、Yang et al.(2024,OPRO)、Madaan et al.(2023,Self-Refine)、Huang et al.(2024,LLMs Cannot Self-Correct Reasoning Yet)、Dhuliawala et al.(2024,CoVe)、Zheng et al.(2023,MT-Bench / LLM-as-a-judge)、Dubois et al.(2024,长度控制的 AlpacaEval)、Verga et al.(2024,PoLL)等;引用的数字都核对过原文。全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:code/lecture10/(只用 Python 标准库;1 个模块 + 6 个脚本;不需要 API key;每个脚本 20 秒以内) 预计阅读时间:3.5 小时(含运行代码)


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

0.1 学习目标 Learning goals

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

  1. 把”自动提示”的各种方法放进同一个框架:一个生成器提出候选,一个评估器打分挑选;说出每种方法里谁是生成器、谁是评估器。
  2. 解释指令归纳为什么本质上是一个假设搜索问题,为什么例子要覆盖边界情况。
  3. 写出 APE 和 OPRO 的流程,并能估计”在开发集上挑第一名”会虚高多少;知道怎样用验证集、提前停止来防止过拟合。
  4. 区分内在的自我纠正和有外部反馈的自我纠正;说清楚 Huang et al.(2024)的三个批评:标准答案泄漏、不公平的初始提示、和同样调用次数的 self-consistency 比较。
  5. 说出验证链(CoVe)有效的条件(短回答比长回答准、验证时看不到草稿),以及它去不掉哪类幻觉。
  6. 测量一个 LLM 判官的位置偏差、长度偏差、自我偏好,并用交换顺序、长度控制、评审团、参考答案去修正。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲只给直觉,后面再展开 Previewed only
前置讲 P3:置信区间、配对检验、赢家诅咒代码生成里的自我调试(第 11 讲)
第 2 讲:上下文学习,提示对说法和示例的敏感性多智能体辩论(第 15 讲)
第 4 讲:思维链、self-consistency、SimReasoner评估设计、benchmark 与指标(第 17 讲)
第 5 讲:长尾知识、稳定的幻觉、SelfCheckGPT
第 6 讲:RLHF 与谄媚;第 8 讲:CRITIC(用工具验证)

0.3 本讲地图 Map

§1  为什么要"自动":生成器 + 评估器
§2  指令归纳:从例子猜指令                                          ← 代码 01
§3  APE 与 OPRO:生成很多、挑一个;把 LLM 当优化器                  ← 代码 01、02
§4  提示优化里的过拟合:赢家诅咒、轨迹、真假改进、补救              ← 代码 02
§5  自我纠正:没有外部反馈时"再想想"为什么不行                      ← 代码 03
§6  Self-Refine:反馈的质量决定一切                                 ← 代码 03
§7  验证链 CoVe:把长回答拆成可以单独核对的说法                     ← 代码 04
§8  LLM-as-a-judge:偏差与修正                                      ← 代码 05
§9  在真实模型上                                                    ← 代码 06(可选)
§10 小结   §11 常见误区   §12 练习   §13 延伸阅读   §14 下一讲预告

0.4 记号与术语 Notation & terms

术语本讲义里的意思
指令归纳(instruction induction)给模型看几个输入—输出的例子,让它写出”产生这些例子的指令”
执行准确率(execution accuracy)让模型照着一条指令做题,做对的比例——评价一条指令的标准
开发集 / 验证集 / 测试集开发集:搜索时打分用;验证集:最后在少数候选里做决定用;测试集:只用来报告,从不参与挑选
虚高(optimism)被挑中的候选在开发集上的分数 − 它在测试集上的准确率
内在的自我纠正(intrinsic self-correction)没有任何外部信息,只让同一个模型审视并修改自己的回答
外部反馈(external feedback)来自模型之外的信号:程序、测试、检索结果、计算器、人
判官(judge)给回答打分或在两个回答之间选一个的模型
位置偏差 / 长度偏差 / 自我偏好判官偏爱放在某个位置的、更长的、自己(家族)写的回答

0.5 本讲的实验装置 · The setup

这一讲的大部分实验在一个词表变换世界(autoprompt_lab.py)里做。每道题是一串小写单词,8 个隐藏任务由 1–3 个操作组成:

输入                  bee turtle lemon banana bee lemon pear
dedupe_sort(去重→排序)  banana bee lemon pear turtle
short_upper(删短词→大写) TURTLE LEMON BANANA LEMON PEAR
  • 执行模型 SimExecutor 读英文指令,真的一句一句地执行。每个操作有几种说法,说法越含糊,越容易在某道题上被”读歪”:Capitalize the words. 在 40% 的题上被读成”只大写首字母”,Remove short words. 在 40% 的题上连 4 个字母的词也删掉。读没读歪由(这句话, 这道题)的哈希决定,所以两个共用同一句话的提示错在同样的题上——和真实模型一样,提示之间是相关的。
  • 万能句:Take a deep breath and work on this step by step.、Be careful.、You are an expert at word games. 这类和任务无关的句子。模拟器里只有一句(Double-check each word before answering.)有真实的好处,一句有真实的坏处,其余的期望效果是零——但它们会”扰动”一部分题,改变错在哪些题上。§4 会看到这正是搜索会”发现”的东西。
  • 归纳模型 SimInducer 看几个例子,在 314 个”操作组合”假设里真的搜索能解释所有例子的那些,偏爱简单的,再写成英文。

另外三套装置:第 4 讲的多步算术推理者 SimReasoner(§5),一个约束生成任务(§6),一个长尾世界里的列表题模型 SimListLLM(§7),以及一个有三个偏差旋钮的判官 SimJudge(§8)。模拟器的每种行为都来自一个显式的旋钮,它们能把论证变具体,但不能证明真实模型就是这样;每次用它们,讲义都给出论文里真实模型的数字作对照。


1. 为什么要”自动” · Why automate

第 2 讲你看到,同一个任务,换一种模板、换几个示例、换一个标签词,准确率就能差 20 个百分点。提示工程的日常工作就是在一个巨大的离散空间里搜索:试一种说法,看看结果,再改。这件事人做起来又慢又依赖直觉。一个自然的想法是:既然模型会写字,能不能让它自己来写提示、改提示、检查答案?

这一讲的所有方法都可以放进同一个框架:

          ┌──────────── 生成器:提出候选(提示 / 回答 / 修改)
循环 ──── ┤
          └──────────── 评估器:给候选打分,决定留下谁、要不要再改
方法生成器评估器
指令归纳、APE(§2–§3)LLM 看例子写指令开发集上的执行准确率
OPRO(§3)LLM 读历史候选和分数,写新候选开发集上的分数
内在的自我纠正(§5)同一个 LLM 重写答案同一个 LLM 的判断
Self-Refine(§6)同一个 LLM 按反馈改写LLM 写的反馈(或程序)
验证链 CoVe(§7)LLM 写草稿同一个 LLM 单独回答的验证问题
LLM-as-a-judge(§8)任何系统另一个(或同一个)LLM

这张表里藏着这一讲的主线:自动化放大的是评估器。生成器可以很便宜地提出成百上千个候选,最后留下哪一个,完全由评估器决定。评估器有噪声(开发集太小,§4),评估器和生成器犯同样的错(同一个模型检查自己,§5、§7),评估器有偏差(判官偏爱长回答,§8)——这些缺陷都会被搜索放大:搜得越多,越可能挑中一个”恰好骗过评估器”的候选。

所以读这一讲的每一节时,都问同一个问题:这里的评估器可靠吗?它和生成器独立吗?


2. 指令归纳:从例子猜指令 · Instruction induction

2.1 任务和评价方式

Honovich et al.(2023)提出了”指令归纳”这个任务:给模型看几个输入—输出的例子,然后写 “The instruction was”,让它续写出产生这些例子的指令。他们整理了 24 个小任务(变复数、找反义词、翻译、判断情感……)。

怎样评价一条归纳出来的指令?他们的做法是执行它:把这条指令交给一个模型,让它在新输入上照着做,看做对多少——执行准确率。这个评价方式很重要:一条指令好不好,不看它和人写的指令字面像不像,而看它能不能让模型做对。

他们报告:InstructGPT 的执行准确率达到人写指令的 65.7%,原始的 GPT-3 只有 9.8%。会不会归纳指令,取决于模型是否足够大、是否经过指令微调(第 3 讲)。

2.2 归纳是一个假设搜索

为什么归纳会错?因为几个例子不能唯一地确定一条规则。看这个例子:

Input: bee turtle lemon banana          Output: banana bee lemon turtle

“按字母排序”能解释它;“先去重再排序”也能(这个例子里没有重复的词);“先倒序再排序”也能。归纳模型必须在所有能解释例子的规则里挑一个——人会挑最简单的那个(奥卡姆剃刀),模型通常也是。

SimInducer 把这个过程显式地做出来:在 314 个”操作组合”假设里,找出能解释全部例子的那些,按”越简单越可能”随机挑一个,再写成英文(还有 15% 的概率漏看一个例子——模型并不总是认真对待每个例子)。01_instruction_induction.py 演示 1:8 个任务、每种例子个数各归纳 40 次。

例子个数 k函数上正确 [95% CI]平均执行准确率其中函数上正确的那些
166.2% [60.9, 71.2]61.6%76.1%
277.2% [72.3, 81.4]67.0%75.4%
386.2% [82.0, 89.6]69.8%73.8%
594.1% [90.9, 96.2]72.4%73.7%
896.9% [94.3, 98.3]74.5%75.0%
人写的指令100.0%91.2%91.2%
最清楚的说法100.0%93.2%93.2%

“函数上正确”是指归纳出的操作组合在 300 道题上和隐藏任务完全一样。一个例子时只有 66%;例子多了,能解释所有例子的规则越来越少,到 8 个例子时 97%。

2.3 错在哪里:例子没有暴露的操作

演示 2 只给 2 个例子,看错在哪里:

错误类型占错误的比例
多了操作47.3%
漏了操作:dedupe37.0%
漏了操作:drop_short6.8%
操作对、顺序错5.8%
其他3.0%

“漏了”和”多了”是同一个原因:例子没有暴露某个操作的效果。例子里恰好没有重复的词,“去重”做不做都一样——模型可能不写它(漏了),也可能写一个对这两个例子没有影响的操作(例子里恰好没有短词时,“删短词”就是这样一个操作)。

怎么办?与其多给例子,不如挑好例子:同样是 2 个,挑”有重复词、有短词”的例子,函数上正确从 81.8% 提到 89.1%,接近随机给 3 个例子的水平。这和第 2 讲”示例的选择影响上下文学习”是同一件事,只是这里影响的是”从示例里归纳出的指令”。实践上:给模型(或者给人)看的例子要覆盖边界情况——重复、空输入、特殊字符、最长的情况。

2.4 两层正确:规则对,说法也要对

表的最后一列值得多看一眼:即使函数上正确,执行准确率也只有约 75%,比人写的指令(91.2%)低一大截。原因是归纳模型按”顺口”而不是按”清楚”挑说法:Capitalize the words.(顺口,但在 40% 的题上被读成首字母大写)比 Write every word in uppercase. 更常被写出来;它还可能多写一个对结果没影响、却可能被读歪的操作,或者加一句万能句。

所以一条指令要过两关:规则对(逻辑层面),说法清楚(执行它的模型层面)。第二关和执行模型有关——对这个模型含糊的说法,对另一个模型可能很清楚。这正是下一节”在开发集上挑”的理由:哪种说法好,要让执行模型自己来告诉你。


3. APE 与 OPRO:生成很多、挑一个 · Generate many, select one

3.1 APE:自动提示工程师

Zhou et al.(2023)的 APE(Automatic Prompt Engineer)把指令归纳变成一个搜索过程:

  1. 提议:从训练数据里抽几组例子,让 LLM 为每组写一条指令(正向生成,“The instruction was”;他们也试了让模型在一段文字中间填空的方式);
  2. 打分:每条候选在一小份开发数据上执行,算执行准确率(或者正确答案的对数概率);
  3. 挑选:留下分数最高的;
  4. (可选)迭代:让 LLM 给高分候选写”意思相近的变体”,再打分——他们叫迭代的蒙特卡洛搜索。

01_instruction_induction.py 演示 3:每个任务 20 条候选(每条看 5 个例子),50 道开发题上挑第一名,1000 道测试题报告。

任务随手拿一条APE 第一名:开发 → 测试+ 两轮换说法人写的最清楚的说法
sort84.5%98% → 95.6%98% → 95.6%96.2%97.2%
dedupe_sort77.7%96% → 94.1%96% → 94.1%93.4%95.4%
dedupe_rev_upper63.3%94% → 90.4%98% → 91.1%90.4%91.1%
short_sort_comma69.3%92% → 88.7%96% → 93.3%86.7%90.9%
8 个任务平均71.1%95% → 92.0%97% → 92.9%91.2%93.2%

三点:

  • “挑”本身就很有价值:随手拿一条平均 71.1%,挑出来的第一名 92.0%,和人写的指令(91.2%)差不多。候选里有一部分漏了操作、用了含糊的说法,开发集把它们筛掉了。
  • 迭代的收益很小:两轮换说法,92.0% → 92.9%。APE 的原论文也发现迭代搜索只带来很小的改进,大部分收益来自第一轮”生成很多、挑一个”。
  • 开发集分数偏高:第一名在开发集上平均 95%,测试上 92.0%。这不是偶然——§4 专门讲它。

APE 的原论文报告,自动生成的指令在大多数 Instruction Induction 任务上达到或超过人写的指令(论文不同版本报告的任务数不完全一样)。它还用同样的方法搜索零样本思维链的触发句,找到了 “Let’s work this out in a step by step way to be sure we have the right answer.”,在他们测的数学题上比 “Let’s think step by step.” 更好一点。

3.2 OPRO:把 LLM 当优化器

APE 的候选彼此独立地生成。Yang et al.(2024)的 OPRO(Optimization by PROmpting)更进一步:把优化过程本身交给 LLM。它的”元提示”(meta-prompt)里放着:

  • 到目前为止最好的 20 条指令和它们的分数,按分数升序排列(最好的在最后,离模型要写的地方最近);
  • 几个任务例子;
  • 一句要求:“写一条和上面都不同、分数更高的指令”。

每一步让优化器 LLM 写 8 条新指令,在训练集的一个固定子集上打分,加进历史,重复上百步。直觉上,LLM 读到”这几条得分高、那几条得分低”,能猜出好指令的共性,往那个方向改——一种用自然语言做的”梯度下降”。

OPRO 报告,优化出来的提示比人设计的提示在 GSM8K 上最多高 8 个百分点、在 Big-Bench Hard 的任务上最多高 50 个百分点。最有名的例子:在 PaLM 2-L 上,它找到的 “Take a deep breath and work on this problem step-by-step.” 在 GSM8K 测试集上得到 80.2%,而 “Let’s think step by step.” 是 71.8%。

autoprompt_lab.opro 是一个简化版:优化器从历史前 20 名里按名次加权挑一条,做一次修改(换一句的说法、加一句万能句、删一句、交换相邻两句、加一个操作句)。这个”优化器”不会推理,但它保留了 OPRO 的结构:只看分数,往高分的方向走。§4 会看到这种结构的问题。

3.3 一个提醒:这些句子为什么有效?

“Take a deep breath” 对一个模型有效,并不意味着它对所有模型都有效,也不意味着我们知道它为什么有效。从搜索的角度看,任何一句在开发集上碰巧有帮助的话都可能被留下来。要判断它是不是真的有用,需要下一节的工具。


4. 提示优化里的过拟合 · Overfitting in prompt optimization

4.1 赢家诅咒

前置讲 P3 §5 讲过赢家诅咒:从 N 个候选里挑开发集分数最高的那个,它的开发集分数是有偏的——它之所以得第一,一部分是因为真的好,一部分是因为在这几道题上运气好。

直觉:开发集分数 = 真实水平 + 噪声,噪声的大小约为 p(1−p)/n\sqrt{p(1-p)/n}。N 个候选里的最大值,大约在平均值之上 E[max⁡NZ]\mathbb{E}[\max_N Z] 个噪声单位(ZZ 是标准正态;N = 5、20、100 时分别约 1.16、1.87、2.51)。p = 0.9、n = 20 时一个噪声单位是 6.7 个百分点——100 个候选时,独立的情况下最多可以虚高 16 个百分点。

02_prompt_overfitting.py 演示 1:每个任务准备一个”像样的”候选池(测试准确率都在 80% 以上,差别主要在说法和万能句),从里面抽 N 条,在 n 道开发题上挑第一名,重复 100 次:

开发集 n候选数 N第一名的测试准确率第一名的开发集分数虚高比 N 条里最好的差
20188.3%88.2%−0.10.0
20589.7%95.1%+5.41.9
2010090.4%98.8%+8.54.2
5010092.0%97.3%+5.32.6
20010093.7%95.6%+1.80.8
  • 只有 1 条候选时没有”挑”,开发集分数是无偏的。候选越多,虚高越大;开发集越大,虚高越小。
  • 实际的虚高(8.5)比独立假设下的估计小,因为候选彼此高度相关(共用同样的句子、错在同样的题上),而且分数有 100% 的天花板。但方向完全一致。
  • 开发集小的时候,挑出来的也不是真正最好的那条(差 4.2 个百分点)——小开发集既虚报了成绩,又挑错了人。

4.2 迭代优化的轨迹

OPRO 式的迭代优化,比”从 100 条里挑一条”搜得更狠:60 步、每步 8 条,一共试几百条候选,而且每一步都在上一步的第一名附近找。演示 2 比较两种打分集:

第几步20 题:开发 / 测试 / 虚高200 题:开发 / 测试 / 虚高
175.6% / 74.2% / +1.481.1% / 79.0% / +2.2
687.5% / 85.8% / +1.793.6% / 91.9% / +1.7
1195.6% / 91.0% / +4.695.1% / 92.5% / +2.6
2197.5% / 90.0% / +7.595.8% / 93.5% / +2.3
6098.8% / 90.5% / +8.296.8% / 93.5% / +3.3

两件事同时成立:

  1. 优化是有效的。20 题打分时,测试准确率从 74.2% 涨到 90.5%——从一条一般的归纳指令改成了不错的指令。
  2. 开发集夸大了进步。开发集分数涨了 23 个百分点,测试只涨了 16 个;多出来的 8 个只存在于那 20 道题上。即使用 200 题打分,第 21 步之后测试准确率就不再上升(93.5%),开发集分数却还在涨——继续搜索只是在拟合这 200 道题的噪声。

OPRO 的原论文对此很坦率:他们在 GSM8K 上只用训练集的 3.5% 打分,训练准确率常常比测试高 5–20 个百分点,过拟合确实存在;他们建议用更大的打分集和提前停止。

(一个我们原本以为会出现、结果没有出现的现象:20 题的搜索留下的万能句并不比 200 题的多——1.6 句 vs 2.4 句。20 题的分数很快碰到天花板,20 道里只错 0–1 道,搜索再也区分不出新候选,于是停止了”改进”。判断过拟合要看虚高,不要数句子。)

4.3 被”发现”的句子,哪些是真的?

演示 3 把每句万能句加在最清楚的指令前面,在 5000 道题上和不加的版本配对比较,再看同一句话在 20 道题上的”效果”:

万能句5000 题上的效果20 题上的”效果”范围20 题上看起来有用的比例
Double-check each word before answering.+3.20−5 ~ +2046%
Take a deep breath and work on this step by step.−0.98−15 ~ +1015%
This is very important to my career.−1.03−10 ~ +1022%
Answer precisely.−0.82−10 ~ +1025%
Ignore anything that seems irrelevant.−4.81−20 ~ +52%

(没用的句子在 5000 题上略为负,是因为每多一句话有一点点长度代价。)

  • 没用的句子在 20 道题上有 1–2 成的时候”看起来有用”,有时 +10 个百分点——这就是它们在搜索里被留下的原因。
  • 反过来,真正有用的那一句在 20 道题上也只有不到一半的时候”看起来有用”。小开发集既会放进假的改进,也会错过真的改进。
  • 判断一句话是不是真的有用,要在同一组很多道题上做配对比较(P3 的 McNemar 检验),而不是看它在一次搜索里赢了没有。

4.4 怎样防

演示 4:仍然用 20 道题做搜索,比较几种补救(8 个任务平均):

做法挑选时看到的分数测试准确率虚高每个任务的执行次数
直接取第一名98.8%90.5%+8.28560
前 10 名在 200 道验证题上重新打分91.7%91.7%+0.010560
目标里加长度 / 万能句惩罚99.4%93.6%+5.86822
用验证集挑”哪一步的第一名”(提前停止)91.3%91.1%+0.29135
(对照)直接用 200 题打分96.8%93.5%+3.379225
  • 验证集重新打分是最便宜、最稳的补救:大部分搜索用小集合,最后在少数几条里做决定时用大集合。验证集只用来”挑一次、挑 10 条里的一条”,挑的次数少,诅咒就弱——它给出的分数几乎没有虚高。
  • 长度惩罚在这里效果最好(93.6%),但要小心这个结论:模拟器的万能句按设定大多没用,惩罚当然划算;真实模型上你不知道哪句话有用,惩罚也可能挡住真正有用的那一句,惩罚强度本身又是一个要调的超参数。
  • 直接用 200 题打分和惩罚差不多好,而且不依赖”哪些句子没用”的假设,代价是 9.3 倍的执行次数。

把这一节总结成几条规矩:

  1. 搜索用开发集,最后的决定用验证集,报告用测试集;测试集从不参与任何挑选(第 17 讲)。
  2. 搜得越多(候选越多、步数越多),开发集要越大,或者越要依赖验证集。
  3. 报告提示的效果时,报测试集上的数字和置信区间,而不是”开发集第一名的分数”。
  4. 对搜索”发现”的具体句子保持怀疑:用配对比较确认它真的有用,换模型时重新测。

5. 自我纠正:没有外部反馈时”再想想”为什么不行 · Self-correction

5.1 一个诱人的想法

人做题时会检查:算完再验算一遍,发现错了就改。能不能让模型也这样?一类工作让模型先回答,再让它”审视你的回答、找出问题、改进它”,报告了不错的提升。

Huang et al.(2024)的论文题目就是结论:Large Language Models Cannot Self-Correct Reasoning Yet。他们区分了两种情况:

  • 内在的自我纠正:没有任何外部信息,只让同一个模型审视自己。
  • 有外部反馈的纠正:有来自模型之外的信号——标准答案、程序执行结果、检索到的事实、工具。

他们发现,在推理任务上,内在的自我纠正通常没有帮助,甚至会变差;一些早期工作报告的提升,来自用标准答案决定什么时候停(答对了就不再改),这在实际中不可能做到。

5.2 在多步算术上

03_self_correction.py 演示 1:400 道 4 步的算术题,第 4 讲的 SimReasoner(medium 规模)。第一次回答是少样本思维链(一次采样);之后每一轮,模型”把题重新想一遍”(新的一次采样),两次答案不同时决定改不改。

做法准确率 [95% CI]调用次数对 → 错错 → 对
第一次回答58.5% [53.6, 63.2]1.0
中性的”检查一下”,2 轮58.8% [53.9, 63.5]5.06869
“找出问题并改进”(Huang et al. 的提示),2 轮51.2% [46.4, 56.1]5.011485
用标准答案决定停不停(作弊),2 轮90.5% [87.2, 93.0]2.20128
程序逐行检查算式(外部反馈),2 轮87.5% [83.9, 90.4]1.42118
self-consistency:5 次采样投票83.2% [79.3, 86.6]5.07106

逐行读:

  • 中性的纠正:改对 69 道,改错 68 道,正好抵消。模型第二次想出来的答案并不比第一次更可靠;两个答案不一样时,它分不清哪个对。
  • 施压的提示(“Review your previous answer and find problems with your answer”)更糟:被要求”找出问题”时,它在答案一致时也会”找到”并不存在的问题,把对的改错(114 道)。
  • 用标准答案停止看起来巨大地进步了——可是它只在答错的题上继续改,永远不会把对的改错(对 → 错 = 0)。这个数字衡量的是”在已知答错时再试一次”的价值,不是自我纠正的价值。
  • 外部反馈(一个只会检查算术的程序,把第一处算错的那一行指给模型):87.5%,平均只多 0.4 次调用。
  • 同样的调用次数给 self-consistency(5 次投票):83.2%,远好于 2 轮自我纠正(也是 5 次调用)。

Huang et al. 在 GPT-3.5-Turbo 上的数字:GSM8K 75.9% → 自我纠正 2 轮后 74.7%,CommonSenseQA 75.8% → 41.8%(多选题更容易被”改”);用标准答案停止时 GSM8K 是 84.3%。他们还比较了多智能体辩论和同样回答数的 self-consistency:GSM8K 上 9 个回答,辩论 83.0%,self-consistency 88.2%。

一个在写这一讲时踩到的坑:第一版实验里,第一次回答用的是贪心解码,“重想”用的是采样。SimReasoner 的贪心解码按设计走一条更容易出错的解题路径(第 4 讲为了演示 self-consistency 这样设计的),于是”重想”本身就更准,中性的纠正看起来有效(52% → 56%)。这正是一种常见的比较错误:纠正前后的回答必须来自同一个分布,否则你测到的是”换一种解码方式”的效果,而不是”纠正”的效果。

5.3 为什么:生成和验证一样难

演示 2 把”再想一遍、答案不一样就报警”当作一个错误检测器:

检测器召回(错的题里报警)误报(对的题里报警)精确率
自我审视93.4%37.2%64.0%
程序逐行检查算式79.5%1.7%97.1%

自我审视能发现大部分错误,但也在 37% 对的题上误报——报警里只有 64% 真的有错。报警之后它又分不清该信哪个答案,所以没用。程序检查几乎不误报,代价是只能发现算术错误,读错题、漏掉步骤它看不见(检查器只对算术负责)。

这引出一个判断自我纠正能否有效的简单标准:验证比生成容易多少?

  • 自己验证自己的推理:验证和生成一样难,用的是同一个模型、同样的知识、同样的误解。验证帮不了生成。
  • 检查算术、检查格式、跑单元测试(第 11 讲)、查一个事实(第 7、8 讲):验证比生成容易得多,而且来自模型之外。这时反馈才真的有用。

第 8 讲的 CRITIC(用工具验证,100%)和没有工具的自我批判(65%)就是这条分界线的两边。

5.4 公平地比较一个自我纠正方法

Huang et al. 的批评可以整理成三条检查清单:

  1. 有没有用到标准答案?哪怕只是用它决定”什么时候停”。
  2. 初始提示是不是故意写弱了?如果纠正的提示里包含了初始提示没有的信息(“别忘了每个概念都要用上”),提升可能只是因为第二个提示更好(§6.3)。
  3. 和同样调用次数的基线比了吗?5 次调用的自我纠正,应该和 5 次采样的 self-consistency 比,而不是和 1 次调用比。

6. Self-Refine:反馈的质量决定一切 · Self-Refine

6.1 方法

Madaan et al.(2023)的 Self-Refine 用同一个 LLM 扮演三个角色:生成初稿 → 写反馈 → 按反馈改写,循环到反馈说”没问题”为止。他们在 7 个任务上(对话回复、代码优化、代码可读性、数学推理、情感反转、缩写生成、约束生成)报告了平均约 20 个百分点的绝对提升。

和 §5 的”再想想”不同,Self-Refine 的任务大多是开放式的生成,“好”有很多个维度(更礼貌、更可读、用上全部给定的词),反馈可以很具体。他们的消融说明了具体反馈的重要:把具体反馈换成笼统的反馈或者不给反馈,分数都下降(例如情感反转任务 43.2 → 31.2 → 0)。

6.2 在约束生成上:反馈从哪里来

03_self_correction.py 演示 3:CommonGen-Hard 式的约束生成——一句话要用上 15 个给定的词,300 组。第一次生成每个词有 75% 的概率被用上;改写时被指出的遗漏以 90% 的概率补上,但会顺手弄丢已有的词(每个 3%)。

反馈来自第一次改 1 轮改 4 轮全部用上调用次数
程序(精确检查哪些词没用上)76.2%95.1%99.7%96.7%2.8
好的模型批评者(指出 80% 的遗漏,误报 2%)75.7%91.0%98.0%75.7%5.9
差的模型批评者(指出 40% 的遗漏,误报 5%)75.4%82.2%89.9%21.0%6.3
没有具体反馈(“Make it better.”)75.4%77.5%79.6%3.3%9.0
  • 反馈来自程序时,Self-Refine 几乎完美:约束生成正是”验证比生成容易得多”的任务——哪些词没用上,字符串匹配就能检查。
  • 好的批评者覆盖率也很高,但”全部用上”只有 76%:最后剩下的一两个词它常常指不出来。差的批评者更慢,而且一旦它说”没问题”,循环就停了。
  • 没有具体反馈时,改写加上的只比丢掉的多一点,调用次数却最多。

6.3 公平的对照:先把初始提示写好

表里没有的一行:如果初始提示本来就写清楚”全部用上”呢? 在模拟里,把它假设成”每个词被用上的概率从 0.75 提到 0.92”(这是一个旋钮,不是测出来的):一次调用就是 92.8% 的覆盖率,超过差的批评者改 4 轮(89.9%)。

这正是 Huang et al.(2024)对 Self-Refine 的一个质疑:他们把 CommonGen-Hard 的初始提示改成明确要求”包含全部概念”,一次调用就得到 81.8%,高于 Self-Refine 原文里自我纠正之后的 67.0%;在这个更好的初始回答上再做自我纠正,反而降到 75.1%。他们的建议是:设计初始提示和设计纠正提示要花同样的功夫。

带走的经验:Self-Refine 有效的前提是反馈本身可靠。可靠反馈的来源是能用程序检查的约束、能跑的测试、能查的事实;“让同一个模型再想一遍”通常不是。并且,评估一个纠正方法时,先确认初始提示已经尽力写好了。


7. 验证链 CoVe · Chain-of-Verification

7.1 方法

第 5 讲讲过,长回答里的幻觉很难检测。Dhuliawala et al.(2024)的 CoVe(Chain-of-Verification)把一个长回答拆成一条条可以单独核对的说法:

  1. 写草稿:正常地回答(例如 “Name some politicians who were born in Boston.”);
  2. 计划验证问题:为草稿里的每条说法写一个问题(“Where was X born?”);
  3. 回答验证问题;
  4. 写最终回答:只保留和验证答案一致的说法。

关键在第 3 步怎么执行,论文比较了三种:

  • 联合(joint):验证问题和答案接在草稿后面,一次生成写完;
  • 两步(two-step):先列问题;再在看不到草稿的一次调用里回答全部问题;
  • 分解(factored):每个问题单独一次调用。

7.2 列表题上

04_chain_of_verification.py:一个长尾的虚构世界,60 个国家、10 种语言,问”哪些国家说 L 语?“。模型对热门国家更了解;长回答很”松”,会把和 L 语沾点边的国家都联想进去。

做法精确率每题对的每题错的每题调用次数
只要草稿(基线)0.283.258.531.0
联合0.353.155.883.0
两步0.602.981.984.0
分解0.642.981.6814.8

两步和分解把错的从每题 8.5 个降到 2 个以下,也去掉了少量对的(短回答也会答错);联合执行几乎没用。Dhuliawala et al. 在 Wikidata 列表题上(Llama 65B)的数字:少样本基线精确率 0.17,CoVe 联合 0.29、两步 0.36、分解 0.32;每题对的从 0.59 降到 0.38,错的从 2.95 降到 0.68。方向一致;在他们的实验里两步比分解略好。

7.3 为什么有效:短回答比长回答准,而且不能看草稿

演示 2 回答两个问题。第一,草稿里的每个国家,单独问它说什么语言,模型答对多少?

草稿里的国家个数单独问答对接在草稿后面问答对
知道这个国家,草稿说错了20499.5%36.8%
不知道这个国家,草稿说错了1378.0%2.2%
知道这个国家,草稿说对了10899.1%100.0%
不知道这个国家,草稿说对了(碰巧猜对)2254.5%81.8%
全部47170.7%43.3%

草稿里只有 27.6% 的国家真说 L 语,可是单独问,模型答对 70.7%。同一个模型,长回答里的说法和单独问时的回答不一样——很多长回答里的错误,是模型其实知道答案的说法。论文报告的是同样的现象:基线列表里约 17% 正确,但单独问每个实体时约 70% 答对。CoVe 利用的正是这个差距。

第二,接在草稿后面问,答对率降到 43.3%:草稿就在上下文里,模型倾向于重复它(“知道这个国家、草稿说错了”的那一类,接在草稿后面问只有 36.8% 答对)。论文也观察到联合方式会重复草稿里的幻觉。这是第 2 讲(模型会照抄上下文里的模式)和第 6 讲(模型顺着已有的说法走)的又一个例子。CoVe 的要点不是”问问题”,而是问的时候不让模型看见草稿。

7.4 CoVe 去不掉的幻觉

演示 3 改变一个旋钮:模型不知道的国家里,有多大比例”每次都编同一个答案”(稳定的幻觉,第 5 讲):

稳定幻觉的比例草稿的精确率CoVe(分解)的精确率每题错的:草稿 → CoVe
0.000.270.898.62 → 0.33
0.350.270.768.60 → 0.88
0.700.280.648.53 → 1.68
1.000.270.578.68 → 2.33

模型对不知道的事每次都随机编时,单独问编出来的多半和草稿不一样,CoVe 能去掉它;每次都编同一个答案时,验证时还是同样的相信,CoVe 无能为力。这和第 5 讲 SelfCheckGPT 的局限一样:用模型自己检查自己,只能发现它说法不一致的错误;它一贯相信的错误,需要外部信息(检索、工具)才能发现。

所以 CoVe 主要修的是”长回答特有的松散”,而不是”模型不知道”。它的代价是调用次数:分解执行每条说法一次调用(这里每题 14.8 次),好在验证问题都很短,而且彼此独立、可以并行。


8. LLM-as-a-judge · Using LLMs to evaluate LLMs

8.1 为什么需要判官,判官怎样用

开放式的回答(“写一封邮件""解释一个概念”)没有唯一的标准答案,人工评估又慢又贵。Zheng et al.(2023)系统地研究了用强 LLM 当判官:在 MT-Bench(80 道多轮问题)和 Chatbot Arena 的人类投票上,GPT-4 判官和人类专家的一致率(不算平局)是 85%,人和人之间是 81%。从那以后,LLM 判官成了评估开放式生成的标准做法——也成了 §3 那些自动搜索的评估器。

三种常见用法:

  • 成对比较(pairwise):给两个回答,选一个更好的(或平局);
  • 单答案打分(single-answer grading):给一个回答打 1–10 分;
  • 参考答案引导(reference-guided):给出参考答案,让判官对照着判。

Zheng et al. 也系统地列出了判官的偏差。下面逐个测量,用的是 SimJudge:它给”A(放在前面)比 B 好”打分 s=acuity⋅(qA−qB)+len_bias⋅ln⁡(ℓA/ℓB)+self_bias⋅(自家A−自家B)+pos_bias+噪声s = \text{acuity}\cdot(q_A - q_B) + \text{len\_bias}\cdot\ln(\ell_A/\ell_B) + \text{self\_bias}\cdot(\text{自家}_A - \text{自家}_B) + \text{pos\_bias} + \text{噪声},ss 足够大选 A、足够小选 B,否则平局。“人”是一个只看质量 qq 的模拟标注者。

8.2 位置偏差

05_llm_as_judge.py 演示 1:600 对回答,交换顺序问两遍。

位置偏差两遍一致两遍都选放在前面的两遍都选放在后面的
0.075.7%5.7%6.5%
0.374.8%13.3%3.0%
0.857.5%33.0%0.3%
1.539.5%52.3%0.0%

即使完全没有位置偏差,也只有约四分之三的判决两遍一致——判官有噪声,两个回答质量接近时,换个顺序就可能换个结论。位置偏差越大,“谁在前面选谁”越多。Zheng et al. 用默认提示测的数字:GPT-4 两遍一致 65.0%(30.0% 偏向前面),GPT-3.5 46.2%,Claude-v1 23.8%(75.0% 偏向前面)。

修正:每一对都问两遍,两遍一致才算数,不一致记为平局(swap_aggregate)。代价是调用次数翻倍。

8.3 长度偏差

演示 2:把一个回答原样重复一遍(内容不变、长度翻倍),和原来的比。

长度偏差选了重复过的那个平局
0.023.0%61.3%
0.342.0%52.0%
0.6(本讲默认)56.3%43.3%
1.284.7%14.7%

Zheng et al. 的”重复列表”攻击(把回答里的列表换个说法再重复一遍):GPT-3.5 和 Claude-v1 选了重复版本的比例都是 91.3%,GPT-4 是 8.7%。强的判官可以不受这种攻击影响——但要测过才知道。

长度偏差不一定是”判官笨”:人也常常偏爱长一点、详细一点的回答,而判官的偏好正是从人的偏好数据里学来的(第 6 讲)。问题在于它奖励的是长度本身,而不是长度带来的信息。

8.4 排行榜:长度控制与评审团

演示 3:AlpacaEval 式的排行榜——6 个系统各和一个参照系统比 400 道题,判官属于家族 A。

系统家族平均质量典型长度人判官(问一遍)交换 + 长度控制评审团 B/C/D评审团 + 长度控制
A-chatA+0.3026051.5%60.5%61.0%54.5%54.4%
B-largeB+0.5518059.8%57.8%57.4%57.5%60.0%
C-conciseC+0.4511050.0%45.2%60.1%46.8%57.5%
C-verboseC+0.0052037.8%48.2%46.2%52.6%46.7%
D-longD+0.2070046.0%60.9%44.4%63.5%48.5%

和人的排名的斯皮尔曼相关:问一遍 0.37、交换顺序 0.49、交换 + 长度控制 0.66、评审团 0.43、评审团 + 长度控制 0.89(两位模拟的人类标注者之间 0.83)。只有 6 个系统,这些相关本身噪声很大,看方向即可。

  • 长度偏差扭曲了排名:D-long(质量中等、特别长)人给 46.0%,判官给 60.9%;C-concise(质量好、很简洁)被压低。
  • 长度控制(Dubois et al. 2024 的做法):对每个系统拟合一个逻辑回归 P(赢)=σ(θ+γ⋅tanh⁡(Δln⁡长度))P(\text{赢}) = \sigma(\theta + \gamma \cdot \tanh(\Delta \ln \text{长度})),然后报告”假如长度一样”的胜率 σ(θ)\sigma(\theta)。D-long 降到 44.4%,C-concise 升到 60.1%。他们报告,长度控制把 AlpacaEval 和 Chatbot Arena 排名的斯皮尔曼相关从 0.94 提到 0.98。注意它的假设:长度本身不带来质量。这个模拟里同一系统内长度和质量确实无关;真实数据里长一点的回答有时确实更好,长度控制会把这部分真实的好处也一起扣掉。
  • 自我偏好:和判官同一家族的 A-chat,判官给的胜率比人高约 9.5 个百分点。换成 B/C/D 三个家族的评审团(Verga et al. 2024 的 PoLL:用几个不同家族的较小模型投票,报告比单个大判官更接近人,还便宜七倍以上),A-chat 的虚高降到 3.0。评审团里每个判官仍偏爱自己的家族,只是偏爱互相抵消了一部分;评审团也修不了长度偏差(D-long 63.5%),要和长度控制一起用。

一个容易被忽略的细节:判官和人在非平局判决上的一致率(77.5%)甚至高于两位模拟的人类之间(72.1%),可排名照样被长度偏差扭曲。“和人的一致率高”说明判官像人,不说明它没有系统性的偏差。

8.5 数学题:判官看不出自己也会犯的错

演示 4:300 道算术题,判官对候选的回答说”对”还是”错”;统计错的回答被判成”对”的比例。

被判的回答判官不解题,读一遍就判先自己解一遍对照参考答案
family-Afamily-A(同一家族)77.9%11.5%0.0%
family-Afamily-C79.5%4.9%0.0%
family-Bfamily-A80.5%0.8%0.0%
family-Bfamily-C73.7%1.5%0.0%
  • 不解题直接判:读一遍推理很难看出读错题、漏掉步骤,失败率约 80%。Zheng et al. 的 20 道数学题上,GPT-4 用默认提示把 14 道错的判成对的(70%),先自己做一遍降到 6 道(30%),给参考答案降到 3 道(15%)。
  • 先自己解一遍:不同家族的判官失败率降到 0.8–4.9%,同一家族时是 11.5%。原因不是”喜欢自己”,而是它们共享同样的误解——判官自己解出来的也是那个错的答案,于是判”对”。这是自我偏好的一种机制:看不出自己也会犯的错。
  • 对照参考答案:失败率 0。能给参考答案时就给。

8.6 古德哈特:当判官成为优化目标

演示 5:让 B-large 把回答写成原来的 0.5、1、2、4 倍长(太长稀释内容、太短丢内容)。

回答长度人给的胜率判官(交换顺序)判官 + 长度控制
× 0.552.0%38.9%43.5%
× 161.2%55.4%56.7%
× 254.0%58.9%57.1%
× 448.2%63.0%53.1%

人最喜欢 × 1,判官最喜欢 × 4。如果把判官的分数当作优化目标——APE / OPRO 的评估器,或者 RLHF 的奖励(第 6 讲)——优化器会把回答越写越长。这就是古德哈特定律:指标一旦成为目标,就不再是好的指标。长度控制之后判官选 × 2,仍和人不一致(差别在噪声以内),但不再一路偏向最长的;而且长度控制的胜率是”假如长度和参照一样”的外推——× 2、× 4 的回答几乎都比参照长,这个估计落在数据之外,并不稳。

8.7 用判官之前的检查清单

  1. 在一份人工标注的小集合上测判官和人的一致率,尤其是在你关心的那些差别上(长短、风格、对错)。
  2. 成对比较时交换顺序问两遍。
  3. 报告时做长度控制,或者至少报告长度和胜率的关系。
  4. 判官和被评的系统不要同一家族;条件允许时用不同家族的评审团。
  5. 有标准答案的任务(数学、代码、事实)用参考答案或直接执行,不要让判官凭感觉。
  6. 把判官当优化目标时,定期用人的评估检查有没有被”刷分”。

9. 在真实模型上 · On a real model

06_real_llm.py(可选,需要 API key,约 220 次短调用,预算上限 1.00 美元)在真实的 Claude 上做两个小实验:

  • A. 指令归纳 + APE:两个任务(dedupe_sort、short_upper),每个任务换 4 组例子归纳出 4 条候选,12 道开发题挑第一名,第一名和人写的指令各做 24 道测试题。要看的是:候选有没有漏掉”例子里不一定看得出来”的操作;第一名在开发集和测试集上的分数差多少;人写的指令错在哪里(往往就是含糊的那一句——真实模型怎样理解 “Capitalize the words.”?)。
  • B. 判官:12 对(一个正确输出、一个某一步做错的输出),交换顺序各问一遍。这些题有唯一的正确答案,强的判官应该几乎总是一致地选对。

--toy 模式用 SimAutoPromptLLM 跑同一段代码,验证流程能跑通;它的数字由旋钮决定。每格只有十几道题,区间很宽:真实模型上的结果用来检验直觉,不要过度解读(编写讲义的环境里没有运行真实模型)。


10. 小结 · Summary

  1. 自动化放大的是评估器。指令归纳、APE、OPRO、自我纠正、Self-Refine、CoVe、LLM 判官都是”生成器 + 评估器”;评估器有噪声、和生成器同源、或者有偏差时,搜索会把缺陷放大。
  2. 指令归纳是假设搜索:例子少时规则不唯一(1 个例子 66% 函数上正确,8 个 97%);没暴露的操作会被漏掉或多写。挑覆盖边界情况的例子比多给例子更管用。规则对之后,说法也要对。
  3. APE / OPRO 有效:生成很多、在开发集上挑一个,把随手一条的 71% 提到 92%;OPRO 一步步地改,把一条一般的指令从 74% 改到 90% 以上。
  4. 但开发集分数会虚高:100 条候选、20 道开发题,第一名虚高 8.5 个百分点;OPRO 在 20 题上最后虚高 8.2。搜索会留下碰巧在开发题上有用的句子,也会错过真的有用的句子。用验证集做最后的决定,用测试集报告。
  5. 内在的自我纠正通常没用:中性的”再想想”改对和改错正好抵消,施压的提示更糟;“用标准答案决定何时停”是作弊;同样的调用次数给 self-consistency 更好。验证比生成容易得多、而且来自模型之外时(程序、测试、检索),反馈才有用。
  6. Self-Refine 的效果取决于反馈:程序反馈几乎完美,笼统的反馈几乎没用;比较时先把初始提示写好。
  7. CoVe:长回答里的很多错误,单独问时模型能答对(27.6% vs 70.7%);验证时不能让模型看见草稿。它去不掉稳定的幻觉。
  8. LLM 判官有位置、长度、自我偏好三种偏差:交换顺序、长度控制、不同家族的评审团、参考答案分别修正它们;判官和人”一致率高”不等于没有偏差;判官成为优化目标时会被刷分。

11. 常见误区 · Common pitfalls

误区实际情况
“APE 找到的提示在开发集上 98%,就报 98%”挑选会抬高开发集分数;要报从没参与挑选的测试集上的数字
“搜索留下的那句’Take a deep breath’一定有用”在小开发集上,没用的句子也常常’看起来有用’;用大样本配对比较确认,换模型重新测
“开发集越小越好,搜得快”开发集越小,虚高越大、挑错的概率越大;至少用一份验证集做最后的决定
“让模型检查自己的答案总没坏处”没有外部反馈时,改对和改错大致抵消,施压的提示还会把对的改错
“自我纠正把准确率从 58% 提到 90%”先问:是不是用标准答案决定了什么时候停?和同样调用次数的 self-consistency 比了吗?
“Self-Refine 有效,说明模型会自我改进”它有效的前提是反馈具体、可靠;笼统的反馈几乎没用,初始提示写好了收益也会缩小
“CoVe 能消除幻觉”它去掉的是长回答里’模型其实知道’的错误;模型一贯相信的错误,需要外部信息
“验证问题接在草稿后面问也行”看得见草稿时,模型倾向于重复草稿;要单独问
“GPT-4 和人的一致率 85%,所以判官可信”一致率高说明它像人,不说明没有位置、长度、自我偏好这些系统性偏差
“判官说长回答更好,那就写长一点”判官奖励长度本身;把它当优化目标会被刷分(古德哈特定律)

12. 练习 · Exercises

练习 1(计算) 你在 30 道开发题上比较 50 条候选提示,它们的真实准确率都约为 80%。(a)用”噪声单位 × E[max]“的估计,第一名的开发集分数大约虚高多少?(N = 50 时 E[max] ≈ 2.25。)(b)要把这个估计降到 2 个百分点以内,开发集要多大?(c)为什么实际的虚高通常比这个估计小?

参考答案:(a)噪声单位 0.8×0.2/30≈0.073\sqrt{0.8 \times 0.2 / 30} \approx 0.073,即 7.3 个百分点;第一名约虚高 2.25 × 7.3 ≈ 16 个百分点(独立、水平相同时的上限式估计)。(b)要 2.25×0.16/n≤0.022.25 \times \sqrt{0.16/n} \le 0.02,n≥0.16×(2.25/0.02)2≈2025n \ge 0.16 \times (2.25/0.02)^2 \approx 2025 道题。(c)候选彼此相关(共用句子、错在同样的题上),有效的”独立候选数”比 50 小;分数有天花板;候选的真实水平不完全相同时,第一名更可能是真正好的那个。演示 1 里 20 题、100 条候选只虚高 8.5,比独立估计(约 16.8)小一半。

练习 2(设计) 你要用 OPRO 为一个客服分类任务优化系统提示。标注数据一共 600 条。设计数据划分、搜索预算和最后的报告方式。

参考答案(一种可能):划分为搜索用的开发集 150 条、验证集 150 条、测试集 300 条(测试集在整个过程中只用一次)。搜索:每步 8 条候选、最多 40 步,用开发集打分;每 5 步把当前前 5 名在验证集上重新打分,验证集分数连续 3 次不上升就停止(提前停止)。最后:在验证集上挑第一名,在测试集上报告准确率和 Wilson 区间,并和起点提示做 McNemar 配对检验。额外:把最后的提示里每一句”和任务无关”的话去掉,在验证集上看分数是否下降(消融),只保留确实有用的句子。

练习 3(判断) 一篇论文说:“我们的自我纠正方法在 GSM8K 上把准确率从 76% 提到 88%。“列出你会检查的至少三件事。

参考答案:(1)纠正过程有没有用到标准答案(哪怕只是决定什么时候停);(2)纠正提示里有没有初始提示没有的信息(例如更详细的解题要求)——如果有,应该把它放进初始提示再比;(3)和同样调用次数的 self-consistency 比较了吗(纠正用了几次调用?);(4)初始回答和纠正后的回答是否用同样的解码方式(贪心 vs 采样);(5)改对和改错各有多少道(只看净提升会掩盖大量的来回翻转);(6)置信区间和配对检验。

练习 4(解释) §7.3 里,“知道这个国家、草稿说错了”的那类国家,单独问时 99.5% 答对,接在草稿后面问只有 36.8%。(a)为什么同一个模型会在长回答里把它知道的事说错?(b)为什么接在草稿后面问会降低准确率?(c)你能想到一种让”联合执行”变好的提示改法吗?它的局限是什么?

参考答案:(a)列表式的长回答是在”生成一串和 L 语有关的国家”,模型按联想和流畅性往下写,不是在逐个核对事实;联想到的国家未必真的说 L 语。(b)草稿就在上下文里,回答”X 国说什么语言”时,“L 语”这个词刚刚和 X 一起出现过,模型倾向于照抄(第 2 讲的归纳头、第 6 讲的顺着已有说法走)。(c)例如在验证部分的指令里写”Answer each question from your own knowledge, ignoring the draft above”,或者把验证问题改成不提 L 语的开放式问题。局限:只要草稿还在上下文里,模型就仍然能看见它,指令只能减弱、不能消除这种影响;最可靠的办法还是两步或分解执行。

练习 5(动手) 修改 autoprompt_lab.SimJudge 的默认值,让它更像 Zheng et al. 报告的 GPT-4(位置偏差小、重复列表攻击的失败率低)。重新运行 05 号脚本:排行榜和人的排名相关会怎样变?长度控制还有多大作用?

参考答案:例如把 pos_bias 和 len_bias 都降到 0.1(重复攻击的失败率会接近只由噪声决定的水平)。编写讲义时这样跑的结果:D-long 只被抬高约 3 个百分点(人 46.0%、判官 49.2%),“问一遍”和人的排名相关从 0.37 升到 0.89;长度控制反而把相关降到 0.77——偏差很小时,长度控制只是多估了一个参数、多引入了一份估计噪声。自我偏好没有变:A-chat 仍被抬高约 9 个百分点,换成评审团降到约 3。这说明修正方法是针对具体偏差的:先测出你的判官有哪种偏差、有多大,再决定用哪种修正;“每种修正都用上”并不总是更好。

练习 6(批判) 有人说:“我们用 GPT 类模型当判官,用 OPRO 优化我们的客服回答提示,判官胜率从 50% 提到了 78%。“指出至少两个可能的问题,并提出检查方法。

参考答案:(1)古德哈特:优化器可能学会了迎合判官的偏差(写得更长、更像判官家族的风格),而不是真的更好——检查回答长度随优化的变化,做长度控制的胜率;(2)同一家族:如果被优化的模型和判官同一家族,自我偏好会抬高胜率——换一个不同家族的判官或评审团重新评;(3)过拟合:78% 是不是开发集上的分数?要在没参与优化的测试问题上报告;(4)位置偏差:评估时是否交换了顺序;(5)最终检查:抽一部分让人盲评,看人的偏好是否同向变化。


13. 延伸阅读 · Further reading

指令归纳与自动提示

  • Honovich et al. (2023). Instruction Induction: From Few Examples to Natural Language Task Descriptions. ACL.
  • Zhou et al. (2023). Large Language Models Are Human-Level Prompt Engineers(APE). ICLR.
  • Yang et al. (2024). Large Language Models as Optimizers(OPRO). ICLR.
  • Pryzant et al. (2023). Automatic Prompt Optimization with “Gradient Descent” and Beam Search(用自然语言的”梯度”改提示)。
  • Fernando et al. (2023). Promptbreeder: Self-Referential Self-Improvement via Prompt Evolution;Guo et al. (2024). Connecting Large Language Models with Evolutionary Algorithms Yields Powerful Prompt Optimizers(EvoPrompt)。
  • Khattab et al. (2024). DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines(把多阶段提示当作可优化的程序)。

自我纠正与自我改进

  • Huang et al. (2024). Large Language Models Cannot Self-Correct Reasoning Yet. ICLR.
  • Madaan et al. (2023). Self-Refine: Iterative Refinement with Self-Feedback. NeurIPS.
  • Shinn et al. (2023). Reflexion: Language Agents with Verbal Reinforcement Learning(第 14 讲)。
  • Gou et al. (2024). CRITIC: Large Language Models Can Self-Correct with Tool-Interactive Critiquing(第 8 讲)。
  • Pan et al. (2024). Automatically Correcting Large Language Models: Surveying the Landscape of Diverse Automated Correction Strategies(综述)。

验证

  • Dhuliawala et al. (2024). Chain-of-Verification Reduces Hallucination in Large Language Models. Findings of ACL.
  • Manakul et al. (2023). SelfCheckGPT(第 5 讲)。

LLM-as-a-judge

  • Zheng et al. (2023). Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena. NeurIPS Datasets and Benchmarks.
  • Dubois et al. (2024). Length-Controlled AlpacaEval: A Simple Way to Debias Automatic Evaluators.
  • Verga et al. (2024). Replacing Judges with Juries: Evaluating LLM Generations with a Panel of Diverse Models(PoLL)。
  • Wang et al. (2023). Large Language Models are not Fair Evaluators(位置偏差与校正)。
  • Panickssery et al. (2024). LLM Evaluators Recognize and Favor Their Own Generations(自我偏好)。

14. 下一讲预告 · Next lecture

这一讲反复出现一个结论:自我纠正要有可靠的外部反馈才有用。有一个领域天然就有这样的反馈——代码:写出来的程序可以运行,可以跑测试,报错信息会精确地指出哪里错了。第 11 讲进入原课第 7 周:代码生成。

  • Codex 与 HumanEval:怎样用执行而不是字面相似度来评估生成的代码;pass@k 的无偏估计,以及为什么直接用 1−(1−p^)k1-(1-\hat p)^k 会估错;
  • 为什么 BLEU 不适合代码:一个差一个字符的程序可能完全错,两个写法完全不同的程序可能都对;
  • 沙箱:怎样安全地运行模型写的代码(进程、超时、资源限制——给没有系统背景的读者从头讲);
  • 自我调试(Chen et al. 2024):把测试结果和报错信息交给模型,让它改——这一讲的”外部反馈”在代码上的样子;
  • 从代码模型到软件工程智能体:SWE-bench、SWE-agent 与”智能体–计算机接口”。
← 返回课程目录