概述
链式推理(Chain-of-Thought, CoT)是一种提示工程技术,通过让语言模型显式生成推理步骤来提升复杂推理任务的表现。CoT的核心洞察是:展示推理过程比直接给出答案更能激发模型的推理能力。1
核心思想:模型”思考”的方式——展示中间步骤——决定了最终答案的质量。
背景与发现
CoT的发现历程
2022年,Wei等人发现当语言模型被要求生成中间推理步骤时,在复杂推理任务上表现出显著的能力提升:
标准提示:
输入: Roger有5个网球。他又买了2罐网球,每罐3个。
Roger现在有多少个网球?
输出: 11个
CoT提示:
输入: Roger有5个网球。他又买了2罐网球,每罐3个。
Roger现在有多少个网球?
让我们逐步思考:
1. Roger一开始有5个网球
2. 每罐有3个网球,买了2罐
3. 新买的网球数 = 2 × 3 = 6
4. 总网球数 = 5 + 6 = 11
输出: Roger现在有11个网球。
关键发现
| 发现 | 描述 |
|---|---|
| 规模效应 | CoT主要在足够大的模型(≥100B参数)上有效 |
| 任务类型 | 对数学、代码、逻辑等任务效果显著 |
| 格式灵活 | 推理步骤可以是自然语言、数学推导或代码 |
| 与采样互补 | CoT + 自我一致性可以进一步提升 |
理论解释
1. 外部化工作记忆
CoT有效的一个解释是:它将模型的”工作记忆”外部化到语言输出中。
# 没有CoT时,模型需要在"内部"维护中间状态
def solve_internal(model, problem):
# 所有计算都在隐状态中进行
return model.generate(problem)
# 有CoT时,中间状态被"外部化"到文本
def solve_with_cot(model, problem):
# 中间计算结果被明确写出
reasoning = """
让我分析这个问题:
1. 首先理解问题...
2. 然后...
"""
return model.generate(problem + reasoning)2. 程序化推理
另一种解释将CoT视为一种”程序合成”:推理步骤定义了一个计算过程:
# CoT本质上是在生成"程序"
reasoning = """
设 x = 初始值
对每个操作:
应用操作到 x
返回 x
"""
# 模型"执行"这个程序来得到答案3. 隐式计算假说
研究表明,CoT可能在模型内部触发了更深的计算:
CoT不只是在输出中写步骤——它可能引导模型在内部进行更深的推理。
CoT变体
1. Few-Shot CoT
通过示例展示推理过程:
few_shot_prompt = """
示例1:
问题:小明有10个苹果,给了小红3个,又买了5个。小明有多少苹果?
推理:10 - 3 = 7,7 + 5 = 12
答案:12
示例2:
问题:计算 15 × 17
推理:15 × 17 = 15 × (20 - 3) = 300 - 45 = 255
答案:255
当前问题:
问题:{question}
推理:
"""2. Zero-Shot CoT
通过简单的触发词激活推理能力:
def zero_shot_cot(model, question):
# 触发词:"让我们逐步思考"
prompt = question + "\n让我们逐步思考。"
reasoning = model.generate(prompt)
# 触发答案提取
answer_prompt = reasoning + "\n所以答案是多少?"
answer = model.generate(answer_prompt)
return reasoning, answer
# 简化为:
prompt = question + "让我们逐步思考。"
response = model.generate(prompt)
# 答案包含在response中3. 思维骨架(Skeleton-of-Thought)
针对长输出的优化,先规划骨架再展开:
def skeleton_of_thought(model, task, max_points=5):
"""
1. 生成思维骨架
2. 对每个骨架点进行扩展
"""
# Step 1: 生成骨架
skeleton_prompt = f"""
任务:{task}
请列出完成这个任务的关键步骤(不超过{max_points}个):
1. ...
2. ...
...
"""
skeleton = model.generate(skeleton_prompt)
# Step 2: 逐点扩展
expanded_sections = []
for point in skeleton.split('\n'):
if point.strip():
expansion = model.generate(f"详细说明:{point}")
expanded_sections.append(expansion)
return combine(expanded_sections)4. 长思维链(Long CoT)
o1和R1等推理模型使用超长的内部推理链:
# 长CoT可能包含数百到数千个token的推理过程
long_cot_example = """
让我仔细分析这个问题...
首先,我需要理解问题的核心...
根据已知条件,我可以推断...
更仔细地考虑这个子问题...
实际上,我之前的分析有一个问题...
重新审视...
从另一个角度考虑...
综合以上分析...
最后,我得出结论...
"""自我一致性(Self-Consistency)
核心思想
自我一致性通过采样多个推理路径来提升CoT的可靠性:
import torch
from collections import Counter
def self_consistency(model, question, n_samples=40, temperature=0.7):
"""
自我一致性:
1. 用CoT采样N个不同的推理路径
2. 通过多数投票选择最终答案
"""
answers = []
for _ in range(n_samples):
# 使用非零温度采样,生成不同的推理路径
prompt = question + "\n让我们逐步思考。"
response = model.generate(prompt, temperature=temperature)
# 提取答案
answer = extract_final_answer(response)
answers.append(answer)
# 多数投票
answer_counts = Counter(answers)
return answer_counts.most_common(1)[0][0], answer_counts为什么有效?
问题:如果x + y = 10,x = 4,求y。
采样路径1:10 - 4 = 6,答案是6 ✓
采样路径2:10 - 4 = 6,答案是6 ✓
采样路径3:4 = 10 - y,y = 6 ✓
采样路径4:10 × 4 = 40,答案是40 ✗ ← 错误路径
采样路径5:4 + 6 = 10,答案是6 ✓
投票结果:6 (4票) > 40 (1票)
最终答案:6
实验结果
| 模型 | GSM8K | SVAMP | StrategyQA |
|---|---|---|---|
| GPT-3 (CoT) | 46.9% | 52.2% | 53.8% |
| GPT-3 (Self-Consistency) | 74.4% | 76.4% | 57.5% |
| 提升 | +27.5% | +24.2% | +3.7% |
树状思考(Tree of Thoughts)
超越线性链
当问题有多个分支决策时,线性CoT可能陷入局部最优。树状思考(ToT)维护多条推理路径:
from dataclasses import dataclass
from typing import List, Optional
import heapq
@dataclass
class ThoughtNode:
text: str
value: float
parent: Optional['ThoughtNode'] = None
depth: int = 0
class TreeOfThoughts:
def __init__(self, model, num_branches=5, max_depth=10):
self.model = model
self.num_branches = num_branches
self.max_depth = max_depth
def expand_node(self, node: ThoughtNode) -> List[ThoughtNode]:
"""扩展一个思考节点"""
prompt = f"基于以下思考,继续推理:\n{node.text}\n可能的下一个步骤是:"
suggestions = self.model.generate(prompt, n=self.num_branches)
children = []
for suggestion in suggestions:
value = self.evaluate(suggestion) # 评估节点价值
children.append(ThoughtNode(
text=suggestion,
value=value,
parent=node,
depth=node.depth + 1
))
return children
def search(self, initial_thought: str) -> str:
"""使用束搜索进行树搜索"""
# 初始化
frontier = [ThoughtNode(text=initial_thought, value=0.0)]
while frontier and frontier[0].depth < self.max_depth:
# 扩展所有节点
new_frontier = []
for node in frontier:
children = self.expand_node(node)
new_frontier.extend(children)
# 剪枝:保留top-k
new_frontier.sort(key=lambda x: x.value, reverse=True)
frontier = new_frontier[:self.num_branches]
# 返回最佳路径
best = max(frontier, key=lambda x: x.value)
return self.reconstruct_path(best)
def evaluate(self, thought: str) -> float:
"""评估思考节点的价值"""
# 可以使用价值模型或启发式评估
return self.model.evaluate(thought)ToT vs CoT vs BFS
| 特性 | CoT | ToT | 最佳搜索 |
|---|---|---|---|
| 路径数 | 1 | 多 | 取决于问题 |
| 回溯能力 | 无 | 有 | 有 |
| 适用场景 | 线性推理 | 多分支决策 | 复杂规划 |
| 计算成本 | 低 | 中-高 | 高 |
CoT的理论基础
1. 计算等价性
研究表明,经过适当prompt的LLM可以模拟任意多项式时间图灵机:
定理(CoT的计算理论):对于任意多项式时间算法 ,存在一个CoT提示,使得LLM模拟 的行为。
2. 形式语言与正则语言
CoT表达能力与形式语言理论有关:
| 推理任务 | 计算复杂度 | LLM能力 |
|---|---|---|
| 命题逻辑 | P | ✓ |
| 一阶逻辑 | PSPACE | ✓ (有限上下文) |
| 算术推理 | PTIME | ✓ |
| 图灵机模拟 | EXPTIME | 有限 |
3. 思维语言假说
一些研究者提出LLM内部有一个”思维语言”:
自然语言(输出) ←→ 思维语言(内部表示) ←→ 概念空间
CoT 隐式推理 知识
实践指南
CoT设计原则
- 分解问题:将复杂问题分解为可管理的步骤
- 明确中间目标:每个步骤应有清晰的子目标
- 保持一致性:推理风格和格式保持一致
- 检查点:在关键决策点添加验证
# 好的CoT设计示例
def good_cot_design(question):
return f"""
问题:{question}
让我系统地分析这个问题:
【第一步:理解问题】
- 问题的核心是什么?
- 已知条件有哪些?
- 需要求解什么?
【第二步:制定计划】
- 我可以使用什么方法?
- 需要哪些公式或定理?
- 有哪些约束条件?
【第三步:执行计算】
[详细写出每一步计算]
【第四步:验证结果】
- 检查计算是否正确
- 结果是否符合常识
- 有没有遗漏的情况?
答案:
"""何时使用CoT
| 场景 | 推荐CoT变体 |
|---|---|
| 数学计算 | Few-Shot CoT + 精确示例 |
| 代码生成 | Zero-Shot CoT + “写代码” |
| 逻辑推理 | ToT + 验证器 |
| 开放式问题 | Self-Consistency |
| 实时响应 | Zero-Shot CoT |
局限性与挑战
1. 推理错误传播
早期步骤的错误会传播到后续步骤:
# 错误示例
"让我计算 23 × 47:
1. 23 × 47 = 20 × 40 + 3 × 7 ← 错误分解
2. = 800 + 21 = 821 ← 错误答案
"2. 虚假推理
模型可能生成看似合理但逻辑错误的推理:
“因为所有乌鸦都是鸟,所有黑的东西都是乌鸦…”(组合谬误)
3. 计算成本
CoT显著增加了输出长度和推理时间:
# 成本对比
no_cot_output_tokens = 50
cot_output_tokens = 500 # 10x增加参考
相关主题
- 测试时计算扩展:测试时计算的完整框架
- 推理模型架构:专门优化的推理模型
- 过程奖励模型:验证推理步骤
- MCTS与LLM推理:搜索增强的推理
Footnotes
-
Wei et al. “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models”. NeurIPS, 2022. ↩