AReaL 分布式模型评估实战指南:复用训练控制器基础设施,在单机、Ray 与 Slurm 上规模化推理评估
2026/9/18 16:34:33 网站建设 项目流程

AReaL 分布式模型评估实战指南:复用训练控制器基础设施,在单机、Ray 与 Slurm 上规模化推理评估

【免费下载链接】AReaLThe RL Bridge for LLM-based Agent Applications. Made Simple & Flexible.项目地址: https://gitcode.com/GitHub_Trending/are/AReaL

本指南系统讲解 AReaL 的评估能力:它并不内置"数据集获取 + 指标计算"的完整评测管线,而是为你训练好的模型提供与训练同构的分布式推理基础设施——你可以复用既有工作流、调度器与配置,在多个 GPU 和节点上扩展评估,并且由于 AReaL 保存的是 HuggingFace 兼容检查点,你可以无需任何转换地把检查点直接交给第三方评估框架使用。读完本文,你将掌握基于 examples/math/gsm8k_eval.py 编写评估脚本、在 local/Ray/Slurm 三种调度器下跑通分布式评估、理解单一控制器架构,以及如何把自定义奖励逻辑接入评估工作流。

核心设计:评估 = 去掉训练组件的控制器模式

AReaL 评估的核心思想一句话概括:评估与训练共用同一套控制器基础设施。训练时,RolloutController通过调度器拉起推理 worker、提交带工作流的生成任务、收集结果并计算奖励;评估时,只需要保留这条"推理链路",去掉训练 worker(actor/critic/ref)即可。

在源码层面,这一设计体现在RemoteSGLangEngine.as_controllerRemotevLLMEngine.as_controller上:

  • areal/engine/sglang_remote.py:as_controller根据配置版本返回RolloutController(v1)或RolloutControllerV2(v2);
  • areal/engine/vllm_remote.py:与 SGLang 完全一致。

也就是说,评估脚本可以直接以engine_cls.as_controller(config.rollout, scheduler)的方式构造控制器,与训练时的 actor 控制器走同一套RolloutController实现(areal/infra/controller/rollout_controller.py)。

注意边界:AReaL 提供的是"分布式推理"而非"完整评测管道"。数据集加载、指标计算、结果汇总这些环节由你自己的工作流与第三方库完成;AReaL 检查点是 HuggingFace 兼容格式,可直接与任意评估框架对接。

快速开始:在 GSM8K 上跑通一次评估

单机评估

examples/math/gsm8k_grpo.yaml为配置骨架,通过命令行覆盖两个关键字段:调度器类型设为local、actor 模型路径指向你训练好的检查点:

python3 examples/math/gsm8k_eval.py \ --config examples/math/gsm8k_grpo.yaml \ scheduler.type=local \ actor.path=/path/to/checkpoint

其中scheduler.type=local表示本机调度,actor.path指向待评估的检查点(可以是本地路径,也可以是 HuggingFace 模型名,如Qwen/Qwen2.5-1.5B-Instruct)。命令行覆盖机制由 areal/api/cli_args.py 中的load_expr_config提供。

分布式评估

AReaL 支持 Ray 与 Slurm 两种集群调度方式,并且rollout.backend中的并行度配置与cluster.n_nodes结合,即可把评估横向扩展到多节点多 GPU:

# 使用 Ray(3 个节点,12 个 GPU) python3 examples/math/gsm8k_eval.py \ --config examples/math/gsm8k_grpo.yaml \ scheduler.type=ray \ rollout.backend=sglang:d12p1t1 \ cluster.n_nodes=3 # 使用 Slurm(12 个节点,96 个 GPU) python3 examples/math/gsm8k_eval.py \ --config examples/math/gsm8k_grpo.yaml \ scheduler.type=slurm \ rollout.backend=sglang:d96p1t1 \ cluster.n_nodes=12

这里的sglang:d12p1t1就是 AReaL 的资源分配语法sglang指定推理后端,冒号后的字母数字对依次表示并行维度。其解析实现在 areal/api/alloc_mode.py 的ModelAllocation.from_str(基于 Lark 文法_LLMParallelParser):

维度缩写含义说明
d数据并行 (data parallel)复制模型、切分数据,等于 worker 实例数
p流水线并行 (pipeline parallel)按层切分模型(PP 推理)
t张量并行 (tensor parallel)按算子切分模型(TP 推理)
c上下文并行 (context parallel)仅对 attention 模块生效,评估中较少用
e专家并行 (expert parallel)仅对 MoE 模块生效

