初读 Miles:RL 后训练,难在采样和训练之间

初读 Miles:RL 后训练,难在采样和训练之间

前段时间我在研究 SGLang,后来顺手点进了 RadixArk 的 GitHub 主页。也是在那里,我看到了刚发布没几天的 Miles v0.1。

一开始吸引我的,其实不是 Miles 支持多少种 RL 算法,而是一个更基础的问题:

为什么一个 RL 训练框架,会把 SGLang 这样的推理引擎放在这么重要的位置?

我以前会很自然地把训练和推理分成两个阶段:先把模型训练好,再拿去做推理。可是在 RL 后训练里,事情不是这么走的。模型要先生成回答,系统根据回答算奖励,再拿这些数据更新模型。模型更新以后,又要回去生成下一批回答。

也就是说,推理并不是训练结束以后才发生的事。它本身就在训练循环里。

Miles 做的,就是把这个循环真正跑起来。它基于 slime 开发,SGLang 负责 rollout(采样生成),Megatron-LM 负责训练,Ray 则用来管理进程和 GPU 资源。项目采用 Apache-2.0 许可证。

我现在还没有合适的硬件去跑完整训练,所以这篇不是实测,也不会讨论 Miles 到底能快多少。主要想记一下我读文档和代码时觉得比较有意思的几个地方,以及它们为什么让我重新理解了 RL 训练系统。

那几行 RL 代码,真正难的是中间怎么接

把一轮 RL 训练写成伪代码,大概就是下面这样:

python 复制代码
trajectories = rollout(policy, prompts)
scores = reward(trajectories)
policy = train(policy, trajectories, scores)
sync(policy, rollout_workers)

这里的 policy,暂时可以理解成正在训练的模型。rollout 则是让模型根据提示词实际生成回答,也就是为后面的训练采样。

只看这几行,好像没有什么特别复杂的地方:生成、打分、训练、同步,循环往复。

但一放到多机多卡环境里,问题就来了。

采样生成的数据怎么交给训练端?模型刚刚更新完,负责生成的进程怎么拿到新权重?一批请求里有的很快结束,有的迟迟跑不完,其他 GPU 要不要一直等?训练端收到的 token,还是不是推理端当时真正生成的那一串?

这些问题在算法公式里基本看不到,却会直接决定这套训练能不能高效、稳定地跑起来。Miles 的大部分设计,也都围绕着这些"步骤之间"的问题展开。

同一个模型,在系统里要干两份完全不同的活

模型做采样时,目标是尽量快地生成更多回答。推理引擎要处理并发请求、维护 KV Cache,还要尽量提高 GPU 利用率。这部分 Miles 交给了 SGLang。

到了训练阶段,重点又变成了反向传播、梯度和优化器状态。这部分主要由 Megatron-LM 负责。

从概念上看,两边当然是同一个策略模型。但在系统里,它们是两套为了不同任务优化的运行形态。训练端每更新一次参数,推理端手里的模型就可能变成旧版本。

读到这里,我才意识到,"把训练好的权重同步给推理端"听起来只是一句话,真正做起来却会碰到显存、通信、进程协调和模型版本等一连串问题。

Miles 支持两种主要的 GPU 布局。

一种是共置模式(colocated),也就是采样和训练共用同一组 GPU。好处是不用准备两套机器,但两边不能同时把显存占满,只能轮流工作。采样结束后让出资源,训练接着跑;训练做完,再切回采样。

另一种是分离模式(disaggregated),采样和训练分别使用不同的 GPU。这样两边可以同时工作,也是全异步模式的前提。代价是权重要在两组 GPU 之间传输;如果两边不在同一台机器上,请求和采样数据还会经过网络。

Ray 在外面负责把这些组件组织起来,包括训练进程、SGLang 服务、路由器和采样管理器。普通的生成请求走 SGLang 的 HTTP 服务和 Miles 路由器,模型权重则有单独的同步方式。

