# 使用分布式数据并行（DDP）进行多 GPU 微调

假设我们有多张 GPU，并且想要使用所有 GPU 对模型进行微调！为此，最直接的策略是使用分布式数据并行（Distributed Data Parallel, DDP），它会在每个 GPU 设备上创建模型的一个副本，在训练期间为每个副本提供数据集中不同的样本，并在每次优化器步骤时汇总它们对权重更新的贡献。

为什么我们要这样做？随着训练中加入更多 GPU，我们在每步训练中处理的样本数量会增加，使每次梯度更新更稳定，并且随着每增加一张 GPU，大幅提高训练吞吐量。

下面是如何使用 Unsloth 的命令行界面（CLI）执行此操作的逐步指南！

**注意：** Unsloth 的 DDP 将适用于你任何的训练脚本，而不仅限于通过我们的 CLI！更多细节在下方。

#### 从源码安装 Unsloth

我们将从 GitHub 克隆 Unsloth 并进行安装。请考虑使用一个 [虚拟环境](https://docs.python.org/3/tutorial/venv.html)；我们喜欢使用 `uv venv –python 3.12 && source .venv/bin/activate`，但任何虚拟环境创建工具都可以。

```bash
git clone https://github.com/unslothai/unsloth.git
cd unsloth
pip install .
```

#### 选择用于微调的目标模型和数据集

在本演示中，我们将微调 [Qwen/Qwen3-8B](https://huggingface.co/Qwen/Qwen3-8B) 在 [yahma/alpaca-cleaned](https://huggingface.co/datasets/yahma/alpaca-cleaned) 聊天数据集上。这是一个有监督微调（Supervised Fine-Tuning，SFT）工作负载，通常用于将基础模型调整为所需的对话风格，或提高模型在下游任务上的性能。

### 使用 Unsloth CLI！

首先，让我们查看内置于 CLI 的帮助信息（我们在若干位置用“...”作了简略）：

{% code expandable="true" %}

```bash
$ python unsloth-cli.py --help
用法：unsloth-cli.py [-h] [--model_name MODEL_NAME] [--max_seq_length MAX_SEQ_LENGTH] [--dtype DTYPE]
                      [--load_in_4bit] [--dataset DATASET] [--r R] [--lora_alpha LORA_ALPHA]
                      [--lora_dropout LORA_DROPOUT] [--bias BIAS]
                      [--use_gradient_checkpointing USE_GRADIENT_CHECKPOINTING]
…

🦥 使用 unsloth 更快地微调你的大模型！

选项：
  -h, --help            显示此帮助信息并退出

🤖 模型选项：
  --model_name MODEL_NAME
                        要加载的模型名称
  --max_seq_length MAX_SEQ_LENGTH
                        最大序列长度，默认是 2048。我们内部自动支持 RoPE 缩放
                        ！
…

🧠 LoRA 选项：
  这些选项用于配置 LoRA 模型。

  --r R                 LoRA 模型的秩（rank），默认是 16。（常见取值：8、16、32、64、128）
  --lora_alpha LORA_ALPHA
                        LoRA 的 alpha 参数，默认是 16。（常见取值：8、16、32、64、128）
…

🎓 训练选项：
  --per_device_train_batch_size PER_DEVICE_TRAIN_BATCH_SIZE
                        训练时每个设备的批大小，默认是 2。
  --per_device_eval_batch_size PER_DEVICE_EVAL_BATCH_SIZE
                        评估时每个设备的批大小，默认是 4。
  --gradient_accumulation_steps GRADIENT_ACCUMULATION_STEPS
                        梯度累积步数，默认是 4。
…
```

{% endcode %}

这应该能让你了解可以传递给 CLI 以训练模型的可用选项！

对于多 GPU 训练（在本例中为 DDP），我们将使用 [torchrun](https://docs.pytorch.org/docs/stable/elastic/run.html) 启动器，它允许你在单节点或多节点环境中启动多个分布式训练进程。在我们的例子中，我们将重点放在单节点（即一台机器）情况，配备两块 H100 GPU。

让我们也通过使用 `nvidia-smi` 命令行工具检查一下 GPU 状态：

{% code expandable="true" %}

```bash
$ nvidia-smi
Mon Nov 24 12:53:00 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              驱动版本: 580.95.05      CUDA 版本: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  名称                 持久化模式 | 总线 ID           显示 A | 易失性不可纠正 ECC |
| 风扇  温度   性能          功耗:使用/上限 |           内存使用情况 | GPU 利用率  计算 模式 |
|                                         |                        |               MIG 模式 |
|=========================================+========================+======================|
|   0  NVIDIA H100 80GB HBM3          On  |   00000000:04:00.0 Off |                    0 |
| N/A   32C    P0             69W /  700W |       0MiB /  81559MiB |      0%      默认 |
|                                         |                        |             已禁用 |
+-----------------------------------------+------------------------+----------------------+
|   1  NVIDIA H100 80GB HBM3          On  |   00000000:05:00.0 Off |                    0 |
| N/A   30C    P0             68W /  700W |       0MiB /  81559MiB |      0%      默认 |
|                                         |                        |             已禁用 |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| 进程：                                                                                 |
|  GPU   GI   CI              PID   类型   进程名称                        GPU 内存 |
|        ID   ID                                                               使用      |
|=========================================================================================|
|  未发现正在运行的进程                                                               |
+-----------------------------------------------------------------------------------------+
```

{% endcode %}

太好了！我们有两块 H100 GPU，正如预期。由于目前没有进行任何训练或将模型加载到内存，两者的内存使用均为 0MiB。

要开始你的训练运行，请发出如下命令：

{% code expandable="true" %}

```bash
# 必需：
#   --model_name
#   --dataset
# 可选；可在这些参数上进行试验：
#   --learning_rate、--max_seq_length、--per_device_train_batch_size、--gradient_accumulation_steps、--max_steps
# 在训练结束时保存模型：
#   --save_model

torchrun --nproc_per_node=2 unsloth-cli.py \
  --model_name=Qwen/Qwen3-8B \
  --dataset=yahma/alpaca-cleaned \
  --learning_rate=2e-5 \
  --max_seq_length=2048 \
  --per_device_train_batch_size=1 \
  --gradient_accumulation_steps=4 \
  --max_steps=1000 \
  --save_model
```

{% endcode %}

如果你有更多的 GPU，可以相应地设置 `--nproc_per_node` 以利用它们。

**注意：** 你可以将该 `torchrun` 启动器用于你任何的 Unsloth 训练脚本，包括 [脚本](https://github.com/unslothai/notebooks/tree/main/python_scripts) 从我们的免费 Colab 笔记本转换而来，并且在使用 >1 张 GPU 训练时 DDP 会自动启用！

再次查看 `nvidia-smi` 在训练进行中时：

{% code expandable="true" %}

```bash
$ nvidia-smi
Mon Nov 24 12:58:42 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              驱动版本: 580.95.05      CUDA 版本: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  名称                 持久化模式 | 总线 ID           显示 A | 易失性不可纠正 ECC |
| 风扇  温度   性能          功耗:使用/上限 |           内存使用情况 | GPU 利用率  计算 模式 |
|                                         |                        |               MIG 模式 |
|=========================================+========================+======================|
|   0  NVIDIA H100 80GB HBM3          On  |   00000000:04:00.0 Off |                    0 |
| N/A   38C    P0            193W /  700W |   18903MiB /  81559MiB |     25%      默认 |
|                                         |                        |             已禁用 |
+-----------------------------------------+------------------------+----------------------+
|   1  NVIDIA H100 80GB HBM3          On  |   00000000:05:00.0 Off |                    0 |
| N/A   37C    P0            199W /  700W |   18905MiB /  81559MiB |     28%      默认 |
|                                         |                        |             已禁用 |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| 进程：                                                                                 |
|  GPU   GI   CI              PID   类型   进程名称                        GPU 内存 |
|        ID   ID                                                               使用      |
|=========================================================================================|
|    0   N/A  N/A            4935      C   ...und/unsloth/.venv/bin/python3      18256MiB |
|    0   N/A  N/A            4936      C   ...und/unsloth/.venv/bin/python3        630MiB |
|    1   N/A  N/A            4935      C   ...und/unsloth/.venv/bin/python3        630MiB |
|    1   N/A  N/A            4936      C   ...und/unsloth/.venv/bin/python3      18258MiB |
+-----------------------------------------------------------------------------------------+
```

{% endcode %}

我们可以看到两块 GPU 现在每块 H100 大约使用 \~19GB 的显存！

检查训练日志，我们看到能够以约 \~1.1 次迭代/秒的速度训练。即使我们增加更多 GPU，这个训练速度也大致保持恒定，因此我们的训练吞吐量会随着 GPU 数量的增加近似线性增长！

### 训练指标

我们在几个短的秩为 16 的 LoRA 微调上进行了测试，使用了 [unsloth/Llama-3.2-1B-Instruct](https://huggingface.co/unsloth/Llama-3.2-1B-Instruct) 在 [yahma/alpaca-cleaned](https://huggingface.co/datasets/yahma/alpaca-cleaned) 数据集，以演示使用多 GPU 的 DDP 训练时提高的训练吞吐量。

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FdySJnhNUzVD3gsWmPqHR%2Funknown.png?alt=media&#x26;token=9905cccb-04c8-45b1-bfb1-680823713319" alt="" width="375"><figcaption></figcaption></figure>

上图比较了两次 Llama-3.2-1B-Instruct LoRA 微调在 500 个训练步骤内的训练损失，单 GPU 训练（粉色）与多 GPU DDP 训练（蓝色）。

注意损失曲线在尺度和趋势上匹配，但在其他方面有 *些* 不同，因为 *多 GPU 训练在每步处理的训练数据是单 GPU 的两倍*。这导致了稍有不同的训练曲线，在逐步基础上具有更少的变动。

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fz4XgknzMgljaFInMEzHc%2Funknown.png?alt=media&#x26;token=4e28e2b1-8bc8-4049-983d-e4f980f3f4cf" alt="" width="375"><figcaption></figcaption></figure>

上图绘制了相同两次微调的训练进展。

注意多 GPU 的 DDP 训练在遍历一轮训练数据时所需的步数仅为单 GPU 训练的一半。这是因为每个 GPU 可以在每步处理一个独立的小批次（大小为 `per_device_train_batch_size`）。然而，由于模型权重更新需要分布式通信，DDP 训练的每步时间略慢。随着 GPU 数量的增加，训练吞吐量会继续近似线性增长（但分布式通信会带来一个小的且逐渐增大的开销）。

这些相同的损失和训练轮次进度行为也适用于 QLoRA 微调，在 QLoRA 中我们以 4 位精度加载基础模型以节省更多的 GPU 内存。这在 GPU 显存有限的情况下训练大型模型时尤其有用：

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FUrCEgA7OBVhc8ICkMaP6%2Funknown.png?alt=media&#x26;token=0f5de3df-77df-4ee5-bf7a-68dead857c9a" alt="" width="375"><figcaption></figcaption></figure>

在 500 个训练步骤内比较两次 Llama-3.2-1B-Instruct QLoRA 微调的训练损失，单 GPU 训练（橙色）与多 GPU DDP 训练（紫色）。

<figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2F8cG6rjmjeznNfgWrYdnG%2Funknown.png?alt=media&#x26;token=d1c2c1fe-c117-49b5-8e9d-fdc01154cc01" alt="" width="375"><figcaption></figcaption></figure>

相同两次微调的训练进度比较。
