【免费下载链接】UniRL
UniRL is a Framework for Unified Multimodal Model Reinforcement Learning
UniRL 是一个统一多模态模型强化学习框架,其子项目unirl-reward-service是一个基于FastAPI + Ray的多奖励模型面板:一个 HTTP 网关背后挂着若干奖励模型(HPSv2、CLIP、PickScore、ImageReward、GenEval2 等),每个模型独占若干 GPU,一次POST /score调用即可并行拿到多个奖励分。本文带你完整走一遍:理解架构 → 单机 3 步部署 → 多机 Ray 集群扩容 → 训练任务对接 → 扩展新打分器,新手也能照着跑起来。
📌 什么是 UniRL 奖励服务?
在强化学习训练中,奖励模型负责把生成结果(图片/视频/文本)变成一个标量信号。UniRL 提供两种取用方式:
- local(进程内):每个后端只挂一个打分器,简单直接;
- remote(HTTP 面板):
RewardService通过POST /score调用远端服务,请求里用required_rewards声明需要哪几个模型,服务端支持weighted_sum / mean / min / max加权聚合成一个奖励,并返回每个模型的明细。
把奖励模型集中到独立机器的面板服务上,好处很明显:训练机的 GPU 全部留给 rollout 和训练,奖励模型可以跨团队复用、独立扩缩容、独立升级依赖。
🏗️ 架构拆解:FastAPI 网关 + Ray WorkerGroup
整条调用链非常简洁:
Client ──HTTP──▶ FastAPI Gateway ──Ray actor call──▶ WorkerGroup(reward=X) ├── actor_0 (独占 GPU k) └── actor_1 (独占 GPU k+1)每个奖励对应一个WorkerGroup,组内是若干可配置的 Ray actor(replicas);路由按required_rewards把请求分桶(bucket)后,对每个奖励只发起一次批量 Ray 调用,组内轮询(round-robin)选 actor。
| 组件 | 源码位置 | 职责 |
|---|---|---|
| HTTP 网关 | server.py | /score、/health、/rewards端点,分桶/派发/聚合 |
| 配置解析 | config.py | YAML → dataclass,校验num_replicas≥1、num_gpus≥0、名字唯一 |
| WorkerPool | pool.py | Ray 运行时生命周期 + 组注册 + 按名派发 |
| WorkerGroup | group.py | N 个 actor + 轮询调度 |
| ScorerActor | actor.py | @ray.remote薄壳,在 GPU 进程内构建模型 |
| BaseScorer | base.py | 抽象契约:score(items) -> list[dict[str, float]] |
| Registry | registry.py | register(name, cls)打分器注册表 |
服务对外承诺三重隔离(详见 docs/ARCHITECTURE.md):
- 资源隔离:GPU 以整数资源分配(
num_gpus=N),同一时刻一块卡只属于一个奖励组; - 错误隔离:某个奖励 OOM/超时/崩溃,只把异常写进
errors[i][reward],同批其他奖励照常返回,不会整批 500; - 依赖隔离:每个打分器有独立的
envs/<scorer>.txt,Rayruntime_env首次启动时自动建隔离 venv——imagereward 和 vLLM 可以使用不同版本的 transformers 互不冲突。
🚀 单机部署:3 步拉起奖励面板
第一步:准备基础环境并安装
前置条件:已激活Python 3.12环境,且预装了 torch + nccl(这些包太大、强绑定硬件,官方不代装)。然后在 unirl-reward-service/ 目录下执行:
./install.sh # 只装基础层:ray、fastapi、uvicorn、pillow 等注意:各打分器的依赖(transformers、vllm、hpsv2……)不在这一步安装,它们由 Ray 在 actor 首次启动时按envs/*.txt自动装进隔离 venv,所以首次启动较慢(vLLM 类约 10 分钟),之后复用缓存几秒即可就绪。
第二步:编辑部署配置
cp configs/service.example.yaml configs/service.yaml打开 configs/service.example.yaml,核心就改三处:
- 打开/注释你需要的奖励条目(默认启用 8 个,共 10 张 GPU 预算);
- 把每个条目的
weights_path填成你本地的权重目录; - 按机器卡数调整
num_gpus/num_replicas。总预算 = Σ(num_gpus × num_replicas),不要超过本机 GPU 数,超预算时ray.init()能成功,但 actor 会卡在资源分配上,可从/health看到某组 ping 不回来。
常用字段速查:max_concurrency(单 actor 并发度,纯前向的 transformers 打分器可放心调大,vLLM 打分器保持 1)、score_timeout_s(单奖励超时,默认 120s)、vLLM 类参数(tensor_parallel_size、gpu_memory_utilization、dtype等直接透传给vllm.LLM)。
第三步:启动并做健康检查
python -m reward_service --config configs/service.yaml # 等价入口:unirl-reward-service --config configs/service.yaml启动后先打两个只读端点确认状态:
| 端点 | 方法 | 说明 |
|---|---|---|
/health | GET | {"status": "ok", "rewards": {名字: [各副本状态]}} |
/rewards | GET | 已就绪的奖励列表 |
/score | POST | 批量打分主入口 |
📞 调用面板:批量打分与错误隔离
最推荐的调用方式是 Python SDK(client.py),请求列表想多长就多长:
from reward_service.client import RewardClient, RewardRequest client = RewardClient("http://localhost:8080") # 跨机时换成服务 IP scores = client.score([ RewardRequest(history=[("a cute dog", Image.open("dog.png"))], required_rewards=["hpsv2", "clip"]), RewardRequest(history=[("a cute cat", Image.open("cat.png"))], required_rewards=["hpsv2", "pickscore"]), ]) # -> [{"hpsv2": {"hpsv2": 0.27}, "clip": {"clip": 0.31}}, ...]RLHF 里最常见的模式是"一个 prompt × N 张候选图":N 个请求放进一次client.score([...]),服务端会把需要同一奖励的 N 个请求打包成单次 actor 调用,效率远高于逐条请求。
响应中results[i]与errors[i]和requests[i]一一对应:
{ "results": [ {"hpsv2": {"hpsv2": 0.27}} ], "errors": [ {"clip": "RayActorError(...)"} ] }即clip失败了,只在该请求里记录异常,hpsv2照常出分——客户端要同时读两个字段,否则失败的奖励会被静默漏掉。跨机调用只需把 URL 换成http://<server-ip>:8080,也可以不装 SDK 直接用裸requests手写 HTTP(参考 scripts/remote_client_zero_deps.py)。
🌍 多机扩容:加入 Ray 集群只需改一行 YAML
单机的 GPU 预算不够时,可以把多个主机组成 Ray 集群,服务代码零改动,参考 configs/service.cluster.example.yaml(2 节点 × 8 GPU = 16 卡预算):
export NODE_IP_LIST="10.0.0.1:8 10.0.0.2:8" # 第一个节点是 head bash scripts/ray_start.sh # pdsh 拉起多机 Ray 集群 python -m reward_service --config configs/service.cluster.example.yaml # 拆集群: bash scripts/ray_stop.shscripts/ray_start.sh 启动完成会打印 head 地址,把它填进 YAML 即可:
cluster: ray_address: <head-ip>:6379 # 服务本身跑在 head 节点上时用 auto多机部署的两个调度要点(配置里都有注释):
scheduling: pack:vLLM 张量并行(如 GenEval2 的 TP=2)必须两卡落在同一节点,跨以太网做 TP 会毁掉吞吐;scheduling: spread:轻量的 1 卡奖励开num_replicas: 2+ spread,让副本摊到不同节点,兼顾负载均衡与故障隔离。
另外注意 YAML 中奖励条目的书写顺序:多卡 actor 放前面,先占好连续卡槽,避免被 1 卡副本把集群切碎。
🔌 对接 UniRL 训练任务:一行环境变量搞定
远程面板最大的价值是让训练机的 GPU 全部留给 rollout 与训练。训练侧由 unirl/reward/remote.py 的RemoteRewardBackend负责对接,官方示例 recipe 见 examples/diffusion/sd3/sd3_nft_reward_service.yaml:
reward: _target_: unirl.reward.service.RewardService backend: _target_: unirl.reward.remote.RemoteRewardBackend config: _target_: unirl.reward.remote.RemoteRewardSpec base_url: ${oc.env:REWARD_SERVICE_URL,http://localhost:8080} required_rewards: [hpsv2, pickscore, unified_reward, imagereward, hpsv3] reward_weights: {hpsv2: 0.2, pickscore: 0.2, unified_reward: 0.2, imagereward: 0.2, hpsv3: 0.2} batch_size: 8 aggregation_method: weighted_sum # 支持 weighted_sum / mean / min / max训练时只需:
REWARD_SERVICE_URL=http://10.0.0.5:8080 python -m unirl.train_diffusion \ --config-dir examples/diffusion/sd3 --config-name sd3_nft_reward_service一次 HTTP 往返就把 5 个奖励模型的分全拿回来,按权重聚合成训练用的单一奖励。
🧩 扩展指南:4 步接入一个新打分器
框架的扩展点设计得很克制:网关、workers、schemas完全不用动,新打分器会被注册表自动发现。以新增my_reward为例:
- 在 reward_service/scorers/ 下新建
my_scorer.py,实现BaseScorer的score(items) -> list[dict[str, float]],模块末尾register("my_reward", MyScorer);重依赖一律放在__init__/方法内,不要在模块顶层 import; - 在 registry.py 的
SCORER_MODULES映射里加一行"my_reward": "reward_service.scorers.my_scorer"; - 写一份
envs/my_reward.txt声明该打分器的 pip 依赖(版本冲突的隔离层); - 在
configs/service.example.yaml加对应条目(name/scorer/runtime_env/num_gpus/num_replicas/params),并补一个测试。
已有子指标想扩新指标?只需扩sub_metric_names元组并在score()返回的 dict 里加键——响应 schema 是 `dict[str, float],天然多键,协议不变。
⚠️一个硬边界:Rayruntime_env的隔离只到Python 包级,不能换 Python 解释器或 torch 底座(worker 必须与集群同 Python 小版本)。像geneval(mmdet/mmcv 栈需要 Python 3.10)这种跑不进本集群(py3.12+)的打分器,官方给了旁路方案:在独立 Python 3.10 主机上用 configs/geneval_service.yaml 单独起一份服务再互调。
⚡ 进阶技巧:MPS 共享 GPU 与压测
- CUDA MPS 省卡:CLIP、PickScore、HPSv2、ImageReward 这类轻量打分器可以各占半张卡(
num_gpus: 0.5+mps:块),服务会在 Ray 初始化前自动拉起本地 MPS 守护进程,示例见 configs/service.mps.example.yaml:
- name: clip num_gpus: 0.5 mps: active_thread_percentage: 100 # H20 + 535 驱动下 fp16/bf16 打分器保持 100 device_memory_limit: 20GiB- 并发压测:服务起来后用 scripts/bench_concurrent.py 输出 p50/p90/p99 延迟与吞吐;
--sweep一次跑多个并发档,--per-reward-isolated单独测每个奖励的独立延迟(混批时 HTTP 只等最慢的那个返回):
python3 scripts/bench_concurrent.py --url http://10.0.0.5:8080 \ --concurrency 200 --total 1000 --rewards clip- 依赖自检:
python scripts/check_venvs.py --standalone可核查各打分器 venv 的安装与隔离状态。
📚 关键文件速查表
| 资料 | 相对路径 |
|---|---|
| 服务总 README(安装/启动/API 全表) | unirl-reward-service/README.md |
| 架构与数据流设计文档 | unirl-reward-service/docs/ARCHITECTURE.md |
| 单机示例配置(8 奖励 10 卡) | unirl-reward-service/configs/service.example.yaml |
| 多机集群示例配置(2×8 卡) | unirl-reward-service/configs/service.cluster.example.yaml |
| MPS 共享 GPU 示例配置 | unirl-reward-service/configs/service.mps.example.yaml |
| 打分器注册表 | unirl-reward-service/reward_service/scorers/registry.py |
| Python SDK(RewardClient) | unirl-reward-service/reward_service/client.py |
| 训练侧远程后端 | unirl/reward/remote.py |
| 远程奖励训练 recipe 示例 | examples/diffusion/sd3/sd3_nft_reward_service.yaml |
| 多机 Ray 启停脚本 | unirl-reward-service/scripts/ray_start.sh |
✅ 小结
UniRL 奖励服务的精髓是"一个 FastAPI 网关 + N 个独占 GPU 的 Ray 奖励组 + 三重隔离":单机三步即可上线一个多奖励面板,多机扩容只靠一行ray_address配置;训练侧一个REWARD_SERVICE_URL环境变量就能接入。想加新打分器,只需写一个模块 + 一行注册 + 一份 env 清单,网关与调度层自动生效。建议按本文顺序先跑通service.example.yaml,再逐步尝试 MPS 省卡与多机集群,最后扩展自己的奖励模型。
【免费下载链接】UniRL
UniRL is a Framework for Unified Multimodal Model Reinforcement Learning
相关推荐
G-Helper 屏幕色彩修复实操指南:华硕笔记本发白、偏色一次解决
G Helper 屏幕色彩修复实操指南:华硕笔记本发白、偏色一次解决 G Helper 是华硕游戏本上 Armoury Crate 的轻量替代方案,它的屏幕色彩
桌面应用KernelSU 模式切换避坑指南:GKI 与 LKM 3 步选对,内核更新不翻车
KernelSU 模式切换避坑指南:GKI 与 LKM 3 步选对,内核更新不翻车 这篇文章解决三件事:KernelSU 的 GKI 与 LKM 怎么选、模式切
操作系统内核驱动系统编程移动开发vanilla-extract的社区贡献奖励指南:奖励指南
vanilla extract的社区贡献奖励指南:奖励指南 vanilla extract是一个基于TypeScript的零运行时样式表解决方案(Zero ru
前端开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考