> For the complete documentation index, see [llms.txt](https://unsloth.ai/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://unsloth.ai/docs/zh/bo-ke/3x-faster-training-packing.md).

# 使用 Unsloth Kernels + Packing 让 LLM 训练速度提升 3 倍

了解 Unsloth 如何提升训练吞吐量，并消除微调中的填充浪费。

Unsloth 现在支持最高 **快 5×** （通常 3 倍）的训练，采用我们新的自定义 **RoPE 和 MLP Triton 内核**，再加上我们新的智能自动打包。Unsloth 的新内核 + 新特性不仅提升训练速度，还进一步 **减少 VRAM 占用（30% - 90%）** 且不损失准确率。 [Unsloth GitHub](https://github.com/unslothai/unsloth)\
\
这意味着你现在可以训练像 [Qwen3](/docs/zh/mo-xing/tutorials/qwen3-how-to-run-and-fine-tune.md)-4B 这样的 LLM，不仅只用 **3GB 显存**，而且还能快 3 倍。

我们的自动 [**无填充**](#padding-free-by-default) 无污染打包会在所有训练运行中智能启用，无需任何改动，并且适用于所有快速注意力后端（FlashAttention 3、xFormers、SDPA）。 [基准测试](#analysis-and-benchmarks) 显示训练损失与不打包运行一致 **完全**.

* **快 2.3 倍的 QK Rotary Embedding** 支持打包的融合 Triton 内核
* 更新后的 SwiGLU、GeGLU 内核，支持 **用于长上下文的 int64 索引**
* **无污染打包快 2.5 倍到 5 倍** 适用于 xformers、SDPA、FA3 后端
* **无填充快 2.1 倍，VRAM 少用 50%**，准确率变化 0%
* Unsloth 现在还改进了 SFT 损失稳定性，并使 GPU 利用率更可预测。
* 这个新升级适用于 **所有训练方法** 例如完整微调、预训练等。

### :drum:带打包的融合 QK RoPE Triton 内核

早在 2023 年 12 月，我们在发布 Unsloth 时就引入了一个用 Triton 编写的 RoPE 内核。2024 年 3 月，一位社区成员通过优化 RoPE 内核，让为一组 head 启动一个 block 成为可能，使端到端训练快了 1-2%。见 [PR 238](https://github.com/unslothai/unsloth/pull/238).

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FewadBu05vK7zAmJRJcj6%2Frope_varlen_qk_rope_kernel_benchmark_v5.png?alt=media&amp;token=04d277d4-c289-4943-9312-e3d3e2d60bec" alt="" width="563"><figcaption></figcaption></figure>

其中一个问题是，对于每个 Q 和 K，都有 2 个 Triton 内核。我们现在把它们合并成 1 个 Triton 内核，并且 启用了可变长度 RoPE，这对无填充和打包支持至关重要。这使得 RoPE 内核在微基准测试中 **在更长上下文长度下快 2.3 倍**，在更短上下文长度下快 1.9 倍。

我们还消除了所有 clone 和连续转置操作，因此 **RoPE 现在完全原地执行**，进一步减少 GPU 内存。注意在反向传播中，我们看到 `sin1 = -sin1` 因为：

```
Q * cos + rotate_half(Q) * sin
等价于
Q * cos + Q @ R * sin
其中 R 是旋转矩阵 [ 0,  I]
                             [-I,  0]
dC/dY = dY * cos + dY @ R.T * sin
其中 R.T 同样是 [ 0, -I]
但负号经过了转置。[ I,  0]
```

### :railway\_car:用于 Triton 内核的 Int64 索引

在我们于以下内容中介绍的 50 万长上下文训练期间 [500K Context Training](/docs/zh/bo-ke/500k-context-length-fine-tuning.md)，我们会遇到 CUDA 越界错误。这是因为 SwiGLU、GeGLU 的 MLP 内核使用的是 int32 索引，而 Triton 和 CUDA 默认都是如此。

我们不能直接这样做 `tl.program_id(0).to(tl.int64)` ，因为使用 int64 索引会让训练稍微变慢。我们改为把它做成一个 `LONG_INDEXING: tl.constexpr` 变量，这样 Triton 编译器就可以对此进行特化。这让更短和更长的上下文运行都能表现出色！

{% code overflow="wrap" %}

```python
block_idx = tl.program_id(0)
if LONG_INDEXING:
    offsets = block_idx.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE).to(tl.int64)
    n_elements = tl.cast(n_elements, tl.int64)
else:
    offsets = block_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
```

{% endcode %}

### :abacus:为什么需要填充以及数学上的加速

计算机和 GPU 无法处理不同长度的数据集，所以我们必须用 0 来填充它们。这会造成浪费。假设我们有一个数据集，其中 50% 是短序列 S，50% 是长序列 L，那么在最坏情况下，填充会导致 token 使用量为 $$\text{batchsize} \times L$$ 因为最长序列长度占主导。

通过将多个示例打包成一个长的一维张量，我们可以消除大量填充。实际上我们得到如下 token 使用量：

$$
\text{Token Usage} = \frac{\text{batchsize}}{2}L+\frac{\text{batchsize}}{2}S
$$

通过一些数学和代数，我们可以通过以下方式计算加速比：

$$
\text{Speedup} = \frac{\text{batchsize} \times L}{\frac{\text{batchsize}}{2}L+\frac{\text{batchsize}}{2}S} = 2 \frac{L}{L + S}
$$

假设 $$S\rightarrow0$$ 那么我们会得到 2 倍的理论加速，因为 $$2 \frac{L}{L + 0} = 2$$

通过把 50% 短序列的比例改掉，并假设我们有更多短序列，例如 20% 长序列和 80% 短序列，我们得到 $$\frac{L}{0.2L + 0.8S}\rightarrow\frac{L}{0.2L}=5$$ 所以训练快 5 倍！这意味着打包的加速效果取决于你的数据集里有多少短行（越短越多，速度越快）。

### :clapper:默认无填充

除了在设置时可获得的大幅吞吐提升之外 `packing = True` 在你的 `SFTConfig` 中，我们将 **会自动使用无填充批处理** 以减少填充浪费、提高吞吐量并提升 tokens/s 吞吐，同时带来 ***完全相同的损失*** 与 Unsloth 之前的版本一致。

例如对于 Qwen3-8B 和 Qwen3-32B，我们看到显存占用下降了 60%，速度快 2 倍，并且损失和梯度范数曲线完全相同！

<div><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FPATEJoJwIotXNPsYT1hu%2FW%26B%20Chart%2010_12_2025%2C%203_57_51%20am.png?alt=media&amp;token=e31ee2cd-cd6e-4fd2-9c59-7f2148179815" alt=""><figcaption></figcaption></figure> <figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FjjnXdgPSgUxL9WNzx9wc%2FW%26B%20Chart%2010_12_2025%2C%203_58_19%20am.png?alt=media&amp;token=54368c73-2ce1-4faa-a1f4-c82341638be3" alt=""><figcaption></figcaption></figure></div>

<div><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FA61fgCtUj0K9dhrHCt0C%2FW%26B%20Chart%2010_12_2025%2C%203_54_40%20am.png?alt=media&amp;token=b8472635-4b05-430e-9df1-3820ed381c3f" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FPh0Xfaup0CTz8REL6P9F%2FW%26B%20Chart%2010_12_2025%2C%203_56_38%20am.png?alt=media&amp;token=86ed33c3-e5ac-4b71-82c3-f8f86ca79862" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2F6FKM6pkRkQX3gzQLdcIP%2FW%26B%20Chart%2010_12_2025%2C%203_55_38%20am.png?alt=media&amp;token=48d2c1d3-e6f2-420c-8209-70ef247ce63d" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FxbhcPeELu78xh3M01xkf%2FW%26B%20Chart%2010_12_2025%2C%203_56_07%20am.png?alt=media&amp;token=375c3f27-5af8-43e5-93eb-3818cb401f95" alt="" width="563"><figcaption></figcaption></figure></div>

### :spades:无污染打包，训练快 2-5 倍

真实数据集可能包含不同的序列长度，因此把 batch size 增加到 32 之类会导致填充，使训练更慢并消耗更多 VRAM。

{% hint style="success" %}
过去，把 `batch_size` 增大到很高的数值（>32）会让训练变得更慢，而不是更快。这是由于填充造成的——我们现在可以通过 `packing = True`来消除这个问题，因此训练更快！
{% endhint %}

当我们把多个样本打包成一个一维张量时，我们会保留序列长度元数据，以便正确地对样本做掩码，而不会在样本之间泄露注意力。我们还需要在 [#fused-qk-rope-triton-kernel-with-packing](#fused-qk-rope-triton-kernel-with-packing "mention") 中描述的 RoPE 内核来允许重置 position ids。

{% columns %}
{% column width="41.66666666666667%" %}

<div align="center" data-full-width="false"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2F508zS4YN2sYnYjYkt8ej%2Fimage.png?alt=media&amp;token=a05f917a-f593-4abd-a834-2f3f6652ca5a" alt="" width="563"><figcaption><p>4 个示例不打包会浪费空间</p></figcaption></figure></div>

{% endcolumn %}

{% column width="58.33333333333333%" %}

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2F8azEy9wbeF2RWSWbNdma%2Fimage.png?alt=media&amp;token=a4b96567-244a-45ed-8b18-b16074bac88c" alt=""><figcaption><p>无污染打包创建了正确的注意力模式</p></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

通过把 50% 短序列的比例改掉，并假设我们有更多短序列，例如 20% 长序列和 80% 长序列，我们得到 $$\frac{L}{0.2L + 0.8S}\rightarrow\frac{L}{0.2L}=5$$ 所以训练快 5 倍！这意味着打包的加速效果取决于你的数据集里有多少短行（越短越多，速度越快）。

### :beach:分析与基准测试

为了展示使用我们的新内核和打包数据训练时的各种改进，我们使用 [Qwen3-32B](/docs/zh/mo-xing/tutorials/qwen3-how-to-run-and-fine-tune.md)、Qwen3-8B、Llama 3 8B 在 `yahma/alpaca-cleaned` 数据集上进行了微调，并测量了各种 [训练损失](#padding-free-by-default) 吞吐量和效率指标。我们将新的运行与一个标准的优化训练运行进行了比较，后者开启了我们自己的内核/优化，并启用了 Flash Attention 3（FA3）等内核。我们固定 `max_length = 1024` 并将 batch size 设为 {1, 2, 4, 8, 16, 32} 进行变化。这使得每个 batch 的最大 token 数变化为 {1024, 2048, 4096, 8192, 16K, 32K}。

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FFfmdok7AmeretPSGlZjg%2Fnew%20rope%20kernel%20graph.png?alt=media&amp;token=d890fd95-c8c0-4817-9ee3-18e3095cde5f" alt="" width="563"><figcaption></figcaption></figure>

上面的图展示了新 Unsloth 在不同 batch size 下，每秒 token 数（tokens/s）训练吞吐量如何变化。这意味着你在一个数据集 epoch 上训练模型的速度会 **快 1.7-3 倍（有时甚至快 5 倍或更多）**！如果你的数据中有很多短序列，并且训练轮次更长，这些收益会更加明显，如中所述 [#why-is-padding-needed-and-mathematical-speedup](#why-is-padding-needed-and-mathematical-speedup "mention")

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FReViERxLWBHnT8GOv0ql%2Fpacking_efficiency_by_per_device_train_batch_size.png?alt=media&amp;token=1c4a78c7-a611-4374-ac03-94aabb1d3184" alt="" width="563"><figcaption></figcaption></figure>

上面的图展示了每个 batch 中有效 token（即非填充）的平均百分比。随着 batch size 的增长，在未打包的情况下会看到更多的填充 token，而在打包的情况下，无论最大序列长度如何，我们都能获得很高的打包效率。

注意，由于 batching 逻辑会将 batch 裁剪到该 batch 中看到的最大序列长度，因此当 batch size 为 1 时，未打包的数据全都是有效 token（即没有填充）。然而，随着更多示例加入 batch，平均填充会增加，在 batch size 为 8 时几乎达到 50% 的填充！我们的样本打包实现消除了这部分浪费。

<div><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FRfzNZVz9uzDPhEe3frGe%2Funknown.png?alt=media&amp;token=e9fe893e-6b94-4c0d-b144-ef8315067c1e" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FTHtcpIdQ0z0mGRYYBfuF%2Funknown.png?alt=media&amp;token=ec52b2c1-1e60-4ed2-969a-d760af26be2a" alt="" width="563"><figcaption></figcaption></figure></div>

第一张图（上图）绘制了 `yahma/alpaca-cleaned` 替换为 `max_length = 2048`上的进度，Unsloth 新版本带打包 + 内核（酒红色）与 Unsloth 旧版本（灰色）对比。两者都训练于 `max_steps = 500`，但我们把横轴绘制为实际时间。注意，在相同步数（以及只多一点实际时间）内，打包情况下我们几乎训练了一个 epoch 的 40%，而未打包情况下连 5% 的 epoch 都不到。

同样地，第二张图（上图）绘制了相同运行的损失，这次横轴是训练步数。注意，损失在规模和趋势上都匹配，但打包情况下的损失波动更小，因为模型在每个训练步骤看到的 token 更多。

### :sparkles:如何启用打包？

**先更新 Unsloth，默认就会启用无填充**！所以所有训练都会立刻快 1.1 到 2 倍，至少减少 30% 的内存占用，而且损失曲线指标没有任何变化！

{% code overflow="wrap" %}

```bash
pip install --upgrade --force-reinstall --no-cache-dir --no-deps unsloth
pip install --upgrade --force-reinstall --no-cache-dir --no-deps unsloth_zoo
```

{% endcode %}

我们还通过 Xformers 支持 Flash Attention 3，也支持 SDPA、Flash Attention 2，而且这适用于旧 GPU（Tesla T4、RTX 2080）以及新的 GPU，如 H100、B200 等！样本打包适用于 *无论选择哪种注意力后端或模型家族*，所以可以享受之前这些快速注意力实现带来的相同加速！

如果你想启用显式打包，那么添加 `packing = True` 即可实现最多 5 倍更快的训练！

{% hint style="warning" %}
注意 `packing=True` 会改变训练损失，并会让数据集行数被截断，因为多个短序列会被打包成 1 个序列。你可能会看到数据集中的示例数量减少。

如果不想得到不同的训练损失数值，只需设置 `packing=False` ，我们就会启用自动无填充，这本身就已经能让训练更快！
{% endhint %}

```python
from unsloth import FastLanguageModel
from trl import SFTTrainer, SFTConfig

model, tokenizer = FastLanguageModel.from_pretrained(
    “unsloth/Qwen3-14B”，
)

trainer = SFTTrainer(
    model = model,
    processing_class = tokenizer,
    train_dataset = dataset,
    args = SFTConfig(
        per_device_train_batch_size = 1,
        max_length = 4096,
        …,
        packing = True, # 需要启用样本打包！
    ),
)
trainer.train()
```

我们所有的 notebook 都会自动变得更快（无需做任何事情）。见 [Unsloth 笔记本](/docs/zh/kuai-su-kai-shi/unsloth-notebooks.md)

{% columns %}
{% column %}
Qwen3 14B 更快：

{% embed url="<https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen3_(14B)-Reasoning-Conversational.ipynb>" %}
{% endcolumn %}

{% column %}
Llama 3.1 对话更快：

{% embed url="<https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_(1B_and_3B)-Conversational.ipynb>" %}
{% endcolumn %}
{% endcolumns %}

谢谢你！如果你感兴趣，请查看我们的 [500K Context Training](/docs/zh/bo-ke/500k-context-length-fine-tuning.md) 博客， [内存高效的 RL](/docs/zh/kuai-su-kai-shi/reinforcement-learning-rl-guide/memory-efficient-rl.md) 博客，以及 [Long Context gpt-oss](/docs/zh/mo-xing/gpt-oss-how-to-run-and-fine-tune/long-context-gpt-oss-training.md) 博客，了解更多关于内核和性能提升的主题！


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://unsloth.ai/docs/zh/bo-ke/3x-faster-training-packing.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
