公司动态
推理大模型测试时扩展:推理模式与可复现评估指南
最近几周在折腾推理大模型的测试时扩展Test-Time Scaling一个很直观的感受是模型本身的能力只是起点推理阶段的“算力分配方式”对最终效果的影响比想象中更大。同一个模型采用不同的推理模式在数学推理、逻辑问答这类任务上可能拉开 10 到 20 个百分点的差距。更麻烦的是这些推理模式的评估结果还不容易复现换一个采样温度、换一个评估脚本结论可能就变了。这篇文章打算系统地拆解一下 Reasoning LLMs 中的 Test-Time Scaling它到底在优化什么、有哪些典型的推理模式Inference Regimes、如何设计可复现的评估流程以及第三方评估工具如何接入自己写的 API 和自定义指标。内容偏向工程落地会给出可以直接跑的 Python 示例适合正在做 LLM 应用评测、推理优化或模型选型的同学参考。文中使用的代码和配置都以常见开源环境为例实际使用时需要根据自己的模型部署方式调整。1. 背景与核心概念1.1 什么是 Test-Time ScalingTest-Time Scaling 指的是在模型推理阶段通过增加计算量来提升输出质量的做法。传统机器学习中我们更熟悉的是训练阶段的扩展也就是通过增加参数量、训练数据量和训练算力来提升模型能力这一类规律对应的是经典的 Scaling Laws。但到了 Reasoning LLM 时代情况发生了一个明显变化模型在推理阶段也可以通过“多想一会”“多采样几次”“多搜索几步”来获得更好的答案。这就是测试时扩展的含义。它并不修改模型权重而是在同样的模型参数下优化推理时的计算预算分配方式。一个直观的例子是数学应用题。直接让模型输出答案可能只能得到 60% 的准确率但如果让模型生成多个候选答案再通过投票或奖励模型筛选准确率可能提升到 80% 以上。这个过程消耗的推理计算量增加了但没有重新训练模型。1.2 为什么 Test-Time Scaling 备受关注Reasoning LLMs 的核心能力在于“推理”而推理本身是可以逐步展开的。像 o1 系列、DeepSeek-R1 这类模型在内部会生成一段较长的思考过程再给出最终答案。这种“长思考”本身就是一种测试时扩展。对于应用开发者来说Test-Time Scaling 的价值主要体现在三个场景高难度推理任务数学竞赛题、代码竞赛题、复杂逻辑推理需要多次尝试和验证。缺少明确参考答案的场景只要答案有可验证的评分方式就可以通过多次采样来提高上限。算力充足但模型参数量受限的情况用小模型配合多轮采样有时可以逼近大模型单次推理的效果。1.3 推理模式Inference Regimes的通俗理解推理模式可以理解为“在给定测试时计算预算下模型以什么策略来生成答案”。不同的模式决定了计算资源的分布方式。举几个典型的模式单次贪心解码让模型直接以最高概率路径生成答案计算开销最小但容易陷入局部最优。多次采样 多数投票生成多个答案按答案出现频率投票适合答案可比较的任务。多次采样 奖励模型排序生成多个答案用奖励模型打分取最高分答案适合答案难以直接比较的任务。搜索式推理在推理过程中引入树搜索或 beam search让模型在候选推理路径中探索适合需要多步验证的任务。1.4 核心关键词速览关键词含义在本文中的作用Test-Time Scaling测试时扩展文章主题Reasoning LLMs推理大模型被优化的对象Inference Regimes推理模式计算预算分配策略Evaluation评估验证扩展是否有效Reproducibility可复现性保证结论稳定可靠2. 环境准备与版本说明2.1 运行环境本文的示例代码基于 Python 3.9 以上版本推荐使用 Python 3.10 或 3.11。操作系统不限Windows、Linux、macOS 都可以运行但涉及模型部署的部分建议使用 Linux 服务器。在开始之前建议先创建一个独立的虚拟环境避免依赖冲突python -m venv venv source venv/bin/activateWindows 环境下激活命令为venv\Scripts\activate2.2 需要安装的依赖示例代码的核心依赖如下openai1.0.0 pandas1.5.0 numpy1.24.0 pyyaml6.0 requests2.28.0 python-dotenv1.0.0安装命令pip install -r requirements.txt如果你的模型服务是通过 vLLM、TGI 或 Ollama 部署的这些工具大多提供 OpenAI 兼容接口本文的代码可以直接使用。只需要修改base_url指向你的本地服务地址即可。2.3 模型服务部署方式本文不重点讲解模型部署细节但为了方便下面的代码演示建议准备一个 OpenAI 兼容的推理服务。以 vLLM 为例部署一个开源模型的命令大致如下python -m vllm.entrypoints.openai.api_server \ --model Qwen/QwQ-32B \ --port 8000这里需要说明示例中的模型名称、端口号仅作演示具体以你实际使用的模型和服务地址为准。如果你使用的是云端 API也可以直接配置OPENAI_API_KEY和base_url。2.4 环境变量配置在项目根目录创建一个.env文件用于保存 API 相关的配置OPENAI_API_KEYyour-api-key OPENAI_BASE_URLhttp://localhost:8000/v1 MODEL_NAMEyour-model-name然后在 Python 中加载from dotenv import load_dotenv import os load_dotenv() api_key os.getenv(OPENAI_API_KEY) base_url os.getenv(OPENAI_BASE_URL) model_name os.getenv(MODEL_NAME)3. 核心机制拆解3.1 推理模式的分类在 Test-Time Scaling 研究中推理模式通常可以按“生成方式”和“选择方式”两个维度来分类。生成方式决定了模型如何产生候选答案Greedy Decoding每次只选概率最高的 token输出唯一答案。Temperature Sampling通过温度参数控制随机性生成多个候选答案。Top-p Sampling在概率累积到一定阈值的小集合内采样兼顾多样性。Beam Search保留多个候选序列逐步扩展适合生成任务。选择方式决定了如何从多个候选中确定最终答案Majority Voting按答案内容聚类取出现最多的答案。Weighted Voting按置信度加权例如使用模型对每个答案的自评估概率。Reward Model Ranking训练或使用一个奖励模型对候选答案打分排序。Verification让模型反向验证每个候选答案的正确性例如要求模型检查推理步骤。实际项目中常常组合使用。例如先采样 N 个答案再用一个轻量级验证器挑选最优答案。3.2 计算预算分配策略Test-Time Scaling 的核心问题是在固定推理成本下如何分配计算预算。先看一个简单的对比策略采样数单次推理长度总成本适用任务单次贪心1短低简单问题自一致性采样8中中答案可比较的任务Best-of-N16中高需要奖励模型筛选的任务搜索扩展8长很高需要多步验证的任务这里的“总成本”并不是简单相乘因为有些任务需要先将生成的候选答案缓存下来再做后续筛选。你可以在实际项目中先跑一个小规模实验统计不同策略的准确率和成本曲线再决定生产环境使用哪种模式。3.3 自一致性Self-Consistency详解自一致性是最容易落地的一种 Test-Time Scaling 方法。它的思路很简单让同一个模型在相同提示下采样多个答案然后统计答案出现的频率选择出现次数最多的答案。这个方法的有效性来自一个观察对于推理任务正确答案往往在采样分布中占据较高概率质量虽然单次采样可能因为随机性跑偏但多次采样后正确答案会更容易“重复出现”。下面是一个伪代码逻辑def self_consistency(samples): # samples 是模型生成的多个答案通常是字符串列表 counter {} for answer in samples: normalized normalize_answer(answer) counter[normalized] counter.get(normalized, 0) 1 best_answer max(counter, keycounter.get) return best_answer, counter需要注意的是自一致性要求答案能够被规范化比较。对于数学题可以比较最终数值对于选择题可以比较选项字母但对于开放式问答直接比较字符串可能不合适这种情况更适合用奖励模型排序。3.4 奖励模型与排序策略当答案难以直接比较时可以引入一个奖励模型Reward Model或验证器。这个模型接收“问题 候选答案”输出一个分数代表答案的质量。在测试时扩展流程中典型的做法是用生成模型采样 N 个候选答案。对每个候选答案调用奖励模型打分。选择分数最高的答案作为最终输出。这里的奖励模型可以是训练好的专用模型也可以是一个通过 API 调用的评分服务甚至可以是一个带有评分 prompt 的大模型。关键是这个评分器要尽量稳定不能在多次评估之间产生较大波动否则会破坏可复现性。4. 完整实战案例4.1 场景定义为了演示 Test-Time Scaling 的完整流程我们使用一个简单的数学推理场景。假设需要回答下面这类问题一个果园有 48 棵苹果树每棵树平均结 126 个苹果。如果每 12 个苹果装一箱一共需要多少个箱子这类问题答案明确适合用自一致性方法评估。下面的代码会实现三种推理模式单次贪心解码。多次采样 多数投票。多次采样 简易排序器。4.2 项目结构test_time_scaling_demo/ ├── .env ├── requirements.txt ├── llm_client.py # 封装 OpenAI 兼容的模型调用 ├── inference_regimes.py # 实现不同推理模式 ├── evaluation.py # 自定义评估脚本 └── run_demo.py # 主入口4.3 模型调用客户端llm_client.py的作用是统一管理模型 API 的调用方式。这里使用 OpenAI Python SDK但通过base_url指向本地或第三方服务。# 文件路径test_time_scaling_demo/llm_client.py import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI( api_keyos.getenv(OPENAI_API_KEY, EMPTY), base_urlos.getenv(OPENAI_BASE_URL, http://localhost:8000/v1), ) MODEL_NAME os.getenv(MODEL_NAME, your-model-name) def generate(prompt: str, temperature: float 0.0, max_tokens: int 1024) - str: 调用模型生成文本。 temperature0.0 时接近贪心解码temperature0 时采样会更有随机性。 response client.chat.completions.create( modelMODEL_NAME, messages[ {role: system, content: 你是一个严谨的数学解题助手。}, {role: user, content: prompt}, ], temperaturetemperature, max_tokensmax_tokens, ) return response.choices[0].message.content.strip()4.4 实现不同推理模式inference_regimes.py中实现了三种推理模式。# 文件路径test_time_scaling_demo/inference_regimes.py import re from collections import Counter from llm_client import generate def greedy_decoding(prompt: str) - str: 模式一单次贪心解码temperature0。 return generate(prompt, temperature0.0) def majority_voting(prompt: str, n_samples: int 8, temperature: float 0.7) - str: 模式二多次采样 多数投票。 关键点 1. temperature 要大于 0否则多次采样结果完全一样。 2. 答案需要统一提取为“最终数值”之类可比较的字符串。 samples [] for _ in range(n_samples): sample generate(prompt, temperaturetemperature) samples.append(extract_answer(sample)) counter Counter(samples) best_answer counter.most_common(1)[0][0] return best_answer def best_of_n_with_ranker(prompt: str, n_samples: int 8, temperature: float 0.7) - str: 模式三多次采样 简易排序器。 这里用一个启发式排序规则代替真正的奖励模型 优先选择包含“因此”“所以”“最终答案”等推理标记且答案行较完整的输出。 生产环境可以替换为训练好的奖励模型或独立的评分 API。 samples [] for _ in range(n_samples): sample generate(prompt, temperaturetemperature) score heuristic_score(sample) samples.append((sample, score)) samples.sort(keylambda x: x[1], reverseTrue) return samples[0][0] def extract_answer(text: str) - str: 从模型输出中提取最终答案。 这里使用一个最简单的规则提取所有数字。 你可以根据自己的任务类型替换为更严谨的解析逻辑。 matches re.findall(r\d, text) if not matches: return NO_ANSWER # 如果有多个数字取最后一个因为最终答案通常出现在结尾 return matches[-1] def heuristic_score(text: str) - float: 简易排序器根据文本特征给答案打分。 注意这只是一个演示用的启发式方法不代表真实奖励模型的性能。 score 0.0 if 因此 in text or 所以 in text: score 1.0 if 最终答案 in text: score 1.0 # 更长的解答往往包含更完整的推理过程但也可能包含废话需要按实际任务调整 score min(len(text) / 500.0, 1.0) return score4.5 主入口脚本run_demo.py会依次运行三种推理模式并输出对比结果。# 文件路径test_time_scaling_demo/run_demo.py from inference_regimes import greedy_decoding, majority_voting, best_of_n_with_ranker PROMPT 一个果园有48棵苹果树每棵树平均结126个苹果。 如果每12个苹果装一箱一共需要多少个箱子 请给出你的推理过程并最终输出数字答案。 if __name__ __main__: print( 模式一贪心解码 ) result1 greedy_decoding(PROMPT) print(result1) print() print( 模式二多数投票 ) result2 majority_voting(PROMPT, n_samples8, temperature0.7) print(result2) print() print( 模式三Best-of-N 排序器 ) result3 best_of_n_with_ranker(PROMPT, n_samples8, temperature0.7) print(result3)运行命令python run_demo.py4.6 预期结果与说明对于上面这个数学题正确结果是50448 × 126 ÷ 12 504。贪心解码可能直接输出正确答案也可能在中间步骤出错后得到错误答案取决于模型本身能力。多数投票会对 8 次采样结果做数字频率统计通常会提高正确答案的占比。Best-of-N 排序器因为只是启发式打分效果不一定比多数投票更好它的优势主要体现在答案无法直接比较的开放式任务中。这里需要强调一个容易误解的点增加采样数并不总是带来提升。如果模型本身能力较弱多次采样只会产生更多错误答案多数投票可能把高频错误答案当成最终结果。因此在实践前建议先做一个小样本实验。5. 评估与可复现性5.1 为什么评估 Test-Time Scaling 很困难评估测试时扩展的效果比评估普通模型生成质量更复杂。原因主要有三个答案多样性不同推理模式产生的答案风格差异很大简单字符串匹配无法衡量质量。指标敏感性准确率、F1、BLEU 等指标对答案格式非常敏感同一个正确答案描述方式不同就可能被判定为错误。采样随机性Temperature Sampling 本身带有随机性如果不对随机种子和采样参数做固定实验结果很难复现。因此设计评估流程时必须把“评估指标”和“评估过程”都固定下来。5.2 第三方评估工具的选型思路如果你正在寻找一个第三方评估工具要求是“可以调用自己写的 API并根据自己定义的评估标准来评分”目前的常见做法有两种。第一种是使用开源评估框架。例如EleutherAI/lm-evaluation-harness可以注册自定义模型后端和自定义任务你在任务中定义评估指标和评分逻辑。它支持接入 OpenAI 兼容的模型服务也支持自定义指标函数。第二种是自建轻量评估服务。通过一个 HTTP API 将待评估的模型输出发送给评分器评分器内部可以调用大模型、规则脚本或人工审核接口最终返回一个可解析的分数。这种方式的优点是灵活适合团队内部已经有评分中台的场景。5.3 调用自定义 API 的评估实现下面是一个轻量评估脚本的示例。它会把“问题、模型输出、参考答案”打包成 JSON发送到你自己的评估 API然后解析返回的分数。# 文件路径test_time_scaling_demo/evaluation.py import json import requests def evaluate_with_custom_api( question: str, model_output: str, reference_answer: str, eval_api_url: str, ) - dict: 调用自定义评估 API 评分。 请求体设计为通用格式 { question: 原始问题, model_output: 模型输出, reference_answer: 参考答案 } 评分 API 应返回 JSON至少包含 score 字段例如 {score: 0.85, passed: true, comment: 答案正确} payload { question: question, model_output: model_output, reference_answer: reference_answer, } try: response requests.post(eval_api_url, jsonpayload, timeout30) response.raise_for_status() return response.json() except requests.exceptions.Timeout: return {score: 0.0, passed: False, comment: 评估超时} except requests.exceptions.RequestException as e: return {score: 0.0, passed: False, comment: f请求失败: {e}} if __name__ __main__: result evaluate_with_custom_api( question一个果园有48棵苹果树每棵树平均结126个苹果每12个苹果装一箱需要多少个箱子, model_output504, reference_answer504, eval_api_urlhttp://localhost:9000/evaluate, ) print(json.dumps(result, ensure_asciiFalse, indent2))你只需在本地启动一个评分服务并在/evaluate接口里实现自己的评分逻辑就可以用这套流程评估任意模型的输出。5.4 可复现性清单为了保证 Test-Time Scaling 实验结果可复现建议记录以下信息项目说明模型名称与版本记录模型权重版本、部署方式采样参数temperature、top_p、max_tokens随机种子如果框架支持固定全局 seed采样次数N 的数量提示词模板包括 system prompt 和 user prompt答案提取规则例如提取最后一个数字评估 API 版本自定义评分服务的版本号运行时间与批次记录实验批次方便回溯将这些信息写入一个experiment_config.yaml文件会是一个不错的选择。# 文件路径test_time_scaling_demo/experiment_config.yaml model: name: your-model-name base_url: http://localhost:8000/v1 version: 2025-06-01 sampling: temperature: 0.7 top_p: 0.9 max_tokens: 1024 n_samples: 8 seed: 42 evaluation: api_url: http://localhost:9000/evaluate metric: custom_score后续复现实验时只要读取这个配置文件就能保证大部分关键参数一致。6. 常见问题与排查思路6.1 典型问题列表问题现象常见原因解决思路多次采样结果完全相同temperature 设置为 0 或模型忽略了采样参数将 temperature 调到 0.7 以上多数投票结果反而变差模型本身错误率高错误答案重复出现减少采样数或改用奖励模型排序评估 API 请求超时自定义评分服务处理速度慢并发过高增加超时时间或对评估请求做批量排队答案提取错误正则规则过于简单无法覆盖所有输出格式根据任务定制解析规则或使用大模型抽取答案实验结果无法复现没有固定随机种子、模型版本或提示词记录配置清单固定所有关键参数成本骤增N 设置过大推理长度过长先画准确率-成本曲线再选拐点6.2 排查流程建议如果你发现 Test-Time Scaling 没有带来明显提升建议按以下顺序排查检查任务类型答案是否存在唯一标准如果答案本身有歧义多数投票可能不适用。检查采样多样性输出是否过于重复可以打印 8 次采样的原始文本观察差异性。检查答案提取规则是否把正确答案解析成了错误格式检查评估指标如果评估 API 的评分标准不稳定实验结果自然不稳定。检查成本预算如果 N 太大但收益很小需要换一个更高效的推理模式。7. 最佳实践与工程建议7.1 结合任务选择推理模式不要一上来就堆采样数量。先明确任务的答案是否可比较、验证成本高不高、延迟要求多严格。答案可比较的任务优先用多数投票。答案不可比较、有验证器优先用 Best-of-N 奖励模型。多步推理、需要探索的任务考虑搜索式推理但成本和延迟会明显增加。7.2 设置自适应预算可以在代码中加入一个简单的自适应逻辑先用低采样数生成回答如果置信度不足再增加采样数。例如通过模型输出多个候选答案若多数投票的一致性比例较高就直接返回结果如果一致性较低再扩大采样规模。def adaptive_majority_voting(prompt, min_samples4, max_samples16, threshold0.6): samples [] for i in range(max_samples): samples.append(extract_answer(generate(prompt, temperature0.7))) if i 1 min_samples: counter Counter(samples) top_count counter.most_common(1)[0][1] if top_count / (i 1) threshold: break return counter.most_common(1)[0][0]这种策略可以显著减少平均推理成本。7.3 做好缓存与去重在多次采样中经常会出现完全相同的输出。可以在客户端缓存历史请求的 response对相同的 prompt 采样参数组合直接复用结果。同时在答案提取后做去重减少后续排序的候选数量。7.4 日志与版本管理每次实验都需要保存原始输出、解析结果、评估结果和配置信息。建议使用统一的 JSON 格式存储文件名包含实验批次和模型名称。{ experiment_id: exp-001, model: your-model-name, inference_regime: majority_voting, n_samples: 8, temperature: 0.7, question: ..., raw_outputs: [..., ...], final_answer: 504, evaluation_score: 1.0 }这样可以随时回溯“某条答案是怎么来的、对应的参数是什么”。7.5 生产环境的注意事项控制最大采样数和 max_tokens避免单次请求超时。对上游模型 API 做限流和重试防止批量评估时触发限流。评估服务与模型生成服务最好隔离部署避免互相影响性能。在正式上线前先用小规模数据集跑通全流程再逐步扩大评估集。8. 总结与学习路线在本文中我们拆解了 Test-Time Scaling 的核心概念梳理了推理模式分类并用一个数学推理场景实现了贪心解码、多数投票和 Best-of-N 排序三种模式。然后我们讨论了评估和可复现性的关键问题给出了调用自定义评估 API 的脚本示例并整理了常见问题和工程建议。如果你正在落地推理模型的测试时扩展我建议按下面的顺序继续深入先固定一个任务集和评估 API保证实验结果可信。在 100 条样本上对比单次贪心、多数投票、Best-of-N 的效果和成本。找到适合自己任务的最优推理模式后再考虑奖励模型或搜索式推理。最后给一个实用的小建议不要追求“把 N 调到最大”。Test-Time Scaling 的核心是“以最低的计算成本获得最大的效果提升”这更像是一个工程优化问题而不是一个简单的参数调节问题。每次实验前写下配置、每次实验后记录结果你会发现可复现性带来的收益远比你多跑几组参数更大。