因此sglang:d12p1t1表示 12 路数据并行、1 路流水线、1 路张量的 SGLang 推理集群,共 12 个 GPU;sglang:d96p1t1同理对应 96 个 GPU。ParallelStrategy类的world_size属性即dp × cp × tp × pp(areal/api/alloc_mode.py),与cluster.n_nodes共同约束调度器分配资源。

评估指标:把奖励逻辑变成评估逻辑

评估的指标本质上就是"对一条生成结果打分"。AReaL 的设计哲学是评估复用训练工作流——训练时使用的奖励函数,在评估时同样作为打分器使用(详见 智能体 RL 指南)。

下面的示例演示了如何编写一个独立的"智能体数学评估器"。注意这段评估代码完全独立于 AReaL:它使用 OpenAI Agents SDK 定义带计算器工具的智能体,通过AsyncOpenAI客户端指向 AReaL 的推理服务,并用math_verify做答案校验。这样你评估的不仅是模型的"裸输出",而是完整的智能体工具调用轨迹

from agents import Agent, OpenAIProvider, RunConfig, SQLiteSession, function_tool from agents import Runner as OpenAIRunner from math_verify import parse, verify from openai import AsyncOpenAI @function_tool def add(a: float, b: float) -> float: """Add two numbers.""" return a + b @function_tool def multiply(a: float, b: float) -> float: """Multiply two numbers.""" return a * b def math_reward_fn(completions: str, answer: str) -> float: return float(verify(parse(completions), parse(answer))) class MathAgent: async def run(self, data, **extra_kwargs): http_client = extra_kwargs.get("http_client") base_url = extra_kwargs.get("base_url") or os.getenv("OPENAI_BASE_URL") api_key = extra_kwargs.get("api_key") or os.getenv("OPENAI_API_KEY") client = AsyncOpenAI(base_url=base_url, api_key=api_key, http_client=http_client, max_retries=0) run_config = RunConfig( model_provider=OpenAIProvider(openai_client=client), model="default", tracing_disabled=True, ) agent = Agent( name="RLVR Math with Calculator", instructions="Answer math questions using the calculator tools.", tools=[add, multiply], ) result = await OpenAIRunner.run( agent, input=data["messages"][-1]["content"], session=SQLiteSession("math"), run_config=run_config, ) return math_reward_fn(result.final_output, data["answer"])

这里MathAgent.run的返回值即该样本的得分;data["messages"][-1]["content"]取出用户问题作为输入,data["answer"]作为标准答案,math_reward_fnmath_verifyparse + verify做严格数学答案比对。它可以是训练奖励函数的"评估版",也可以是专门为评测编写的独立打分逻辑。

架构:单一控制器进程 + GPU 推理 worker

评估采用单一控制器架构,不启动任何训练 worker:

Controller Process │ └─> Inference Engine Controller (SGLang/vLLM) ├─> Scheduler creates inference workers ├─> Submits evaluation tasks with workflow └─> Collects results and computes metrics

控制器运行在CPU 进程中负责协调:通过调度器(LocalScheduler/RayScheduler/SlurmScheduler)创建推理 worker、把评估任务连同工作流提交下去、汇总结果并计算指标;推理 worker 运行在 GPU上,实际执行 token 生成。这与训练时的 actor 控制器拓扑一致,只是把"训练引擎"替换为纯推理引擎。

实现:从配置解析到结果导出的完整调用链

examples/math/gsm8k_eval.py 是评估的标准实现,其关键模式如下(与文档中的骨架一致,且与仓库源码逐行对应):

from areal.api.alloc_mode import ModelAllocation from areal.api.cli_args import GRPOConfig, SGLangConfig, load_expr_config, vLLMConfig from areal.engine.sglang_remote import RemoteSGLangEngine from areal.engine.vllm_remote import RemotevLLMEngine from areal.infra import LocalScheduler, RayScheduler, SlurmScheduler # 加载配置并解析 rollout 后端 config, _ = load_expr_config(args, GRPOConfig) rollout_alloc = ModelAllocation.from_str(config.rollout.backend, name="rollout") # 根据配置初始化调度器 if config.scheduler.type == "local": scheduler = LocalScheduler(exp_config=config) elif config.scheduler.type == "ray": scheduler = RayScheduler(exp_config=config) elif config.scheduler.type == "slurm": scheduler = SlurmScheduler(exp_config=config) # 选择推理引擎并构建服务器参数 if rollout_alloc.backend == "sglang": engine_cls = RemoteSGLangEngine server_args = SGLangConfig.build_args( sglang_config=config.sglang, tp_size=rollout_alloc.parallel.tp_size, base_gpu_id=0, ) elif rollout_alloc.backend == "vllm": engine_cls = RemotevLLMEngine server_args = vLLMConfig.build_args( vllm_config=config.vllm, tp_size=rollout_alloc.parallel.tp_size, pp_size=rollout_alloc.parallel.pp_size, ) # 创建控制器并初始化 eval_rollout = engine_cls.as_controller(config.rollout, scheduler) eval_rollout.initialize( role="eval-rollout", server_args=server_args, ) # 定义工作流及其配置 workflow = "areal.workflow.rlvr.RLVRWorkflow" workflow_kwargs = dict( reward_fn="areal.reward.gsm8k.gsm8k_reward_fn", gconfig=config.gconfig, tokenizer=config.tokenizer_path, enable_thinking=False, ) # 提交评估任务 cnt = 0 for data in valid_dataloader: for item in data: eval_rollout.submit( item, workflow=workflow, workflow_kwargs=workflow_kwargs, group_size=config.gconfig.n_samples, ) cnt += 1 # 等待完成并收集结果 eval_rollout.wait(cnt, timeout=None) eval_stats = eval_rollout.export_stats()

