不训练模型也能变强:测试时计算与并行采样实战指南 在 LLM 应用开发者的日常里经常会遇到一个矛盾模型已经训练好了但面对复杂推理问题时同一个问题模型换个说法就可能答错。过去我们习惯把这归结为“模型能力不够”解法是换更大模型、更多数据、更长的训练。但最近一两年AI 圈子里出现了一个很多开发者还没完全跟上的转折——可以不训练模型只靠推理阶段多花计算就能把回答质量明显拉高。这个方向在斯坦福 CS329A《自我改进 AI 智能体》课程里被放进了第二讲测试时计算Test-Time Computing。它和并行采样、验证机制关系密切。它的核心观点很反直觉模型不是只能靠训练变强在回答问题时“多想一会儿”“多试几个方案”“让验证器检查一遍”同样能带来显著收益。这篇文章会讲清楚四件事测试时计算到底解决了什么问题它的技术边界在哪里。并行采样和验证是怎么配合工作的各自的关键参数和作用是什么。测试时计算的“扩展规律”是什么为什么不是无脑多采样就完事。如果你想在自己的代码生成、推理问答或 Agent 项目里用起来应该从哪里入手常见坑在哪里。如果你正在做 RAG、Agent、代码生成或者复杂数学推理类的应用这篇文章值得读完最好收藏备用。1. 为什么要重新理解“测试时计算”先回到一个基础问题过去几年我们怎么让 AI 变强答案是训练。把模型参数变大塞进更多高质量语料用更长的训练时间把模型能力压榨出来。这条路线确实有效但代价也越来越高一次大模型训练的成本是数百万美元级数据也遇到了增长瓶颈。更麻烦的是模型训练完就固定了部署之后只能做前向推理遇到没见过的难题只能硬答。这就出现了一个很尴尬的场景你在生产环境里发现模型对某类问题回答不稳定但你能做的事情非常有限。换模型不是每次都有更优解继续训练成本太高收集标注数据又要等。测试时计算提供了第三种选择既然模型已经拥有了知识我们能不能在它“开口回答”之前多给它一点计算资源让它自己检查、多生成几个候选、甚至自己修正自己从已经公开的课程信息和行业实践来看CS329A 第二讲正是把这个问题放在“自我改进 AI 智能体”的大框架下讲的。这里的逻辑很清晰一个智能体如果只能根据输入生成一次输出它的上限就是模型本身但如果它能利用额外的测试时计算生成多个候选、用验证器筛选、必要时再重来一轮它就具备了一种“部署后自我改进”的能力。需要注意一个边界测试时计算不是替代训练更不是让一个完全没有相关知识的模型“硬想”出答案。它的前提是模型已经具备基础能力缺的是在关键任务上的稳定性、覆盖率和搜索空间。你可以把训练理解成“打基础”把测试时计算理解成“临场发挥”。如果只看表面很容易把测试时计算等同于“多调用几次 API”。真正的关键点有三个如何采样出多样且高质量的候选如何在没有标准答案时挑选出正确结果如何在有限算力预算下分配采样的数量和验证的深度。这三件事正好对应课程里并行采样和验证这两条主线。2. 核心概念训练时计算、测试时计算、采样、验证在进入实操之前先把概念边界理清楚。很多开发者第一次听到“测试时计算”都会问这不就是推理吗严格说推理Inference指模型前向计算生成输出的过程而测试时计算强调的是在推理环节主动追加计算用来提升输出质量。两者是包含关系不是等价关系。2.1 训练时计算 vs 测试时计算维度训练时计算测试时计算阶段模型训练前向反向传播模型部署后的前向推理阶段目的更新模型权重改进单次输出质量常见形式预训练、微调、对齐并行采样、验证、搜索、修正成本特征一次性成本高每次请求可能增加推理成本上线速度慢需要数据、GPU、训练流程快可以按请求动态调整这里的关键是“动态调整”。训练时计算是你无法在线上临时改变的而测试时计算可以针对不同难度的问题动态分配。简单问题少算一点难题多采样几个候选这种灵活性是测试时计算吸引人的重要原因。2.2 采样Sampling采样指从模型输出的概率分布中随机抽取结果。同样的 prompt设置合适的温度参数后每次生成结果可能不同。并行采样就是同时生成多个这样的候选结果为后续验证提供“备选项”。为什么同一个模型对同一个问题能产出多个不同答案因为在复杂推理中模型的解码路径会受随机性影响不同路径可能探索到不同的解题思路。这是并行采样能提升效果的根本原因。2.3 验证Verification验证是为候选结果打分或判别对错的过程。没有验证器并行采样只是增加了候选数量无法回答“哪个答案更好”。验证器可以是规则检查器、代码执行结果、训练好的奖励模型也可以是另一个语言模型。可以把验证器理解成一个“裁判”。模型作为“运动员”负责生成答案验证器负责筛选答案。一个系统里如果只有运动员没有裁判比赛结果无法判断。2.4 自我改进 AI 智能体自我改进智能体指在不需要重新训练权重的情况下通过额外计算、工具调用、自我验证等方式提升任务表现的系统。测试时计算是自我改进的一种重要手段但不是全部。CS329A 课程会陆续覆盖更广的 Agent 学习与评估方法而第二讲聚焦在“用计算换能力”这一条路径上。从概念上理解一个带测试时计算的 Agent 执行流程通常是接收用户问题并行生成 N 个候选计划或答案验证器对每个候选打分或执行检查选择得分最高的候选或让模型根据验证反馈再修正返回最终结果。这条流程在数学题、代码生成、工具调用等任务上都适用只是验证器的形态不同。3. 并行采样让模型自己给自己出多个答案接下来进入实操层面。先说采样。3.1 为什么不能直接用贪心解码默认情况下很多推理模型用贪心解码Greedy Decoding或低温度采样每次选概率最高的 token。它的优点是稳定缺点是遇到复杂推理问题时容易陷入局部最优。一个链式推理可能在第 10 步出错但第 10 步的 token 在局部看概率并不低于是错误被一路带到底。贪心解码本质上只探索一条路径。模型的概率分布里可能还藏着许多得分接近但结论不同的推导路径。并行采样的思路是一次跑多个路径路径之间相互独立然后用验证器决定哪条路径最靠谱。3.2 影响采样多样性的关键参数并行采样不是简单地把 N 设大就行还要控制温度和采样方式。Temperature温度控制概率分布的平滑程度。温度越高输出多样性越大但过低质量的风险也增加。做并行采样时温度通常设置在 0.6 到 1.0 之间具体需要根据任务验证。如果答案有唯一标准比如数学题太低可能多样性不足太高会导致大量无效候选。Top-p核采样只在累计概率达到 p 的 token 集合中采样。Top-p 配合温度使用。p 越大候选词越多多样性越高。Top-k只从概率最高的 k 个 token 中采样。现在很多推理模型接口仍提供 top-k 参数但 top-p 更常用。max_tokens给每个候选足够的生成空间。复杂推理如果输出长度受限很容易在关键步骤上截断导致最终结果不完整。并行采样时不要为了省成本把 max_tokens 压得太低。3.3 并行采样最小代码示例下面是一个使用 OpenAI 兼容接口风格的并行采样示意代码。注意实际 API 参数以你使用的模型服务为准这里重点演示整体流程。import asyncio from openai import AsyncOpenAI client AsyncOpenAI() async def sample_once(prompt: str, temperature: float 0.8, max_tokens: int 1024) - str: resp await client.chat.completions.create( modelyour-model-name, messages[{role: user, content: prompt}], temperaturetemperature, max_tokensmax_tokens, ) return resp.choices[0].message.content async def parallel_sample(prompt: str, n: int 8, temperature: float 0.8) - list[str]: tasks [sample_once(prompt, temperaturetemperature) for _ in range(n)] results await asyncio.gather(*tasks) return results if __name__ __main__: prompt 请解这道数学题一个两位数十位数字比个位数字大 3两位数字之和是 9求这个数。 candidates asyncio.run(parallel_sample(prompt, n8)) for i, ans in enumerate(candidates): print(f[{i}] {ans}\n)这段代码的关键是asyncio.gather实现并行请求。如果模型服务支持 batch 接口也可以一次性传入多个相同的 prompt效率更高。真实项目中还要做超时控制、错误重试和并发限流避免把下游模型服务打爆。到这里我们有了 N 个候选答案但还没有办法判断哪个是对的。下面进入验证环节。4. 验证没有标准答案时如何判断好坏并行采样只是第一步验证才是测试时计算的灵魂。如果验证器不可靠那么采样再多样也没用甚至会选到更差的答案。从工程角度验证器可以分为三类规则型验证器、学习型验证器、语言模型验证器。4.1 规则型验证器规则型验证器通过确定性规则判断答案是否正确。最典型的场景是代码生成候选代码可以被执行通过测试用例就是正确不通过就是错误。数学题也可以通过比较最终答案的字符串是否为预期值来判断但要求问题本身有确定的正确答案。这类验证器有两个优点准确、无额外模型成本。缺点是覆盖率有限只能处理适合程序化检查的任务。不要把规则验证器用在不适合的任务上否则会得到“表面通过”但实际质量很差的候选人。4.2 学习型验证器结果奖励模型与过程奖励模型学习型验证器通过训练一个奖励模型来给候选结果打分。常见两种形式Outcome Reward ModelORM根据最终结果的好坏打分适合判断“这组答案是否正确”。Process Reward ModelPRM根据推理过程的每一步质量打分能更早发现中间步骤错误。PRM 比 ORM 能提供更细粒度的反馈但训练数据更难标注。实践中很多团队先训练 ORM因为标注成本低效果也足够用。如果你的场景是“多个候选答案选最优”训练一个 ORM 是性价比很高的选择。流程是先用模型生成一批答案让人工或自动规则打标然后训练一个二分类或打分模型。这里不展开训练细节但需要强调验证器必须和生成模型在同一分布下训练或验证否则会出现验证器偏好某类风格、忽略真实质量的问题。4.3 LLM 作为验证器不用额外训练直接让另一个更强的语言模型做裁判即 LLM-as-a-Judge。实践里常用 GPT-4 这类大模型对候选答案打分或者用同模型附带“你是一个严格的评审员”之类指令来打分。优点是零训练成本缺点是可能有偏好偏差、不稳定、成本较高。使用 LLM 验证器的时候最好让评委输出评分理由或结构化评分项不要只给一个分数否则后续很难定位问题。4.4 自一致性没有验证器怎么办自一致性Self-Consistency的思路非常巧妙不单独验证每个答案而是生成多个推理路径和答案然后对最终答案投票出现次数最多的答案胜出。它的假设是正确的答案通常能被多条路径收敛到而错误答案往往是发散、各不相同的。这个假设在数学推理等单答案任务上表现得很好而且实现起来几乎零成本。from collections import Counter import re def normalize_answer(answer: str) - str: # 示意提取最终答案数字或核心文本 match re.search(r[(]?\s*([0-9](?:\.[0-9])?)\s*[)]?$, answer.strip()) if match: return match.group(1) return answer.strip() def self_consistency(answers: list[str]) - str: normalized [normalize_answer(a) for a in answers] counter Counter(normalized) most_common counter.most_common(1)[0][0] return most_common自一致性的局限也很明显它要求任务有单一正确答案或少数规范答案。对于开放式写作、创意生成、对话任务投票可能没有意义。另外如果模型系统性地犯错多条路径可能都收敛到同一个错误答案投票无法纠偏。4.5 验证器打分选择答案当你有一个可打分的验证器时可以用 Best-of-N 方式选择最高分答案。def select_best_with_verifier(answers: list[str], verifier) - str: scored [(verifier.score(a), a) for a in answers] scored.sort(keylambda x: x[0], reverseTrue) return scored[0][1]这段代码把验证器抽象成了有score方法的对象。真实项目中score可能是奖励模型的分数、LLM 打出的 1 到 10 分或者是规则执行的结果。无论哪种核心都是把“选择”从人工决策变成程序决策。从课程讨论的问题看验证器质量直接决定测试时计算的天花板。如果验证器本身不行那么增加采样数量只会带来计算浪费不会带来效果提升。5. 测试时计算的扩展规律不是无脑多采样就完事很多人会把测试时计算简单理解成“加大 N”。从这个视角看测试时计算就只是个“花钱买效果”的工程技巧。实际上测试时计算同样存在扩展规律盲目增加采样数会进入收益递减区间。5.1 Best-of-N 的收益曲线当 N 从 1 增加到 8、16、64 时候选答案越多选到正确结果的概率越大但这个增速会放缓。原因很简单模型能生成的有效解答路径是有限的。当 N 大到一定程度新增的样本大多在重复已有答案很难带来新的正确路径。更关键的是如果验证器并不完美那么 N 增大后分数最高但实际错误的“高分数错误答案”也可能增多。在验证器有偏差时Best-of-N 的效果可能比想象中差很多。5.2 顺序修正与搜索除了并行采样测试时计算还有一种重要形态顺序修正。模型先生成一版答案验证器发现问题模型根据反馈修正再验证再修正。这个过程类似人在做题时反复检查草稿纸。顺序修正可以用搜索算法组织比如带验证器的 beam search、树搜索。每一步扩展若干个候选节点用验证器评估节点质量保留最好的几条路径继续搜索。这种方式比单纯并行采样更擅长处理需要长链 推理的任务因为它能在探索过程中及时发现错误分支并剪枝。但顺序搜索也有代价延迟更高、实现更复杂、每一步验证器的误差会累积。并行采样适合“答案有多个独立思路但判定简单”的任务顺序搜索适合“解题过程高度依赖前序步骤、需要中途纠错”的任务。5.3 计算预算怎么分一个值得记住的经验法则采样数量和验证器深度要先分别调优再联合调优。先用固定验证器把 N 调到收益曲线拐点附近再提升验证器质量。如果提升验证器质量带来的收益大于增加 N 带来的收益说明瓶颈在验证器。实际项目中更推荐按问题难度动态分配计算预算。简单问题直接用一次生成结果返回中等难度用 N 次采样加投票困难问题用顺序修正或树搜索。代价是需要引入一个难度估计器但换来的是在固定总预算下的更好效果。5.4 为什么这和“自我改进”强相关CS329A 把测试时计算放进“自我改进 AI 智能体”主题背后的逻辑是智能体不需要“修改权重”也能改进自己。它通过增加候选、验证反馈、修正循环在单次任务内实现性能提升。这意味着我们可以把“训练”的很多思想搬到推理阶段训练时的损失函数对应验证器训练时的多次迭代对应顺序修正。这种思路对大模型应用团队非常有用因为它把“模型能力提升”从月度级训练任务变成了小时级的部署策略调整。6. 综合示例一个最小可运行的测试时计算流水线把前面几节的概念串起来下面是一个更完整的测试时计算流水线并行采样 - 验证器打分 - 阈值检查 - 必要时候选重采样。代码是示意级的真实项目里需要替换成具体模型接口和验证器实现。import asyncio import random from openai import AsyncOpenAI client AsyncOpenAI() class SimpleVerifier: 示意验证器实际可替换为 ORM / PRM / 代码执行结果 def score(self, answer: str) - float: # 这里只是一个示例关键词命中得分 score 0.0 if 42 in answer: score 0.5 if 因为 in answer and 所以 in answer: score 0.2 return score async def sample_batch(prompt: str, verifier: SimpleVerifier, max_rounds: int 2): best_answer best_score -1.0 for i in range(max_rounds): tasks [ gen_answer(prompt) for _ in range(6) ] answers await asyncio.gather(*tasks) for ans in answers: s verifier.score(ans) if s best_score: best_score s best_answer ans if best_score 0.8: break # 简单重采样把上一轮高分答案拼在 prompt 后引导模型修正 prompt f{prompt}\n\n请参考上次较好的思路{best_answer[:200]}\n再给一个更严谨的解答。 return best_answer, best_score async def gen_answer(prompt: str) - str: resp await client.chat.completions.create( modelyour-model-name, messages[{role: user, content: prompt}], temperature0.8, max_tokens1024, ) return resp.choices[0].message.content if __name__ __main__: prompt 某个数的两倍加 10 等于 94求这个数。 verifier SimpleVerifier() final_answer, score asyncio.run(sample_batch(prompt, verifier)) print(f最终答案{final_answer}) print(f验证分数{score})这段代码包含两个重要设计。第一是“阈值提前停止”。如果验证器打分达到阈值就不再继续生成。这个设计能明显节省计算成本避免所有请求都跑到最大采样数。第二是“带反馈的重采样”。当候选答案没有达标时把上一轮的高分片段作为提示词补丁让模型在下一轮基于已有思路继续改进。这比单纯重新采样更接近“自我改进”的含义。运行后你能看到控制台输出最终答案和验证分数。这个示例的核心目的不是解决具体题目而是演示测试时计算流水线的骨架采样、验证、反馈、再采样。7. 测试时计算的常见问题与排查思路在实际项目里应用测试时计算很多问题是有共性的。下面列出一份排查清单。问题现象可能原因排查方式解决方案采样数增多但效果变化不大温度过低候选过于相似查看候选答案的多样性计算去重率适当提高温度或改为动态调整 temperature答案多样性高但正确率低温度过高产生了大量无用候选抽样检查无效候选的错误模式降低温度或引入过程奖励模型提前剪枝验证器选出明显错误的答案验证器存在风格偏好或偏差对比验证器分数与人工评估重新训练/校准验证器或使用多条验证规则延迟增加用户可感知并行采样占用时间长或串行执行监控单次请求耗时和并发数使用异步并发、批处理接口或做预算分级计算成本过高所有请求都做大量采样检查日志中采样次数分布按问题难度设置不同采样预算简单问题直接返回带反馈重采样时模型开始“编造”反馈 prompt 不严谨模型过度联想观察修正后的答案与原始问题相关性限制反馈内容长度保留原始问题或改用验证器过滤后再反馈自一致性投票答案不唯一任务本身存在多个合法答案检查候选答案的最终结果格式改用验证器打分或先做答案规范化这里真正容易踩坑的地方是第一条。很多工程团队在调试并行采样时发现 N 从 4 调到 32效果几乎没变就以为测试时计算无效。实际原因往往是温度设成了 0.2模型每次输出几乎一样。在开始排查验证器之前先检查候选的多样性。另一个容易踩坑的是验证器校准。如果你发现验证器给高分的结果在人工评估里表现反而更差先不要急着换模型可以尝试校准验证器阈值或者把单分数验证改成多维度评分加上人工抽检。8. 工程实践与生产落地建议8.1 建立验证器的闭环评估测试时计算系统的核心是验证器所以第一步要建立验证器本身的评估集。从生产数据里抽一批请求人工标注正确结果然后评估验证器的准确率和排序一致性。不要在没评估验证器的情况下盲目上线测试时计算。8.2 日志要记录采样分布和验证分数生产环境必须记录每个请求的采样次数、温度、每个候选答案的验证分、最终选中的答案。否则你无法判断效果波动是因为模型升级、验证器变化还是因为采样种子变化。8.3 缓存与去重并行采样会带来大量相似结果。可以在验证器之前加一个语义去重或哈希去重把完全相同或高度相似的答案去掉减少验证器调用次数。这一步在高成本 LLM 验证器场景下尤其重要。8.4 延迟与成本分级先定义一个“简单问题”的判定规则比如基于问题长度、关键词或验证器直接打分。简单问题走单次生成中等难度走并行采样投票困难问题走顺序修正。不要让所有问题都享受最高等级的测试时计算否则成本不可控。8.5 安全与边界测试时计算不会天然解决安全风险。一个高噪声采样环境里模型可能生成更多不安全的候选。因此安全过滤应该放在验证器之前或并行采样的出口处而不是只依赖验证器打低分。尤其是在 Agent 场景下候选内容可能在工具执行之前就需要安全过滤避免模型调用危险操作。8.6 什么时候不值得用测试时计算如果任务本身答案没有质量梯度比如简单的称呼、固定文案替换测试时计算没有意义。如果模型能力明显不足连基础推理都经常跑偏测试时计算只能放大错误路径这时候应该先换模型或做微调。9. 总结与继续学习方向这一讲的核心是把“让 AI 更强”的思路从训练阶段扩展到部署阶段。测试时计算不是一项玄学技术它由三块基石构成并行采样、验证机制、计算预算分配。采样负责提供多样性验证负责判断质量预算决定在什么尺度上执行前两者。对于普通开发者最快能上手的路径是先在一个有规则判断的任务比如代码生成或数学选择题上实现 Best-of-N把采样、验证、选优的链路跑通然后逐步引入 LLM 验证器处理开放问题最后再尝试带反馈的多次重采样或搜索方法。如果希望深入学习可以继续关注 CS329A 课程中关于验证器设计、Agent 评估与学习的内容。测试时计算是自我改进智能体的第一块拼图后面还会有更多关于经验利用、环境反馈和长期学习的主题。把这些概念和工程实现结合起来你就能在自己的系统里真正用上“不用训练也能变强”的模型优化思路。建议你下一步做这样一个小实验拿一个你当前效果不够理想的思考类任务跑 8 个并行候选用最简单的字符串规则或 LLM 评分当验证器对比单次生成的准确率。多数情况下你会立刻看到提升。注意记录温度和采样多样性的关系这一步的调优经验会在你后续扩展搜索、验证和 Agent 能力时持续发挥作用。