模型一更新,采样端马上就落后了

RL 训练里有一个绕不开的问题:用来生成数据的模型,最好不要和当前正在训练的模型差得太远。

最容易想到的同步方法,是训练端保存一个检查点(checkpoint),再让推理端重新加载。偶尔做一次问题不大,但如果训练过程中频繁这么干,大模型的保存、读取和重新加载很快就会变成瓶颈。

Miles 针对不同的部署方式准备了不同的权重同步路径。

在共置模式下,权重可以通过 CUDA IPC 在设备侧传递。训练和采样分开部署时,默认可以使用 NCCL 广播;文档里还提供了基于 Mooncake 的 P2P RDMA,以及通过共享存储传输权重差量的 disk-delta 方案。

不过,权重也不是说换就能换。SGLang 里可能还有请求生成到一半,KV Cache 也还在用。系统需要在预先设置好的同步点暂停生成,更新权重,再继续处理请求。

所以权重多久同步一次,不只是一个部署参数。

同步得太频繁,通信和暂停本身会影响吞吐;隔得太久,采样端用的模型又会越来越旧。看到这里,我开始理解为什么这类框架会把权重同步当成核心能力,而不是一个顺手补上的功能。

异步能少等一会儿,但也会带来新问题

最容易理解的是同步训练:先生成完整的一批数据,再训练一步,然后同步权重,开始下一轮。

这样做的好处是流程清楚。哪批数据由哪个版本的模型生成,也比较容易对上。

但生成任务有个很现实的问题:每条回答的长度不一样。有的请求很快遇到 EOS,有的会一直生成到上限。只要这一批里还有几个慢请求没结束,其他已经空下来的资源就只能等。

Miles 的全异步模式不再死守这条批次边界。采样进程持续生成,完成的数据先放进一个大小有限的缓冲区;训练端一边从中取数据,一边更新参数。这样采样和训练就能重叠起来。

当然,它不是打开一个开关就能白拿性能。

一条采样轨迹放进缓冲区以后,可能不会立刻被训练端取走。等轮到它时,模型已经更新了几次。于是训练端正在使用的数据,可能来自几个版本以前的策略。

Miles 会记录每轮生成对应的权重版本,也允许设置样本的最大陈旧度(staleness),用来丢掉过旧的数据。这个过滤默认并不会自动开启,需要使用者自己确定阈值。

另外,全异步模式要求采样和训练使用不同的 GPU。到了设置好的权重同步点,生成还是会暂停,等新权重换好以后再继续。

所以异步解决的是"大家互相干等"的问题,但同时也把模型版本和样本新鲜度带进了训练过程。缓冲区设多大、多久同步一次、旧到什么程度的数据还能用,这些已经不只是系统调优,也可能影响最后的训练效果。

如果是第一次跑,我应该还是会从同步模式开始。速度慢一点,但出问题时至少更容易知道是哪一步出了问题。

人看起来是同一句话,对模型来说却不一定

Miles 里另一个让我印象很深的设计叫 TITO,也就是 token-in-token-out:推理端输入和输出都保留为 token。

最省事的数据传递方式,似乎是让推理端把 token 解码成文字,再把文字交给训练端重新分词:

python 复制代码
reconstructed_ids = encode(decode(token_ids))

# 这次往返不保证保留策略模型真正采样的动作
assert reconstructed_ids == token_ids  # 可能失败

问题在于,解码再编码以后,不一定还能得到原来的 token 序列。分词器可能把同一段文字切成另一种形式,解码时也可能移除特殊 token,或者对文本做一些规范化处理。

多轮智能体的情况更麻烦。每次调用工具以后,系统可能重新套用对话模板,也可能把工具调用解析以后再拼回 JSON。哪怕人眼看着内容没变,空格、字段顺序或边界位置的变化,都可能让下一轮输入变成另一串 token。

为什么这件事值得单独处理?

