memory内存高效 RL

我们很高兴在 Unsloth 中引入更高效的强化学习 (RL),包含多项算法改进:

  • 上下文长度提高 1.2 到 1.7 倍 无任何速度变慢且无需额外内存!

  • 强化学习训练运行加速 10% 通过重构内核和异步数据移动实现

  • 快 2 倍 torch.compile 在模型加载期间

Unsloth 已经 相较于所有其他使用 FA2 的配置,提升了 RL 训练速度、上下文窗口并将 VRAM 使用减少 50–90%,但现在 Unsloth 的待机(Standby) 进一步改进了这一点。我们的 Standby 功能在限制速度下降方面独具优势,有时还能使训练更快!

现在,Qwen3-32B LoRA 16 位在单个 H100 80GB GPU 上可以达到 6,144 的上下文长度,而此前为 3,600(长 1.7 倍)。Llama-3.1-8B QLoRA 4 位可以达到 47,500 长度,而此前为 42,000(长 1.13 倍)。

我们通过各种内核优化使 RL 运行加速 10%,并在从训练切换到推理模式时移除了 CPU 与 GPU 之间的 LoRA 通信通道。最后,我们使用自定义 torch.compile 标志使 vLLM 的 rollout 加速 10%,并将编译时间减少 2 倍。

如何启用优化

要启用 Unsloth 的待机(Standby) 功能,请在任何 Unsloth 导入之前设置环境变量 UNSLOTH_VLLM_STANDBY 然后设置 gpu_memory_utilization = 0.95 就这样!

import os
os.environ["UNSLOTH_VLLM_STANDBY"] = "1"

from unsloth import FastLanguageModel
import torch
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/Qwen3-8B-Base",
    max_seq_length = 2048, # 对于更长的推理轨迹可以增加
    load_in_4bit = False, # LoRA 16 位时为 False
    fast_inference = True,
    max_lora_rank = 32, # 更大的秩 = 更聪明,但更慢
    gpu_memory_utilization = 0.95,
)

🎓不再需要 gpu_memory_utilization!

有了 Unsloth 的新 RL 改进,您再也不必担心调整或设置 gpu_memory_utilization 了——只需将其设置为 90% 或 95% 的 GPU 利用率即可——可惜 100% 无法使用,因为仍需为小张量保留一些空间。此前需要将其从 30% 调整到 95%——现在不必了!设到最大值,Unsloth 会处理剩下的!

⁉️为什么 RL 要使用这么多内存?

GRPO(以及许多 RL 变体)在很大程度上依赖生成,而生成主要由 vLLM 驱动。但这代价很高,因为它需要持续的 GPU 内存用于权重、激活和 KV 缓存.

推理占用大量 VRAM

训练也会使用 VRAM!

这意味着 RL 需要在 GPU 上同时保留两套 VRAM/内存:

  1. 推理引擎(包含模型权重、KV 缓存)

  2. 训练引擎(包含模型权重、激活、梯度、优化器状态)

当前的 RL 框架不得不在 80GB GPU 上 50/50 划分,50% 给推理,50% 给训练。从训练模式切换到推理模式时移动权重可能需要相当长的时间。

80GB GPU
推理引擎(50%)
训练引擎(50%)

模型权重

16GB

16GB

KV 缓存

24GB

激活、梯度、优化器状态

24GB

之前的 Unsloth 版本已经智能地优化了上述内容,因为我们 直接共享 vLLM 的权重空间,这消除了模型权重的双重内存使用。例如,这释放了 16GB 的空间,可用于增加上下文长度或提高生成速度。此外,我们不需要进行内存移动,这也使训练更快。

80GB GPU
推理引擎(50%)
训练引擎(50%)

模型权重

16GB 共享

<<< 共享

KV 缓存

24GB + 8GB = 32GB

激活、梯度、优化器状态

24GB + 8GB =32GB

🦥Unsloth 待机(Standby)

但我们可以走得更远——我们首先注意到 RL 会交替进行推理然后训练然后再推理再训练等。

这意味着理论上推理和训练的内存空间可以被重复使用,因为推理和训练是不同的模式——这就是 vLLM 的睡眠(sleep)模式功能arrow-up-right 的作用,它有两个选项:

  1. level = 1 将权重复制到 CPU 并删除 KV 缓存

  2. level = 2 删除权重并删除 KV 缓存

但请注意在 Unsloth 中我们共享 vLLM 的权重内存空间——这意味着我们需要一种新的方法来删除 KV 缓存并忽略删除权重,我们称之为 Unsloth Standby。

80GB GPU
推理引擎
训练引擎

模型权重

16GB 共享

<<< 共享

多用途

64GB 空间

KV 缓存

激活、梯度、优化器状态

要启用此功能,只需在任何 Unsloth 导入之前将以下内容添加到所有 RL / GRPO 训练运行中:

🧪性能实验

在这里您将看到我们如何为 GRPO 基准测试内存使用和上下文长度。请注意我们进行 每个提示 2 次生成,因为为了使 GRPO 生效,我们需要至少 2 次生成以计算样本均值和方差。 如果没有 2 次生成,单样本的标准差为 0。这会导致使用该值的优势 (reward - mean)/std 变得未定义.

