测试时搜索 vs 采样扩展:Pass@k 与 Majority Voting 的效率边界

在大模型深度推理时代的测试时计算扩展(Test-Time Compute Scaling)体系中,如何将给定的计算预算(FLOPs)转化为最高的推理胜率,存在两大基本扩展流派:
- 采样扩展(Sampling Scaling):保持单向生成不变,通过并行采样多个候选轨迹,利用 \\text{Pass}@k(潜在上限)或多数投票(Majority Voting / Self-Consistency)聚合答案;
- 搜索扩展(Search Scaling):沿着推导路径引入过程奖励模型(PRM)或验证器,在每个逻辑分叉点进行剪枝、回溯与前向探索(如 Beam Search, MCTS)。
在算力有限的实际业务与科研探索中,这两种扩展方式在不同难度任务上的"算力投资回报率(Compute ROI)"呈现出截然不同的效率边界。
一、两类扩展范式的算力消耗与收益模型
[采样扩展 vs 搜索扩展的计算图拓扑]
1. 采样扩展 (Parallel Sampling @ N):
- 拓扑结构: N 条相互完全独立的单向一维生成线。
- 算力特性: O(N * L) FLOPs,高度并行,无 GPU 节点间通信与回溯调度开销。
- 收益瓶颈: 纯粹依赖概率碰运气。若模型在某一步存在系统性盲区,增加采样数收益迅速衰减。
2. 搜索扩展 (Tree Search with Verifier):
- 拓扑结构: 树状有向无环图 (DAG),包含节点展开、评分、剪枝与回溯。
- 算力特性: O(Nodes * L_step) FLOPs,串行依赖强,需要频繁调用 PRM 评估。
- 收益核心: 因果剪枝机制能够瞬间扼杀错误分支,将算力集中投向高潜力路径。
二、Pass@k 的对数收益衰减与 Majority Voting 的极限
对于难度较低或中等的题目(如 GSM8K):
- 模型的单路通过率已达到 70%;
- 此时采用多数投票(Majority Voting @ 16),胜率可迅速逼近 90%+;
- 原因:正确解题路径在模型的后验概率中占据主模态(Major Mode),多次采样能够极快地通过大数定律滤除偶然的单步笔误。
然而,对于 AIME(美国数学邀请赛)等竞赛级难题:
- 模型的单路通过率往往低于 10%;
- 错误答案在离散空间中高度分散且具有欺骗性,多数投票的胜率提升极其微弱;
- 若采用 \\text{Pass}@k 评估,虽然 k=64 时的理论覆盖率达到 40%,但这需要一个近乎完美的外部裁判(Oracle Verifier) 才能将正确答案精准挑出,在真实场景中并不具备独立落地可行性。
三、搜索扩展在极难任务上的断层优势
[极难任务 (AIME) 上的算力转化效率对比]
投入算力预算: 等价于 64 次完整前向生成 (64x Compute Budget)
方案 A: 纯采样多数投票 (Sampling @ 64) ────────> AIME 准确率: 18.5%
方案 B: PRM 引导的树搜索 (PRM-Tree Search) ────> AIME 准确率: 46.2% (高出 2.5 倍!)
* 核心机理: 搜索算法在第 2 步和第 4 步提前剪除了 85% 的错误分支,
将宝贵的 64x 算力全部投入到了真正有效的 3 条深度推导主干上!
四、Python 模拟实战:两种扩展范式在不同难度下的 ROI 效率曲线
以下代码模拟了在简单任务与极难任务中,随着计算预算(FLOPs 乘数)的增加,采样投票与树搜索的准确率收敛轨迹。
python
import numpy as np
def simulate_compute_roi(
task_difficulty: str, # "EASY" or "HARD"
compute_budgets = [1, 2, 4, 8, 16, 32, 64]
):
results = {"sampling_acc": [], "search_acc": []}
if task_difficulty == "EASY":
# 简单任务: 单路基准 0.70
base_p = 0.70
for b in compute_budgets:
# 采样多数投票: 极速逼近 1.0
acc_samp = 1.0 - (1.0 - base_p) * (0.6 ** np.log2(b))
# 搜索扩展: 收益同样高,但由于调度开销略平缓
acc_search = 1.0 - (1.0 - base_p) * (0.65 ** np.log2(b))
results["sampling_acc"].append(float(np.clip(acc_samp, 0, 0.99)))
results["search_acc"].append(float(np.clip(acc_search, 0, 0.99)))
elif task_difficulty == "HARD":
# 极难任务: 单路基准仅 0.08
base_p = 0.08
for b in compute_budgets:
# 采样多数投票: 提升缓慢,边际收益极快见顶
acc_samp = base_p + 0.15 * (1.0 - np.exp(-np.log2(b) / 2.5))
# 搜索扩展: 突破阈值后剪枝爆发,呈强劲对数增长
acc_search = base_p + 0.45 * (1.0 - np.exp(-np.log2(b) / 1.8))
results["sampling_acc"].append(float(np.clip(acc_samp, 0, 0.95)))
results["search_acc"].append(float(np.clip(acc_search, 0, 0.95)))
return results
if __name__ == "__main__":
budgets = [1, 2, 4, 8, 16, 32, 64]
easy_res = simulate_compute_roi("EASY", budgets)
hard_res = simulate_compute_roi("HARD", budgets)
print("================ 测试时计算扩展效率边界矩阵 ================")
print("【简单任务 (GSM8K 级别)】")
print(f"算力乘数: {budgets}")
print(f"采样投票准确率: {[round(x*100, 1) for x in easy_res['sampling_acc']]}")
print(f"树搜索准确率: {[round(x*100, 1) for x in easy_res['search_acc']]}")
print("\n【极难任务 (AIME 竞赛级别)】")
print(f"算力乘数: {budgets}")
print(f"采样投票准确率: {[round(x*100, 1) for x in hard_res['sampling_acc']]}")
print(f"树搜索准确率: {[round(x*100, 1) for x in hard_res['search_acc']]} ──> 🚀 压倒性优势!")
print("==========================================================")
五、工业界混合扩展决策法则
- 低难度高吞吐业务(客服、常规代码补全、简短问答) :
- 绝对首选单次贪心解码或轻量 Majority Voting @ 3。开发和调度成本极低,GPU 利用率满载,性价比最高;
- 高价值复杂推理任务(自动定理证明、算法竞赛解题、长架构代码设计) :
- 果断启用 PRM 驱动的轻量 MCTS / Beam Search 树搜索。利用过程剪枝突破概率天花板,以确定性的状态探索换取最高的准确率上限。