对照仓库源码,这个流程可以拆解为六个环节:

  1. 配置加载load_expr_config(args, GRPOConfig)读取 YAML 配置并支持命令行覆盖(如scheduler.type=ray)。
  2. 分配解析ModelAllocation.from_str(config.rollout.backend)sglang:d4p1t1这类字符串解析为后端 + 并行策略。
  3. 调度器选择:三个分支对应LocalSchedulerRaySchedulerSlurmScheduler,未知类型抛出ValueError
  4. 引擎初始化initialize(role="eval-rollout", server_args=server_args)在 areal/infra/controller/rollout_controller.py 中会按instance_size = tp_size × pp_size放大 worker 资源规格(CPU/内存/GPU),然后以replicas=dp_size创建Job提交给调度器,即"每个数据并行副本对应一个推理实例"。
  5. 任务提交submit(item, workflow=..., workflow_kwargs=..., group_size=config.gconfig.n_samples)封装为_RemoteRolloutTaskInput(含is_eval等标记)交给 dispatcher;group_size表示每个样本生成的候选数。
  6. 等待与导出wait(cnt)阻塞直至收到cnt个结果;export_stats()通过_collective_rpc收集所有 worker 的统计信息并调用_merge_worker_stats合并(areal/infra/controller/rollout_controller.py),返回dict[str, float]形式的指标。

一个必须注意的实现细节:脚本第 48 行config.rollout.max_head_offpolicyness = int(1e12)。这是评估与训练的关键区别——训练时该参数用于控制策略的"离策略度"上限(如训练配置中的max_head_offpolicyness: 2),而评估时我们不希望丢弃任何样本,因此设为极大值以禁用过滤,保证每个提交的评估任务都被执行并纳入统计。

工作流与奖励函数

评估默认复用areal.workflow.rlvr.RLVRWorkflow(areal/workflow/rlvr.py)。该工作流的核心逻辑在arun_episode中:先用apply_chat_template把消息列表编码为输入 token(enable_thinking=False表示不启用推理 token),构造ModelRequest提交生成,再通过AsyncRewardWrapper异步计算奖励,最后把input_ids / logprobs / rewards / is_truncated等组织成带 batch 维的张量返回。

奖励函数以字符串路径"areal.reward.gsm8k.gsm8k_reward_fn"传入,由import_from_string动态加载(areal/utils/dynamic_import.py)。其实现位于 areal/reward/gsm8k.py:

def gsm8k_reward_fn(prompt, completions, prompt_ids, completion_ids, answer, **kwargs) -> float: try: worker = get_math_verify_worker() return worker.verify(str(completions), str(answer)) except Exception: logger.warning("Exception in gsm8k_reward_fn", exc_info=True) return 0.0

即对生成的文本与标准答案做数学验证,异常时安全返回 0.0(不因单条失败中断整个评估)。

配置:复用训练配置,仅需推理相关字段

评估完全复用训练配置结构——examples/math/gsm8k_grpo.yaml可以直接配合评估脚本使用,只需关注如下字段:

experiment_name: gsm8k-eval trial_name: eval0 seed: 1 scheduler: type: local # 或 'ray', 'slurm' rollout: backend: "sglang:d4p1t1" # 仅推理分配 max_concurrent_rollouts: 256 # max_head_offpolicyness 在内部设置为 1e12 用于评估 gconfig: n_samples: 8 temperature: 1.0 max_new_tokens: 1024 actor: path: Qwen/Qwen2.5-1.5B-Instruct dtype: bfloat16 scheduling_spec: - task_type: worker port_count: 2 gpu: 1 cmd: python3 -m areal.infra.rpc.rpc_server valid_dataset: name: gsm8k split: test batch_size: 32

