トークンを流し続けよう:16のオープンソースRLライブラリから得た教訓
TL;DR – Hugging Face は 16 のオープンソース強化学習(RL)ライブラリを調査し、すべての成功した非同期 RL システムは推論と学習を別々の GPU プールに分離し、ロールアウトバッファを使用し、モデル重みを非同期にプッシュしていることを発見しました。Ray が支配的なオーケストレーションフレームワークであり、NCCL ブロードキャストが一般的な重み同期手法です。また、LoRA と Mixture‑of‑Experts(MoE)トレーニングのサポートはまだ乏しいです。
1. なぜ非同期 RL が重要か
非同期 RL は、同期トレーニング時に大規模言語モデル(LLM)の生成ボトルネックが壁時計時間の最大 60 % までアイドルになる問題を解消します。 同期パイプラインでは、32 K トークンのロールアウトを 32 ビリオンパラメータモデルで実行すると数時間かかり、その間勾配更新に割り当てられた GPU がアイドル状態になります。推論と学習を別々の GPU プールに分離し、ロールアウトバッファで接続することで、トレーナーが以前に生成されたデータを消費している間も生成を継続でき、GPU 利用率が劇的に向上します。
2. 調査概要
Hugging Face は 16 のオープンソース非同期 RL ライブラリ(AReaL、ART、Atropos、MILES、NeMo‑RL、OAT、open‑instruct、PipelineRL、PRIME‑RL、ROLL、SkyRL、SLIME、TorchForge、Tunix、verl、verifiers‑rl)を調べ、7 つの独立した軸で評価しました。
- オーケストレーション & 並行性プリミティブ – 分散コンポーネントの調整方法。
- ロールアウトバッファ設計 – 推論から学習へ生成サンプルを運ぶデータ構造。
- 重み同期プロトコル – 更新されたパラメータを推論プールにプッシュする方法。
- 古さ(Staleness)管理 – オフポリシーロールアウトの取り扱い戦略。
- 部分ロールアウト処理 – 重みが変わったときに飛行中の生成がどうなるか。
- LoRA トレーニングサポート – アダプタのみのパラメータを効率的に学習・同期できるか。
- 分散トレーニングバックエンド & 並列化 – 並列化戦略(FSDP、Megatron、DeepSpeed、JAX など)と MoE のサポート。
完全な比較表は元ブログ記事にあります。以下のセクションは各軸の主要な所見を要約したものです。
3. オーケストレーション & 並行性プリミティブ
| オーケストレーションタイプ | 内容 | 使用ライブラリ |
|---|---|---|
| 分散アクターモデル(Ray) | 状態を持つアクターと非同期 RPC、オブジェクトストア、組み込みの障害耐性。 | AReaL、verl、SkyRL、NeMo‑RL、SLIME、MILES、ROLL、OAT、open‑instruct など |
| ネイティブ Python 並行性 | スレッド、asyncio、multiprocessing;外部ランタイム不要。 |
verifiers‑rl、PipelineRL(プール内)、ART、AReaL(asyncio ベース) |
| Pub/Sub メッセージバス | Redis ストリームや追記専用ファイルでデカップルされた生産者/消費者。 | PipelineRL(プール間)、SLIME(非同期モード) |
| HTTP マイクロサービス | REST で通信する独立サービス。 | Atropos |
所見: Ray が 8/16 のライブラリで使用されており、支配的です。そのアクターモデルは RL の異種コンポーネント(推論サーバ、トレーナ、報酬モデル、環境プール)にマッチし、スケジューリング、障害耐性、Ray オブジェクトストアによるゼロコピー転送を自動で提供します。ただし、Ray は重いランタイムを追加するため、規模が小さいデプロイではネイティブ Python や Pub/Sub が軽量代替として検討されます。
4. ロールアウトバッファ設計
| バッファパターン | 深さ(最大同時バッチ数) | ライブラリ | 備考 |
|---|---|---|---|
| バッファなし(同期) | 0 | TRL(現行)、ART(全取得後トレーニング) | 生成と学習が交互に実行;古さは最大だが重なりはなし |
| ダブルバッファ | 1 | verifiers‑rl、SLIME(非同期モード)、MILES、OAT | ちょうど 1 バッチの生成と 1 バッチの学習が重なる;最小の古さ |
| 有界非同期キュー | 2–K | SkyRL、verl、NeMo‑RL、ROLL、PRIME‑RL、TorchForge、Tunix、open‑instruct、AReaL | 複数バッチが同時に飛行;古さはキュー容量で上限付け |
| 無制限 / ストリーム | 無制限 | PipelineRL(Redis ストリーム)、SLIME(完全非同期)、Atropos | 連続生成;古さはバージョンタグや重要度サンプリングで管理必要 |
深いキューはスループットを向上させますが、明示的な古さ管理が必須です(軸 4 参照)。
5. 重み同期プロトコル(軸 3)
プロトコルは レイテンシ と 割り込み粒度 を決定します。
5.1 転送メカニズム
| メカニズム | 典型的レイテンシ | ライブラリ |
|---|---|---|
| NCCL ブロードキャスト | 100–500 ms | 大半(PipelineRL、SkyRL、SLIME、MILES、ROLL、OAT、NeMo‑RL、PRIME‑RL、open‑instruct、AReaL) |
| NCCL + バケッティング | 約 20 ms | verl |
| 共有メモリ / CUDA IPC | 非常に低い | NeMo‑RL、MILES |
| ファイルシステム + HTTP | 中程度(秒) | PRIME‑RL、AReaL、ART |
| HTTP PUT | 高(秒) | verifiers‑rl |
| JAX cross‑mesh | 低い | Tunix |
5.2 割り込み粒度
| 粒度 | 挙動 | ライブラリ |
|---|---|---|
| 停止しない(トークン単位スワップ) | 前方パス間で重みが入れ替わり、生成は中断しない。 | PipelineRL、open‑instruct(オプトイン) |
| HTTP リクエスト単位で中止 | 進行中の HTTP 呼び出しがキャンセルされ、プレフィックスで再試行。 | SkyRL、SLIME |
| ソフトポーズ(飛行中をドレイン) | 新規リクエストはブロック、既存生成は完了してから同期。 | PRIME‑RL、AReaL、open‑instruct(デフォルト)、verl(非同期) |
| バッチ単位 / ブロッキング | 生成と学習が交互に実行、同期は両側をブロック。 | NeMo‑RL、ROLL、OAT、TorchForge、Tunix、verifiers‑rl、Atropos |
所見: 真の 停止しない 重み更新を実現しているのは PipelineRL だけです。他はすべてより粗い境界で一時停止し、短時間ながら古い重みで推論が走ります。
6. 古さ管理(軸 4)
生成と学習が重なるとロールアウトはオフポリシーになります。ライブラリは次の 3 つの独立戦略を取ります。
- サンプル単位のバージョン拒否 –
model_versionが許容ラグを超えるサンプルを破棄。 - 深さ制限 – 同時バッチ数を制限し、構造上最大バージョンギャップを保証。
- 重要度サンプリング(IS)補正 – 古いサンプルを (\frac{\pi_{\theta}(a|s)}{\pi_{\text{old}}(a|s)}) で重み付け(多くはクリップ)。
| ライブラリ | バージョン拒否 | 深さ制限 | IS 補正 |
|---|---|---|---|
| AReaL | ❌ | ✅ | ⚠️(オプション) |
| ART | —(同期) | — | — |
| Atropos | ❌ | ✅ | ❌ |
| MILES | ❌ | ❌ | ✅ |
| NeMo‑RL | ✅ | ❌ | ❌ |
| OAT | ❌ | ❌ | ✅ |
| open‑instruct | ❌ | ✅ | ⚠️(オプション) |
| PipelineRL | ✅ | ❌ | ❌ |
| PRIME‑RL | ✅ | ✅ | ✅ |
| ROLL | ❌ | ❌ | ✅ |
| SkyRL | ❌ | ✅ | ❌ |
| SLIME | ❌ | ❌ | ✅ |
| TorchForge | ✅ | ❌ | ❌ |
| Tunix | ❌ | ✅ | ❌ |
| verl | ❌ | ❌ | ✅ |
| verifiers‑rl | ❌ | ✅ | ❌ |
ハイブリッド手法(例:PRIME‑RL、open‑instruct)は深さ制限とオプションの IS 重み付けを組み合わせ、パイプラインをシンプルに保ちつつロバスト性を確保しています。
7. 部分ロールアウト処理(軸 5)
数万トークンに及ぶ長文ロールアウトは、重み更新が来た時点でまだ生成中であることがあります。主な戦略は次の通りです。
| 戦略 | ライブラリ | 説明 |
|---|---|---|
| 暗黙的継続 | PipelineRL | 中断なし;トークン単位の前方パス間で重みが入れ替わる |
| 中止 + プレフィックスで再試行 | SkyRL、SLIME | 飛行中生成をキャンセルし、部分プレフィックスを新ポリシーで再送信 |
| 明示的保存/再開 | verl(完全非同期) | トークン ID と KV キャッシュを保存し、同期後に保存状態から再開 |
| グループキャンセル | PRIME‑RL | 古いロールアウトグループを破棄し、HTTP リクエスト間で重み同期 |
| ソフトポーズ(ドレイン) | AReaL | 新タスクは停止、既存タスクは完了してから同期 |
| 未対応 | verifiers‑rl、OAT、Atropos、Tunix | すべての飛行中生成が完了してからのみ同期 |
真の 停止しない 挙動を提供しているのは PipelineRL と verl だけで、残りは中止またはドレイン方式です。
8. LoRA トレーニングサポート(軸 6)
LoRA は学習可能パラメータを劇的に削減し、アダプタのみの重み同期 を可能にします。これにより 7 B 以上のモデルでも NCCL 転送時間が数百ミリ秒からサブミリ秒に短縮されます。
| ライブラリ | LoRA 対応? | バックエンド | アダプタのみ同期 |
|---|---|---|---|
| AReaL | ✅ | HF peft(FSDP2/Megatron) |
✅ |
| ART | ✅ | Unsloth / Megatron | ✅ |
| Atropos | ✅ | HF peft |
✅ |
| MILES | ✅ | Megatron‑Bridge | ✅ |
| NeMo‑RL | ✅(カスタム) | DTensor / Megatron | ❌(証拠なし) |
| OAT | ✅ | HF peft |
✅ |
| open‑instruct | ❌(コードはあるが未接続) | — | ❌ |
| PipelineRL | ✅ | HF peft |
❌(全体ブロードキャスト) |
| PRIME‑RL | ✅ | カスタム MultiLoRA | ✅ |
| ROLL | ✅(DeepSpeed のみ) | DeepSpeed | ❌ |
| SkyRL | ✅ | HF peft / Megatron‑Bridge |
✅ |
| SLIME | ❌ | — | ❌ |
| TorchForge | ❌ | — | ❌ |
| Tunix | ✅ | qwix(JAX) | ✅ |
| verl | ✅ | HF peft / Megatron‑Bridge |
✅ |
| verifiers‑rl | ✅ | HF peft + FSDP2 |
✅ |
LoRA のみの同期は割り込みモデルを大幅に緩和します。たとえリクエスト単位で中止しても、転送データが極小なため頻繁な重み更新が可能です。
9. 分散トレーニングバックエンド & 並列化(軸 7)
トレーニングバックエンドはモデルサイズ上限、集合通信パターン、MoE との互換性を決定します。
| ライブラリ | バックエンド | 並列化(DP/TP/PP/EP) | MoE サポート |
|---|---|---|---|
| AReaL | FSDP2、Megatron、Archon | DP、SP、TP、PP、CP、EP | ✅ |
| ART | Unsloth、Megatron | DP、TP、EP | ✅ |
| Atropos | PyTorch native、TRL | DP | ❌ |
| MILES | Megatron、FSDP2 | DP、TP、PP | ✅ |
| NeMo‑RL | DTensor、Megatron | DP、SP、TP、PP、CP、EP | ✅ |
| OAT | DeepSpeed | DP、TP | ❌ |
| open‑instruct | DeepSpeed | DP、SP | ❌ |
| PipelineRL | DeepSpeed | DP、SP | ❌ |
| PRIME‑RL | FSDP2 | DP、TP、CP、EP | ✅ |
| ROLL | DeepSpeed、Megatron、FSDP2 | DP、SP、TP、PP、CP、EP | ✅ |
| SkyRL | FSDP、Megatron‑Bridge | DP、SP、TP、PP、EP | ✅ |
| SLIME | Megatron | DP、TP、PP、SP | ✅ |
| TorchForge | FSDP2(Monarch) | DP、TP、CP | ❌ |
| Tunix | JAX/XLA | DP、TP | ❌ |
| verl | FSDP、Megatron | DP、SP、TP、PP、CP、EP | ✅ |
| verifiers‑rl | DeepSpeed | DP | ❌ |
重要な示唆: MoE トレーニング(エキスパート並列)は Megatron または EP を明示的に扱える FSDP2 系のライブラリ(AReaL、verl、PRIME‑RL、SkyRL、ROLL、NeMo‑RL)でのみサポートされています。DeepSpeed や純粋な ZeRO(EP 未実装)でも MoE チェックポイントはロード可能ですが、すべてのランクにエキスパートが分散されるためスパース性の利点が失われます。
10. 新たな設計圧力
10.1 Critic‑Free アルゴリズム
価値ネットワークを除去(例:GRPO、REINFORCE++)するとメモリが解放され、ロールアウトバッチが大きくなりますが、重み更新頻度が上がります。ポリシードリフトが速くなるため、古さ管理がより重要になります。サンプル単位のバージョンタグ付けと IS 補正が安定学習に必須です。
10.2 プロセス報酬
中間推論ステップを評価するプロセス報酬モデルは、生成と学習の間に非自明な計算ステージを追加します。非同期パイプラインは 報酬アクター をトレーナと並行して走らせる必要があり、PRIME‑RL と NeMo‑RL がこの構成を採用しています。これが無いと報酬計算が新たなボトルネックになります。
10.3 マルチエージェント共進化
マルチエージェント自己対戦はストラガー問題を増幅させ、実効ロールアウト長がエージェント数の積になります。バッファ設計はエピソード全体を単一の原子単位として扱い、古さ戦略はサンプルではなくエピソード単位のバージョン追跡が必要です。
10.4 MoE のトレーニング‑推論ミスマッチ
DeepSeek‑v3.2 で 2 つの構造的ミスマッチが確認されました。
- エキスパートルーティング不整合 – 推論とトレーニングで同一トークンが異なるエキスパートを選択することがある。解決策(“Keep Routing”)は推論サーバがルーティング決定を返し、トレーナがそれを強制すること。
- サンプリングマスク不整合 – 生成時の top‑p/top‑k 切り捨てにより、トレーニングのフルボキャブ前方パスと行動空間が異なる。“Keep Sampling Mask” は切り捨てマスクを記録し、トレーニング時に再適用する。 現在調査対象のライブラリはどれもこの機能を実装しておらず、次世代非同期 RL システムの課題として浮上しています。
10.5 ディスティレーションを非同期 RL として扱う
オンポリシーディスティレーション(学生が生成し教師がスコア付け)は、生成 → スコア → 勾配更新 → 重み同期という非同期パターンと完全に一致します。したがって、すべての設計軸がそのまま適用可能です。汎用的な非同期トレーナはスコアリングステップをハードコーディングせず、プラガブルコンポーネントとして公開すべきです。
11. TRL の非同期トレーナ設計選択
調査結果に基づき、TRL チームは次の具体的決定を行います。
- 軽量オーケストレーション – 重いランタイムは避け、ネイティブ Python
asyncioと最小限のアクター抽象を使用。 - 有界キュー+トークン単位
model_version– 各トークンに生成ポリシーのバージョンを付与し、細粒度の IS 補正を可能にし、後付けの実装を不要に。 - バケッティングした NCCL 重み同期 – vLLM の
NCCLWeightTransferEngineを利用し、約 20 ms のチャンクでブロードキャスト、同期レイテンシを大幅に削減。 - 部分ロールアウトサポート – エージェントワークロード向けにプレフィックス再開機構を実装し、重み更新後も飛行中生成を継続できるように。
これらの選択はエコシステム全体のベストプラクティスを統合しつつ、TRL コミュニティが扱いやすい実装を目指します。
結論: 非同期 RL トレーニングは大規模 LLM のポストトレーニングにおける事実上の標準となっています。調査は、推論と学習の分離、ロールアウトバッファ、非同期重みプッシュという方向への明確な収束を示し、Ray が支配的なオーケストレーション層、NCCL ブロードキャストがデフォルトの同期手法であることを示しています。今後は LoRA のみの同期、MoE ルーティング整合性、マルチエージェントエピソード処理に取り組む必要があります。
Sources
関連
- Dispatch
- プロジェクト
- Dispatch
- プロジェクト