自洽性提示的意思是,把同一个推理问题问模型好几次,然后保留出现次数最多的答案。每次运行都会写出自己的推理过程,就像思维链提示那样;又因为模型在选词时带有一定的随机性,每次运行走的路径都不同。正确的路径往往会得出同一个答案,而错误则往往分散在各种不同的错误答案上。多数投票利用的正是这种差别。
这项技巧出自 Wang 等人 2022 年的论文《Self-Consistency Improves Chain of Thought Reasoning in Language Models》。论文比较了“采样多条推理路径再投票”和“只取一个贪心解码的答案”,发现投票提高了算术和常识推理基准测试上的准确率。
下面展示了同一个提示词的三次运行。每个标签页是一次运行及其回复。其中两次数对了,另一次犯了一个很容易犯的错误。
按数位来数 7。
- 个位: 7、17、27、37、47、57、67、77、87、97。共 10 个 7。
- 十位: 70 到 79。又是 10 个 7。
- 百位: 只有 100 是三位数,而它不含 7。
77 在两组里都出现了,这是对的,因为它包含两个 7。
Answer: 20
第 2 次运行回答的其实是一个略有不同的问题:有多少个数含有 7,而不是数字 7 出现了多少次。它的推理读起来和另外两次一样有把握,单看它,你没有任何理由怀疑。三次运行的投票结果是 20、19、20,多数答案正是正确答案。
自洽性提示的原理
- 写一个带固定答案行的思维链提示词。 要求先写推理、最后给最终答案,并且答案采用一种你能找到的形式,比如“Answer: N”。没有固定的答案行,你就得读完每次运行的内容才能找到答案。
- 开启随机性,采样多次运行。 每次运行都需要有走不同路径的自由。在 API 中,把温度设为大于 0 就能做到。温度为 0 时,大多数运行会重复同样的推理,而把同一个错误重复五次并不能算作证据。
- 从每次运行中提取最终答案。 这一步忽略推理过程。两次运行通过不同的路线都得出 20,就算两票投给 20。
- 取最常见的答案。 计数之前先统一格式,让“20”“20.”和“二十”算作同一个答案。
答案的一致程度本身就是有用的信息。如果五次运行全部一致,随机失误的可能性就很小。如果投票分成三派,说明这个问题对模型来说很难,这时就该自己检查答案,或者改写提示词。论文还报告说,样本之间一致程度越高的问题,答对的可能性也越大。
手动使用自洽性提示
任何聊天应用都能用这个技巧。发送提示词,记下最后一行,然后再得到一次独立的运行:点击重新生成,或者把同一个提示词粘贴到新的对话里。重复这个过程,直到你得到三个或五个答案。
不要在同一个对话里用“你确定吗?”或“再试一次”来重新提问。模型能看到它上一次的回复,所以新的答案并不独立:它要么倾向于重复第一个答案,要么只因为你似乎有所怀疑就改掉答案。这两种都不算投票。
下面这个提示词已经为投票做好了准备。填入你自己的问题和答案格式,然后运行几次。
12 支笔是 12 ÷ 3 = 4 包。
4 包的价格是 4 × 2 元 = 8 元。
从 10 元中找零:10 元减 8 元 = 2 元。
Answer: 2 元
正是格式这一部分让投票成为可能。把它关掉,每次运行给出答案的措辞往往各不相同,而且常常夹在正文中间,一次简单的计数就变成了仔细的阅读。
用代码实现自洽性提示
在代码中,这个循环很短:把同一个提示词运行 N 次,取出答案行,然后计数。这个版本使用 OpenAI Python SDK;任何能设置温度的 API 都可以用同样的方式实现。
import re
from collections import Counter
from openai import OpenAI
client = OpenAI()
MODEL = "your-model-id" # e.g. from your provider's model list
PROMPT = (
"How many times does the digit 7 appear when you write out all the whole "
"numbers from 1 to 100? Think it through step by step, then give the final "
'answer on the last line in the form "Answer: N".'
)
def final_answer(text):
# Also matches "**Answer: 20**", since chat models often bold the last line.
lines = re.findall(r"^\**Answer:\**\s*(.+)$", text, re.MULTILINE)
return lines[-1].strip(" *.") if lines else None
answers = []
for _ in range(5):
response = client.chat.completions.create(
model=MODEL,
temperature=0.8,
messages=[{"role": "user", "content": PROMPT}],
)
answers.append(final_answer(response.choices[0].message.content))
votes = Counter(a for a in answers if a is not None)
if votes:
best, count = votes.most_common(1)[0]
print(f"{best} ({count} of {len(answers)} runs agree)")
else:
print("No run gave an answer line.")
这些运行彼此独立,所以在生产环境中,你会并行发送它们,而不是一个接一个地发。有些推理模型只接受默认温度,如果你设置了温度就会返回错误;对于这类模型,不要传 temperature。它们的运行结果仍然会有差异,因为默认设置本身就会采样。
什么时候用,以及代价是什么
当以下三点都成立时,自洽性就值得一用:答案简短且可比较(一个数字、一个标签、一个选项),答错的代价高于多调用几次的成本,并且模型在这个任务上还不够可靠。数学应用题、边界情况棘手的分类任务以及选择题都很合适。
它消耗的 token 大约是单个答案的 N 倍,而且如果模型每次运行都抱有同样的错误认识,它就帮不上忙。如果五次运行都犯了同一个错误,投票结果就会全票一致,而且是错的。投票减少的是随机失误,而不是系统性错误,所以它不能代替时不时地拿已知结果来核对答案。
会在回答之前先在内部推理的模型,每次运行本来就会详细地把问题想一遍,这往往会缩小投票带来的收益。但在它们的运行结果不一致的难题上,投票仍然可能有回报。
自洽性只对完成的答案投票。思维树提示更进一步,在解题过程中就比较不完整的推理,保留有希望的分支,并在弱的分支得出答案之前就把它们舍弃。
常见问题
自洽性提示是什么?
自洽性提示是 Wang 等人(2022)提出的一种技巧。你在开启采样的情况下把同一个思维链提示词发送好几次,让每次运行沿着不同的路径推理,然后取出现次数最多的最终答案。它用多条推理链之间的投票,取代了对单独一条推理链的信任。
自洽性提示应该采样多少次?
日常使用中,运行三到五次就足以在投票中压过偶尔的失误,而奇数次可以避免两个答案打成平手。原论文对每个问题采样了多得多的路径,发现样本越多,准确率越高,但每次的提升越来越小。答错代价高时就多运行几次,在乎成本或速度时就少运行几次。
自洽性和思维链有什么区别?
思维链是一次运行,模型在给出答案之前先写出推理过程。自洽性建立在思维链之上:它把思维链运行好几次,再对最终答案投票。思维链改变的是提示词;自洽性改变的是你收集多少个答案,以及怎样从中选出一个。
自洽性适用于文章或开放式回答吗?
不能直接用,因为投票需要能比较是否相同的答案:一个数字、一个标签、一个选择题选项或一个简短的事实。对于开放式文本,常见的变通办法是生成几个版本,再让模型判断哪个与其他版本最一致,但这是一种判断,而不是计数。
可以在 ChatGPT 或 Claude 里使用自洽性提示吗?
可以,手动操作即可。在几个新对话中分别发送这个提示词,或者把回复重新生成几次,记下每次运行的最终答案。要用新对话或重新生成按钮,而不是在同一个对话里再问一遍,因为能看到自己上一个答案的模型往往会重复它。