各字段的语义与注意事项:

  • experiment_name/trial_name/seed:实验标识与随机种子,seed会通过 areal/utils/seeding.py 的set_random_seed(..., key="eval")生效,保证评估可复现。
  • scheduler.typelocal(单机)/ray(Ray 集群)/slurm(Slurm 集群)。
  • rollout.backend:推理侧的资源分配,评估场景下只配置推理分配(如sglang:d4p1t1),不需要训练分配。max_concurrent_rollouts控制并发上限;注释明确提示max_head_offpolicyness会在评估脚本内部被设为1e12(examples/math/gsm8k_eval.py)。
  • gconfig:生成超参数。n_samples是每个测试样本的候选生成数(影响group_size),temperature采样温度(评估可用较低温度或贪心,训练配置默认1.0),max_new_tokens限制生成长度。
  • actor.path:待评估模型,可覆盖为任意训练好的检查点目录。注意评估时不会用到actor的训练参数(优化器、KL 等),但保留该节是因为其中的scheduling_spec定义了 worker 进程(rpc_server)的资源与启动命令,rollout.scheduling_spec默认从actor.scheduling_spec继承。
  • valid_dataset:评估数据集配置,与训练数据集解耦,独立指定split: testbatch_size。真实脚本中通过get_custom_dataset(split="test", dataset_config=config.valid_dataset, ...)加载,并用create_dataloader构建 dataloader(examples/math/gsm8k_eval.py)。
  • tokenizer_path:在gsm8k_grpo.yaml中默认为${actor.path},评估脚本用其加载 tokenizer。

记录结果:用 tabulate_stats 格式化指标

评估完成后,export_stats()返回所有 worker 汇总的指标字典(键如rewardrollout/...等),推荐用tabulate_stats格式化为表格输出:

from areal.utils.printing import tabulate_stats eval_stats = eval_rollout.export_stats() logger.info(f"Evaluation Results: {tabulate_stats(eval_stats)}")

tabulate_stats(areal/utils/printing.py)把dict[str, float]按列优先重排成 4 列的表格,默认使用科学计数法(floatfmt=".4e")与fancy_grid表格样式,适合直接写入日志。这也是仓库评估脚本的标准收尾方式——examples/math/gsm8k_eval.py 在wait之后导出并打印指标,并在finally中调用eval_rollout.destroy()释放资源。

自定义评估工作流

评估的灵活性体现在工作流可替换

  • 复用训练工作流:直接沿用训练时的工作流类(如RLVRWorkflow),只需把reward_fn换成合适的打分函数,即可实现"用训练时的评估逻辑做评测"。
  • 创建自定义工作流:实现自己的RolloutWorkflow,重写arun_episode定义"输入 → 生成 → 打分"的完整逻辑,然后以字符串路径传入submitworkflow参数即可。若你的评估涉及智能体工具调用、多轮交互,可参考 智能体 RL 教程 的代理(proxy)方案;关于RolloutWorkflow接口与arun_episode契约的详细约定,见 自定义:Rollout 工作流。

更进一步的扩展方向:

  • 分布式实验编排:结合 Ray 或 Slurm 调度器(docs/zh/tutorial/gsm8k_grpo.md),把"训练 + 评估"组织进同一套集群资源体系;
  • 大型 MoE 模型评估:对于千亿级 MoE 模型,评估同样可以复用 Megatron 推理的并行配置,参见 大型 MoE 训练 中的推理相关章节;
  • 完整评估管道:AReaL 负责分布式推理与生成,数据集切分、指标聚合(如 accuracy 均值)由你的脚本在export_stats之外自行完成——评估脚本的 dataloader 循环与cnt计数正是为这一层预留的钩子。

小结

AReaL 的评估方案可以概括为三点:

  1. 零训练组件:以engine_cls.as_controller(config.rollout, scheduler)构造单一控制器,调度器创建纯推理 worker,max_head_offpolicyness设为1e12以禁用离策略过滤,确保全部样本参与统计;
  2. 配置与工作流完全复用:同一份gsm8k_grpo.yaml既可训练也可评估,sglang:dNpT资源分配语法让你在一行命令内从单机扩到 Ray/Slurm 集群;
  3. 边界清晰、可自由组合:AReaL 提供分布式推理与生成打分(工作流 + 奖励函数),指标计算与第三方评估框架对接完全由你掌控,而 HuggingFace 兼容检查点保证了与外部评估生态的即插即用。

【免费下载链接】AReaLThe RL Bridge for LLM-based Agent Applications. Made Simple & Flexible.项目地址: https://gitcode.com/GitHub_Trending/are/AReaL

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询