用 Hub Bucket 部署万亿参数:TRL 中的 Delta 权重同步
Shipping a Trillion Parameters With a Hub Bucket: Delta Weight Sync in TRL
Hugging Face 团队(包括 Quentin Gallouédec、Kashif Rasul、Lewis Tunstall 等)提出了一种基于稀疏 delta 的异步强化学习(async RL)权重同步方案。在 bf16 格式下,相邻 RL 优化器步骤间约 99% 的权重比特未发生变化。该方法通过优化器钩子生成布尔掩码,仅将变化的元素编码为稀疏 safetensors 文件,上传至 Hugging Face Bucket,并通知 vLLM 推理引擎获取。在 Qwen3-0.6B 模型上,每步传输负载从 1.2 GB 降至 20-35 MB。训练器与推理引擎通过 Hub bucket 通信,无需共享集群或 RDMA。相关代码已提交为 TRL PR #5417。
](https://huggingface.co/aminediroHF)
太长不看版,因为你有模型要训练,我们尊重这一点:
- Async RL 有一个肮脏的秘密:每一步,训练器都必须将整个模型发送到推理引擎。对于一个 7B 的 bf16 模型,这是 14 GB。对于一个前沿的 1T 模型 checkpoint,这大约是 1 TB。每一步。
- 事实证明,你并不需要这样做。在两个连续的 RL 优化器步骤之间,大约 99% 的 bf16 权重是比特完全相同的(在最坏情况下也从未低于 98%)。实际的 delta 非常小。
- 我们提交了一个 TRL PR,它将仅发生变化的元素编码为稀疏的 safetensors 文件,上传到Hugging Face Bucket,并通知 vLLM 去获取它。在 Qwen3-0.6B 上,每步的传输负载从 1.2 GB 下降到 20 到 35 MB。
- 锦上添花的是:我们运行了一个完全分离的训练,其中训练器在一台机器上,vLLM 运行在一个 Hugging Face Space 中,Wordle 环境运行在另一个 Space 中,权重通过一个 Hub bucket 流动。没有共享集群,没有 RDMA,没有 VPN。
Async RL 变得便宜多了。继续阅读。
传输相同权重的两种方式。红色是未生成任何 token 的挂钟时间。
1. 一太字节问题
如果你读过我们之前关于 async RL 训练格局 的文章,你已经知道关键点了。每个 async RL 库,无论它如何拼写 "actor model" 或将其 NCCL 后端涂成什么颜色,最终都会绊倒在同一个根源上:权重同步。
推理引擎使用的是第 N 步的策略。训练器刚刚完成了第 N+1 步。新的权重必须在推理引擎开始无可救药地偏离策略之前从一端传输到另一端。无论你运行的是同步还是异步,这都处于关键路径上:阻塞传输是 GPU 未生成 token 的浪费的空闲计算。通过稀疏 delta 路径,你可以将该空闲时间压缩到几秒钟,并且训练器甚至不必等待推理引擎就绪:它只需在优化器步骤完成时发布 "weights ready" 并将权重上传到共享 bucket,而推理引擎则在自己的时间获取。
Fireworks 在他们的文章 Frontier RL Is Cheaper Than You Think 中为这个数字提供了一个非常令人难忘的数值:对于一个 fp8(他们的设置)的前沿 1T 参数 checkpoint,一个完整的快照是 1024 GiB,而传统观点认为,每次更新你的 rollout 集群时都需要传输这么多数据。正是这种数字让人们开始绘制包含超级集群、RDMA 网络和专用跨区域链接的图表。他们测量到的相邻 checkpoint 之间的平均 delta 为 20.3 GiB,即完整模型的 1.98%,并且 "超过 98% 的 bf16 格式权重在连续 checkpoint 之间保持比特等效"。
Cursor 的 Composer 2 报告 讲述了一个类似的故事。他们在不同区域运行训练和推理,并通过一个共享的 S3 bucket(他们的原话)将它们拼接在一起,训练器将压缩的权重差异每个训练步骤都上传到该 bucket。每个集群独立地从共享的 delta 链下载并重建,"不需要与训练集群直接连接"。双方从不直接相互通信参数。Bucket 就是传输介质。
两篇论文在三点上达成一致,我们想慢慢重复一遍,因为这篇文章的其余部分本质上是对它们的忠实开源翻译:
- 在相邻的两个 RL 步骤之间,大多数权重实际上没有改变。
- 如果你只发送发生变化的部分,你的带宽成本大约会下降两个数量级。
- 如果你通过一个共享的对象存储来路由这些微小的差异,你就不再需要训练器和推理集群位于同一个数据中心。
唯一缺少的是一个你可以 pip install 的版本。所以我们写了一个。
2. 为什么 bf16 RL 权重几乎总是稀疏的
在我们连接任何东西之前,值得理解为什么整个游戏是可行的。"98% 的权重没有变化" 这个说法听起来可疑地像那种在演示中有效但在实际中失效的数字。但事实并非如此。这是 bf16 算术在 RL 使用的学习率下工作的结果。
一个 bf16 数字有 7 位尾数。在两个连续的 2 的幂之间,恰好有 $2^7 = 128$ 个可表示的值,因此 $|w|$ 附近相邻 bf16 数字之间的间隔大约是 $|w| \cdot 2^{-7}$。当一个更新低于该间隔的一半时,即当 $|\Delta w| < |w|/256$ 时,它会被 bf16 转换吸收。这就是 PULSE 在其图 3 中绘制的 "bf16 可见性阈值"。
现在看看 Adam 做了什么。在 RL 学习率,比如 $3 \times 10^{-6}$ 下,对单个权重的更新是:
$$ \Delta w = - \eta \cdot \frac{\hat{m}}{\sqrt{\hat{v}} + \epsilon} $$
归一化步骤 $\hat{m}/(\sqrt{\hat{v}}+\epsilon)$ 大致是 1 的量级,所以 $|\Delta w| \approx \eta \approx 3 \times 10^{-6}$。对于大多数权重,$|w|$ 大约在 $10^{-2}$ 到 $10^{-1}$ 之间(PULSE 报告代表性 LLM 权重的中位数为 0.019)。在该量级下的阈值 $|w|/256$ 大约为 $4 \times 10^{-5}$ 到 $4 \times 10^{-4}$,这大于更新量。
换句话说:优化器在低语,而 bf16 听不到。更新被舍入吸收,$w$ 的字节表示没有改变,从推理引擎的角度来看,这个权重没有移动。将其乘以几亿个参数,你就得到了 >99% 的稀疏度数字,免费的,零近似。
这正是 PULSE 论文 (Mihai & Belilovsky, 2026) 中形式化的论点。他们定义了两个阈值。吸收界 $10\eta$ 是 Adam 更新的保守最坏情况,有效界 $\eta$ 是你实际所处的状态。bf16 可见性阈值是 $|w|/256$。每当更新低于可见性阈值时,它就会被吸收,并且 bf16 字节不会改变。他们的图 3 将两个边界绘制在代表性 LLM 权重的点云上,结论是明确的:在 $\eta = 3 \times 10^{-6}$ 时,吸收界本身已经低于模型中几乎所有权重的可见性阈值。他们在 Qwen2.5 (0.5B/1.5B/7B)、Llama-3.2-3B 和 Gemma-3-4B 上进行了实证测量,一致发现平均每步稀疏度约为 ~99%,在 400 个训练步骤上标准差为 0.2% 到 0.4%。最坏情况下的步骤保持在 98% 以上。所以 <1% 发生变化不是一个幸运的测量结果;这是算术保证的。
我们不必分析性地预测这一点(实际上,我们确实尝试过从 Adam 的 $m$ 和 $v$ 统计量预测变化掩码,但召回率只有可怜的 30%,稍后会详细说明)。我们只需要观察哪些字节翻转了。这是每个参数的一个微小的布尔张量,在优化器步骤附近计算。
将学习率拖到 RL 领域,观察回转到 bf16 的标记如何瞬间跳回原始刻度。左下角的 256 元素网格是跨一个小模型的聚合效果。
3. HF Buckets 与架构
这就是故事的第二部分登场的地方,也是这篇文章从 Fireworks/Cursor 的翻译开始变成 Hugging Face 的东西的地方。
3.1 什么是 Bucket?
Bucket 是 Hub 上的一种仓库类型,专为高频对象存储设计。没有提交仪式,没有 PR 工作流,没有 LFS 怪癖。你添加文件,列出文件,下载文件。Python 接口是两个函数:
from huggingface_hub import batch_bucket_files, download_bucket_files
# 训练器端
batch_bucket_files("my-org/wordle-deltas", add=[(buffer, "deltas/step_000042.safetensors")])
# 推理端
download_bucket_files("my-org/wordle-deltas", files=[("deltas/step_000042.safetensors", local_path)])
就是这样。两个函数调用,你的权重就在传输中了。
在底层,bucket 由 Xet 支持,这是 Hub 的基于内容分块的存储层。Xet 查看你上传的每个文件,根据其实际内容(而不是固定偏移量)将其切片成块,并针对 bucket 中已有的所有内容进行去重。在这种情况下,实际的好处(非常令人愉快)是,即使我们懒得编写稀疏编码,只是每一步都上传完整的锚点,Xet 仍然只会传输发生变化的块。稀疏编码 + Xet 栈:我们只为移动的内容付费,并且只付一次。
这是 Fireworks 和 Cursor 都使用的 "共享 S3 bucket" 的开源等价物,只是存储层已经了解内容哈希,你现有的 HF token 已经拥有权限,并且它与栈的其余部分(Spaces、datasets、models)原生集成。
3.2 三个盒子
完整的架构正好有三个盒子和一个共享基板:
- 训练器。 任何你想要的地方。一个 GPU,八个 GPU,一台带 USB 连接 H100 的笔记本电脑,我们不会评判。拥有模型权重,运行优化器,发出稀疏 delta。
- HF Bucket。 一个单一的仓库,两个前缀:
anchors/用于偶尔的完整快照,deltas/用于中间的稀疏补丁。这是双方唯一达成一致的东西。 - vLLM rollout 服务器。 任何你想要的地方,关键是不一定与训练器在同一位置。从 bucket 拉取,应用 delta,并提供 rollout。
- 环境。 以通常的方式(HTTP、函数调用,无论你的环境支持什么)挂在 rollout 服务器上。
需要内化的属性,也是 Cursor 的论文大力推销并且在这里完全成立的一点:训练器和 rollout 服务器从不就权重相互通信。它们交换一个微小的 POST,包含 {"repo_id": ..., "filename": ...},这就是整个控制平面。实际的字节传输发生在每一侧和 bucket 之间,并行进行,没有共享的网络结构。
为什么这在实践中很重要:
- rollout 服务器可以在另一个区域、另一个云,或者 Hugging Face Space 内的 NAT 后面。它不在乎。
- N 个推理副本可以从同一个 bucket 拉取同一个 delta,Xet 对所有副本的字节进行去重。
- 训练器永远不必知道存在多少个推理副本,或者它们在哪里,或者其中一个是否刚刚崩溃。
训练器写入。副本读取。Hub 负责管道。
4. 协议
现在我们可以打开引擎盖了。该协议有四个部分:一种传输格式、一个 bucket 布局、一个 30 行的 vLLM 扩展,以及一个训练器端的变化检测器。老实说,代码比听起来少。
4.1 Safetensors 作为传输格式
我们选择了 safetensors 作为磁盘和传输格式。它已经是 Hub 上的规范 checkpoint 格式,每个合理的框架都能读取它,并且头部携带任意字符串元数据。这个元数据字段就是我们隐藏协议的地方。
bucket 中有两种文件。
锚点看起来像一个普通的 checkpoint:每个参数一个张量,完整的 bf16 权重,每 $N$ 次同步写入一次(我们默认 $N=10$)。
anchors/step_000010.safetensors
├── model.layers.0.self_attn.q_proj.weight (bf16, 完整)
├── model.layers.0.self_attn.k_proj.weight (bf16, 完整)
└── ...
metadata:
sparse=False, model_version=10, sparsity=0.0
Delta 是有趣的部分。对于每个实际发生变化的参数,我们存储两个条目:一个平坦的 int32 张量,包含元素索引,以及一个 bf16 张量,包含这些索引处的值。
deltas/step_000011.safetensors
├── model.layers.0.self_attn.q_proj.weight.indices (int32, [num_changed])
├── model.layers.0.self_attn.q_proj.weight.values (bf16, [num_changed])
├── model.layers.0.mlp.gate_proj.weight.indices
├── model.layers.0.mlp.gate_proj.weight.values
└── ...
metadata:
sparse=True, model_version=11, sparsity=0.9938, changed_params=[...]
这个选择带来的一些不错的结果:
- delta 是一个文件。你可以用 Python 的
safe_open(...)打开它,并检查其中的每个张量。没有专有的帧格式,没有长度前缀,没有版本握手。 - 元数据是自描述的。接收者读取
sparse=True/False并分支。没有单独的清单。 - 在推理端通过 mmap 实现零拷贝,当你每隔几秒执行此操作时,这很重要。
节奏很简单:每第 N 步锚点,中间是 delta。两者最终都在同一个 bucket 中,位于 anchors/ 和 deltas/ 前缀下。每个新的推理副本只需要获取最新的锚点,然后重放自那以后的 delta。
十个训练步骤。第 1 步和第 6 步是锚点(完整快照),其他每一步是稀疏 delta。文件在你观看时落入 bucket。
4.2 训练器端:来自优化器钩子的布尔掩码
训练器需要知道哪些 bf16 元素实际翻转了。我们用一个微小的 BF16ChangeDetector 来实现,它在优化器上注册了一个步骤前和步骤后的钩子:
class BF16ChangeDetector:
def __init__(self, model, optimizer):
self._pre_step_bf16: dict[str, torch.Tensor] = {}
self._validated_masks: dict[str, torch.Tensor] = {}
optimizer.register_step_pre_hook(self._pre_step_hook)
optimizer.register_step_post_hook(self._post_step_hook)
def _pre_step_hook(self, opt, args, kwargs):
for p in self._params:
self._pre_step_bf16[name_of(p)] = p.detach().to(torch.bfloat16).cpu().clone()
def _post_step_hook(self, opt, args, kwargs):
for p in self._params:
self._validated_masks[name_of(p)] = (
p.detach().to(torch.bfloat16).cpu() != self._pre_step_bf16[name_of(p)]
)
PR 中的实际代码有更多的管道(通过 data_ptr() 匹配优化器参数对象到模型参数,因为 Accelerate 将它们包装为不同的 Python 对象),但这个想法可以写在餐巾纸上:快照,步骤,差异。
这是事实来源。我们尝试过更优雅的路径,即直接从 Adam 的 $m$ 和 $v$ 统计量预测掩码,使用 bf16 ULP 阈值。理论上可行。但在实践中,召回率约为 30%,这意味着我们会发送一个缺少三分之二实际更新的 delta。Adam 的归一化足够混乱,以至于分析阈值并不严格。所以我们只是比较字节。这在训练器端花费一个 bf16 CPU 模型快照,我们愿意支付。
新的 _sync_weight 流程的四个阶段是:
- 推理继续运行时上传。 训练器将掩码元素编码到 safetensors 缓冲区中,并将其推送到 bucket。在整个步骤中,vLLM 仍然愉快地服务于旧策略。
- 暂停 vLLM。 一个简短的 HTTP 调用,几百毫秒。
- 发送
/update_weights信号。 发送 bucket 坐标。vLLM 下载、应用、返回。 - 恢复。 vLLM 重新上线。
日志行说明了情况:
Delta: 1234567/200000000 elements changed (sparsity=99.38%)
[delta_engine] uploaded user/wordle-deltas/deltas/step_000042.safetensors (27.4 MB, ...)
Weight sync: done. Total 9.4s (inference paused 1.1s)
重要的是括号里的内容。推理暂停了 1.1 秒。剩下的 9.4 秒花在上传上,这是在 rollout 服务器仍在生成 token 时发生的。使用 NCCL,我们支付的是完整的同步时间作为暂停时间。在这里,我们将其作为后台时间支付。
一个端到端的同步。在 delta-over-bucket 和 NCCL broadcast 之间切换,并尝试副本计数切换以查看扇出情况。
4.3 vLLM 端:一个 30 行的扩展
vLLM 为此提供了一个干净的抽象,称为 WeightTransferEngine。我们实现了一个 DeltaWeightTransferEngine,其 receive_weights 方法的精神如下:
def receive_weights(self, update_info, load_weights):
download_bucket_files(update_info.repo_id, files=[(update_info.filename, local_path)])
with safe_open(local_path, framework="pt", device="cpu") as f:
meta = PatchMetadata.from_metadata_dict(f.metadata())
if not meta.sparse:
# 锚点:提供每个张量并为未来的 delta 创建快照
for name in f.keys():
tensor = f.get_tensor(name)
self._bf16_snapshot[name] = tensor.clone()
load_weights([(name, tensor)])
else:
# Delta:将 (indices, values) 应用到快照,将完整张量交给 vLLM
for name in json.loads(meta.changed_params):
indices = f.get_tensor(f"{name}.indices").long()
values = f.get_tensor(f"{name}.values")
snap = self._bf16_snapshot[name].flatten()
snap[indices] = values
self._bf16_snapshot[name] = snap.reshape(self._bf16_snapshot[name].shape)
load_weights([(name, self._bf16_snapshot[name])])
我们通过 vLLM 的 --worker-extension-cls 标志注册它,这意味着不需要 fork vLLM。你将 TRL 安装到与 vLLM 相同的镜像中,将 CLI 指向我们的类,就完成了。
值得一提的是:vLLM 本身有一个正在进行中的工作,旨在原生支持稀疏权重传输,vllm-project/vllm#40096。它直接在 WeightTransferEngine 基类上添加了 receive_sparse_weights() 和 trainer_send_sparse_weights(),补丁编码为 (indices, values) 并通过 index_copy_() 就地应用,完全消除了 GPU/CPU 验证往返。该 PR 报告了在 Qwen3-1.7B 上,稀疏补丁的传输为 0.16 MB 耗时 0.40 ms,而完整密集发送为 942 MB 耗时 192 ms。
我们在推理端实现中的一个诚实警告:我们保留了一个 CPU bf16 模型快照,以便我们可以从稀疏的 (indices, values) 补丁重建完整的张量,因为目前 vLLM 中的 load_weights 期望完整的张量。一旦 #40096(或其后续版本)落地并暴露一个就地稀疏 load_weights 路径,我们就可以直接在 GPU 上应用索引并丢弃快照!
5. 在 Spaces 上真正运行起来
这是让我们感到得意的一部分。到目前为止我们描述的一切都可以在你的笔记本电脑上运行,但通过 Hub bucket 路由权重的意义在于训练器和 rollout 服务器不必彼此靠近。因此,我们使用三台机器运行了一个完全分离的训练,它们之间不共享网络:
- 一台带有一个 GPU 的机器,运行训练器。
- 一个 Hugging Face Space(Docker SDK,L4 GPU),运行带有我们扩展类的 vLLM。
- 第二个 Hugging Face Space(CPU),运行 Wordle 环境服务器,具有 256 个并发会话容量。
- 中间的 Hub bucket。
设置这实际上只需要几个 hf CLI 调用。vLLM Space 的 Dockerfile 本质上是上游的 vLLM 镜像加上 pip install trl@... 再加上入口点:
FROM vllm/vllm-openai:latest
RUN pip install "trl @ git+https://github.com/huggingface/trl.git@delta-weight-sync"
ENV VLLM_SERVER_DEV_MODE=1
EXPOSE 7860
ENTRYPOINT ["vllm", "serve", "Qwen/Qwen3-1.7B", \
"--host", "0.0.0.0", "--port", "7860", \
"--worker-extension-cls", "trl.experimental.async_grpo.delta_engine.DeltaWorkerExtension", \
"--weight-transfer-config", "{\"backend\":\"nccl\"}", \
"--max-model-len", "32768", \
"--gpu-memory-utilization", "0.8"]
将其部署为 Space:
hf repos create $USER/vllm-wordle-inference \
--type space --space-sdk docker --flavor l4x1 \
--secrets HF_TOKEN=$HF_TOKEN
hf upload $USER/vllm-wordle-inference examples/scripts/openenv/vllm_space/ --type space
并从地球上任何可以通信 HTTPS 的地方启动训练:
python examples/scripts/openenv/async_wordle.py \
--vllm-server-url https://$USER-vllm-wordle-inference.hf.space \
--env-url https://openenv-wordle.hf.space \
--delta-sync-repo-id $USER/wordle-deltas \
--model Qwen/Qwen3-1.7B
训练器从不打开端口。Space 从未见过训练器的 IP。Wordle 环境不知道它们任何一个存在。它们都通过 Hub 通信。训练在即时 EOS 健全性检查上收敛,然后在真实的 Wordle rollout 上收敛:奖励上升,delta 负载保持在 20 到 35 MB 范围内,每次同步的推理暂停窗口保持在大约一秒左右。完整的运行日志链接在配套的 PR 中。
6. 这实际上解锁了什么?
几件事,我们认为它们很重要。
无需集群的 Async RL 训练。 如果你有一个 GPU 和一个 Hugging Face 账户,你现在可以进行真正的分离训练。你的训练器在 GPU 上;你的 rollout 集群生活在 Spaces 中;你的环境生活在另一个 Space 中;权重通过 bucket 移动。这以前需要一个同地部署的设置(以及随之而来的所有吞吐量妥协)或一个具有共享网络的实际集群。现在不再需要了。
免费的多副本推理。 启动两个 vLLM Space,或者十个。它们都从同一个 bucket 拉取。Xet 内容寻址存储,因此连续的锚点在静态时共享块(这可以防止你的 bucket 爆炸),而 Hub 的边缘缓存使得重复下载同一个文件变得廉价。想要一个全球分布的 rollout 集群?现在这是一个小的 DevOps 练习,而不是一个研究项目。
一种可以用现有工具调试的传输格式。 delta 是一个 safetensors 文件。你可以从 notebook 中 safe_open 它,列出它的键,检查索引,自己计算稀疏度。我们在不透明的 NCCL 流上花在 tcpdump 上的时间已经够多了,足以欣赏这一点。
一条通往前沿规模的路径。 20 到 35 MB 的数字是针对 Qwen3-0.6B 的。有趣的问题是,当你调高刻度时,曲线会是什么样子。让我们做一下餐巾纸数学。
以 Llama-3.1-405B 为例。在 bf16 下,它在磁盘上是 810 GB。PULSE 测量到在 RL 学习率下平均每步稀疏度约为 ~~99%,因此实际的 delta 大约占参数的 1%。他们部署测量的编码在 **7B 模型上达到 108 MB,这是 PULSE 报告的 ~~130× 缩减。线性缩放到 405B,delta 大约为每步 6 GB。
这在挂钟时间上能给你带来什么?NCCL 在集群内部很快,当然。假设一个慷慨的 100 GB/s 聚合广播带宽(多节点,RDMA,全套)。一次完整同步是 810 GB / 100 GB/s ≈ 8 秒 的推理暂停,每一步。使用 delta 路径,训练器在生成继续运行时在后台将 6 GB 流式传输到 bucket,而 rollout 服务器实际的暂停窗口只是应用步骤,在这个规模下大约需要几秒钟。所以即使在我们离开集群之前,delta 将可见暂停减少了 4 倍,并将线路上的字节减少了约 130 倍。
现在离开集群。NCCL 完全不能跨云工作。一旦你想要一个在 us-east 的 rollout 集群,另一个在 eu-west,也许还有一个在 Hugging Face Space 中,基于 bucket 的路径是唯一的路径。在 1 GB/s 的可用互联网带宽下,一次完整的广播需要 13 分钟;delta 在 6 秒内完成。
对于 Fireworks 框架中的 1 TB 级模型,他们自己测量的数字显示 20.3 GiB 的 delta 对比 1024 GiB 的完整快照,大约减少了 50 倍。PULSE 更紧凑的稀疏编码会进一步推动这一点(外推每个 delta 约 15 GB,更接近 65 倍)。无论哪种方式,你都处于一个通过商品对象存储传输权重不再是 hack,而是唯一合理架构的状态。
7. 我们还有哪些待办事项
我们不假装这已经完成了。这是诚实的清单。
- 两个 CPU bf16 快照,多了一个。 训练器保留一个(用于变化检测器),rollout 服务器保留一个(用于为 vLLM 的
load_weights重建完整张量)。第一个我们暂时无法摆脱,直到有人找到一个紧凑的分析掩码,这比看起来要难。第二个在 vLLM 获得稀疏load_weightsAPI 后就会消失。PR 即将到来。 - 固定的锚点节奏。 我们目前每 $N$ 步转储一个完整的锚点。一个自适应策略("当累积漂移超过 X 时锚点")将减少长时间运行时的锚点成本。
- 多节点 FSDP2 训练器。
BF16ChangeDetector是围绕每个进程的优化器钩子构建的。它应该可以干净地泛化到 FSDP2,但我们还没有在多节点规模上测量过。PR 中有一个带有我们名字的TODO。 - 挂钩到优化器。 我们仅从 $(m, v)$ 预测掩码的尝试给出了低召回率,这意味着分析性 bf16 阈值正在做一些比教科书公式更微妙的事情。我们很乐意听到任何破解了这个问题的人的意见。
- 与传输中压缩叠加。 稀疏 safetensors 和每块 gzip 是正交的。我们还没有尝试过将它们结合起来。尽管我们不期望巨大的压缩增益。
8. 尝试一下
- PR: huggingface/trl#5417。分支是
delta-weight-sync。 - 完整的 Wordle 示例:
examples/scripts/openenv/async_wordle.py。 - Spaces Dockerfiles:
examples/scripts/openenv/vllm_space/和examples/scripts/openenv/wordle_space/。 - 背景阅读: 我们的 async RL 格局文章,Fireworks 1 TB 文章,Cursor Composer 2 报告。