因为采样生成的 token ID,就是策略当时实际选出的动作。训练端如果重新构造出另一条 token 序列,优化的可能已经不是产生那份采样对数概率的动作了。文字看起来一样,不代表这还是同一条训练数据。

在 Miles 的文本智能体会话里,TITO 会以 SGLang 实际生成的 token 为准,并保存对应的对数概率和路由信息。到了下一轮,已经存在的 token 前缀会直接复用,只对后来追加的内容重新分词。

我原本会把这种问题归到"分词器的边角细节"里。读完这一段才发现,它其实是在保证一件很基础的事:训练端学到的,必须是推理端当时真正做过的选择。

没有八张数据中心 GPU,还能怎么看这个项目

Miles 目前支持 GRPO、GSPO、PPO、REINFORCE++、SFT 和 OPD,也提供多轮智能体采样、低精度训练和多种模型配置。

不过它显然不是给普通笔记本准备的入门项目。

官方快速入门从一台八卡机器起步,要求 H100、H200 或 B 系列 GPU,至少 500 GB 可用磁盘空间,以及能够使用 GPU 的 Docker 环境。示例跑的是 Qwen3-4B:四个双卡 SGLang 推理实例和 Megatron 训练端共用这八张卡,轮流工作。

如果暂时没有这样的硬件,我觉得仍然可以先从代码和配置入手。比如看它怎样启动各个组件、怎样分配 GPU、一次采样保存了哪些字段,以及权重同步从哪里触发。这些内容本身就是一份挺完整的 RL 训练系统案例。

但吞吐、稳定性和扩展能力就不能只靠读文档下结论了。那些还是得等真正跑过以后再聊。

写在最后

在看 Miles 之前,我对 RL 后训练框架的关注点还是偏算法:支持哪些损失函数、奖励怎么计算、有没有 GRPO。

看完它的整体设计以后,我更关心的是另外一些问题:采样和训练怎么同时使用同一个模型?新权重怎样送回推理端?异步以后,训练数据到底旧了多少?传给训练端的 token,还是不是模型当时真正生成的 token?

这些东西单独看都不像"RL 算法",但少了任何一个,前面那几行简单的训练伪代码都很难在真实集群里顺利跑起来。

这也是 Miles 最吸引我的地方。它让我看到,RL 后训练的难点不只在每一步怎么算,还在这些步骤之间到底怎么接。

接下来我想挑一个具体入口继续往下读。TITO 会是一个不错的方向,权重同步也很值得单独拆开。等有条件跑起官方示例以后,再补一篇真正的上手记录。

参考资料

相关推荐
txg66644 分钟前
LLM 驱动漏洞传播验证:TransferFuzz-Pro 如何自动调试“继承“来的安全债
人工智能·深度学习·安全
shujudang1 小时前
从数据闭环到 Agent 协同:企业营销自动化如何演进为智能运营?
大数据·运维·人工智能·数据分析·自动化
吉安特尔雅1 小时前
2026行业实测|主流AI获客系统功能拆解与选型指南(含剪流全矩阵深度分析)
人工智能·剪流geo·剪流·ai获客哪家靠谱
动恰客流统计1 小时前
ReID边缘计算视觉客流统计:无网户外场景的数字化落地路径
大数据·人工智能
FL16238631291 小时前
仙人掌品种类型检测数据集VOC+YOLO格式1991张10类别
人工智能·yolo·机器学习
Sunlly1 小时前
OpenClaw 的 Elevated 到底给了 Agent 什么权限
人工智能
小白说大模型1 小时前
Codex 实战:用 AI 写运维脚本
大数据·运维·网络·人工智能·机器学习·prompt
用户73499134716531 小时前
我把思考外包给了AI,三个月后我成了自己项目的文盲
人工智能
Rocky Ding*1 小时前
【三年面试五年模拟】2026-08-18_哔哩哔哩_AI应用岗Agent开发一面面经(含完整答案)
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·ai agent