Z=riμ1n(riμ)2Zn=1=r1μ11(r1μ)2=00=undefinedZ=\frac{r_i - \mu}{\sqrt{\frac{1}{n}\sum(r_i-\mu)^2}} \\ Z_{n=1}=\frac{r_1 - \mu}{\sqrt{\frac{1}{1}\sum(r_1-\mu)^2}}=\frac{0}{0}=\text{undefined}

这意味着就 GRPO 而言,Qwen-3 32B 的最大上下文长度 6,144 实际上是乘以 2 次生成,即长度为 12,288。

我们下面提供了针对 Llama-3.1 8B 在 LoRA(16 位)和 QLoRA(4 位)两种情况下的实验:

如果您注意到任何训练时间差异,也不会太大。在我们同条件比较中,我们注意到 <1% 的训练时间减慢或甚至加速,这可能归因于误差范围。

我们还推测,由于内存压力降低,可能会出现提速,因此 CUDA 内存分配器端的内存清理可能减少。

在上图中,您可以看到在单个 T4 GPU 上 Qwen 3 4B 的基线模式与待机模式之间的差异。 我们可以将 vLLM 的 gpu_memory_utilisation 拉高到高达 0.95,而不用担心会影响训练。这意味着您可以容纳更高的上下文长度序列并处理更多序列。例如在第一个案例中,我们有足够的内存来容纳并处理 32K 长度的序列,前提是训练允许,而此前任何超过 2K 的输入可能都无法放入并导致 OOM(内存不足)。

实验
配置
状态
GPU 内存使用情况
备注

standby True

vllm_gpu_util 0.95

num_gen 2

grad_acc_steps 2

运行 40 步 / 40 分钟

14.5 GiB(由 vllm_gpu_util 设置)

足够容纳 32K KVCache,使用 2-4K 的分块,或例如 16K KVCache + 16K 分块

standby True

vllm_gpu_util 0.9

num_gen 2

grad_acc_steps 2

在 40 分钟内运行 32 步

13.8 GiB(由… 设置)

大概足够容纳约 28K 的 KVCache,使用 2-4K 的分块,或例如 15K KVCache + 15K 分块

standby False

vllm_gpu_util 0.9

num_gen 2

grad_acc_steps 2

模型加载但无法训练,因为即使批次大小为 1 也无法放下

OOM(内存不足)

standby False

vllm_gpu_util 0.8

num_gen 2

grad_acc_steps 2

模型加载但无法训练,因为即使批次大小为 1 也无法放下

OOM(内存不足)

standby False

vllm_gpu_util 0.7

num_gen 2

grad_acc_steps 2

训练正常

28 步耗时 39 分钟

约 15.1 GiB

任何稍长的输入都会在 colab 上导致 OOM

standby True

vllm_gpu_util 0.7

num_gen 2

grad_acc_steps 2

训练正常

29 步耗时 40 分钟

13 GiB,但大多数时间在 10–11GB 左右

在相同配置下,我们节省了 2 GiB,即 15% 的内存。 对更长的序列而言节省可能更高

H100 实验

模型
GPU
序列长度
生成次数
梯度累积步数

Qwen2.5-14B-Instruct

NVIDIA H100 80GB PCIe

32,768

8

4

在我们下面可折叠的结果中,您可以看到峰值内存使用存在 9 GiB 的差异(请注意在我们的情况中 90% 的时间 GPU 内存使用等同于峰值内存)。 作为对比,使用 TRL 和 LoRA 我们最多只能微调一个 8B 参数模型,并且最大上下文长度为 1024(少 32 倍)。 任何具有更高序列长度的配置(在相似配置下)都会导致进程因 OOM 而失败。

chevron-right点击查看 Unsloth 待机模式与无待机的基准对比hashtag

下图显示了待机与非待机训练在 Unsloth 中的比较。数据取三次运行的平均以确保指标不噪声化。事实上,如果放大观察,您会发现启用待机还能使其更快,这很可能如前所述是由于内存压力减少。

之前的 A100 40GB 实验

在我们之前针对 A100 40GB GPU、使用 Qwen-2.5-3b-instruct 且每个样本 8 次生成的实验中,我们观察到在未启用待机时,GRPO 训练(模型以 16 位加载、LoRA、仅权重可训练)最多只能容纳 6K 的序列长度。使用我们的待机功能,我们能够容纳 10K 及更高! 相比之下,TRL 在保持相同批次大小的情况下最多只能给出 1K 的上下文长度。

🎉其他优化

我们现在选择了更好的编译标志并将编译时间减少了 50% 或更多。我们还成功对任意 vLLM 版本进行动态补丁以处理 gc.collect 为向后兼容性做得更好,灵感来自这个 vLLM pull request(拉取请求)arrow-up-right。这将编译时间从 2 分钟缩短到不到 40 秒。

我们还对 torch.compile 标志进行了优化并尝试开启某些标志——不幸的是 combo_kernelsmulti_kernel 在 vLLM 0.10 与 Torch 2.8/2.9 nightly 上无法正常工作,并且 coordinate_descent_tuning 使得所有内核的自动调优显著变慢。以前编译不到一分钟,但启用后耗时超过 13 分钟以上,且性能提升很小。

📚GRPO 笔记本

我们所有的 GRPO 笔记本默认开启 Unsloth Standby 并应用所有优化!请参见 https://docs.unsloth.ai/get-started/unsloth-notebooksarrow-up-right 获取我们所有的 GRPO 笔记本,或试试下面的:

最后更新于

这有帮助吗?