📢 转载信息
原文链接:https://machinelearningmastery.com/3-ways-to-speed-up-model-training-without-more-gpus/
原文作者:Shittu Olumide
在本文中,您将学习三种经过验证的方法来加速模型训练,方法是通过优化精度、内存和数据流——而无需增加任何新的GPU。
我们将涵盖的主题包括:
- 混合精度和内存技术如何安全地提高吞吐量
- 使用梯度累积来使用更大的“虚拟”批次进行训练
- 使用ZeRO进行分片和卸载,以便在现有硬件上容纳更大的模型
我们不要再浪费时间了。
3 Ways to Speed Up Model Training Without More GPUs
图片作者:Editor
引言
训练大型模型可能非常缓慢,人们的第一反应往往是要求增加更多的GPU。但额外的硬件并非总是一个选择。预算和云限制等问题会成为障碍。好消息是,有方法可以在不增加任何GPU的情况下显著加快训练速度。
加速训练不仅关乎原始计算能力;它还关乎更高效地利用您已有的资源。大量的训练时间被浪费在内存交换、空闲的GPU和未优化的数据管道上。通过改善代码和硬件的通信方式,您可以从训练运行中节省数小时甚至数天的时间。
方法 1:混合精度和内存优化
在不增加新GPU的情况下加速训练的最简单方法之一是使用混合精度。现代GPU能比标准的32位浮点数更快地处理半精度(FP16)或bfloat16运算。通过使用更小的数据类型进行存储和计算,可以减少内存使用和带宽,从而一次性将更多数据装入GPU,这意味着操作完成得更快。
其核心思想很简单:
- 对大多数操作使用较低的精度(FP16 或 BF16)
- 将关键部分(如损失缩放和少量累加)保留在全精度(FP32)下以保持稳定性
如果操作得当,混合精度通常能带来1.5 – 2 倍的训练速度提升,同时准确率几乎没有下降。它原生支持于PyTorch、TensorFlow 和 JAX,并且大多数NVIDIA、AMD 和 Apple GPU 现在都为此提供了硬件加速。
以下是一个启用自动混合精度的 PyTorch 示例:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# Mixed Precision Example (PyTorch)
import torch
from torch import nn, optim
from torch.cuda.amp import GradScaler, autocast
model = nn.Linear(512, 10).cuda()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
scaler = GradScaler()
for inputs, targets in dataloader:
optimizer.zero_grad()
with autocast(): # operations run in lower precision
outputs = model(inputs.cuda())
loss = nn.functional.cross_entropy(outputs, targets.cuda())
scaler.scale(loss).backward() # scaled to prevent underflow
scaler.step(optimizer)
scaler.update()
|
为什么有效:
autocast()会自动为每个操作选择 FP16 或 FP32GradScaler()通过动态调整损失比例来防止下溢- GPU执行速度更快,因为它在每次操作中移动和计算的字节更少
您还可以使用 PyTorch 的自动混合精度 (AMP) 或 Apex 库(用于旧设置)在全局激活它。对于较新的设备(如A100、H100、RTX 40 系列),bfloat16 (BF16) 通常比 FP16 更稳定。
内存优化与混合精度相辅相成。两种常见的技巧是:
- 梯度检查点 (Gradient checkpointing):仅保存关键激活,并在反向传播过程中重新计算其他激活,以计算换取内存
- 激活值卸载 (Activation offloading):暂时将很少使用的张量移动到 CPU 内存
这些可以在 PyTorch 中通过以下方式启用:
|
1
|
from torch.utils.checkpoint import checkpoint
|
或者使用 DeepSpeed、Hugging Face Accelerate 或 bitsandbytes 自动配置。
何时使用:
- 如果您的模型刚好能装入 GPU 内存,或者您的批次大小较小
- 您使用的是较新的 GPU(RTX 20 系列或更新版本)
- 您可以容忍训练期间轻微的数值变化
根据模型大小和硬件的不同,通常可以预期获得 30–100% 更快的训练速度和最高 50% 的内存使用量减少。
方法 2:梯度累积和有效批次大小技巧
有时,阻碍训练速度的最大障碍不是计算能力,而是GPU 内存。您可能希望使用大批次来提高梯度稳定性,但 GPU 在您达到该批次大小之前就耗尽内存了。
梯度累积可以完美解决这个问题。您不必一次性处理一个巨大的批次,而是将其分成更小的微批次。您对每个微批次运行前向和后向传播,累积梯度,并且只在经过多次迭代后才更新模型权重。这使您能够在不改变硬件的情况下模拟大批次训练。
以下是 PyTorch 中的实现方式:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# Gradient Accumulation Example (PyTorch)
import torch
from torch import nn
from torch.cuda.amp import GradScaler, autocast
# Assumes `model`, `optimizer`, and `dataloader` are defined elsewhere
criterion = nn.CrossEntropyLoss()
scaler = GradScaler()
accum_steps = 4 # accumulate gradients over 4 mini-batches
for i, (inputs, targets) in enumerate(dataloader):
with autocast(): # works nicely with mixed precision
outputs = model(inputs.cuda())
loss = criterion(outputs, targets.cuda()) / accum_steps # normalize
scaler.scale(loss).backward()
if (i + 1) % accum_steps == 0:
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad(set_to_none=True)
|
工作原理:
- 损失除以累积步数,以保持梯度平衡
- 梯度在各步骤之间存储在内存中,而不是被清除
- 在
accum_steps个微批次之后,优化器执行一次更新
这个简单的改变使您能够使用多达四到八倍大的虚拟批次大小,从而在不超出 GPU 内存的情况下提高稳定性和潜在的收敛速度。
重要性:
- 更大的有效批次可以减少梯度更新中的噪声,从而提高复杂模型的收敛性
- 您可以将此与混合精度结合使用以获得额外的好处
- 当限制因素是内存而不是计算时,它特别有效
何时使用:
- 您遇到了大批次时的“内存不足”错误
- 您希望在不更换硬件的情况下获得大批次的好处
- 您的数据加载器或增强管道能够跟上每次更新所需进行的多个微步骤
方法 3:智能卸载和分片训练 (ZeRO)
随着模型规模的增大,GPU 内存成为主要瓶颈,远早于计算能力。您可能拥有训练模型的原始能力,但没有足够的内存来同时容纳其所有参数、梯度和优化器状态。这就是智能卸载和分片训练发挥作用的地方。
其思想是智能地拆分和分配内存使用,而不是在每个 GPU 上复制所有内容。DeepSpeed 和 Hugging Face Accelerate 等框架通过诸如 ZeRO(Zero Redundancy Optimizer,零冗余优化器)之类的技术来实现这一点。
ZeRO 如何工作
通常,多 GPU 设置中的每个 GPU 都保存一份完整的模型参数、梯度和优化器状态的副本。对于大型模型来说,这非常浪费。ZeRO 通过在设备间分片这些状态来打破这种复制:
- ZeRO 阶段 1:分片优化器状态
- ZeRO 阶段 2:分片优化器状态和梯度
- ZeRO 阶段 3:分片所有内容,包括模型参数
现在,每个 GPU 只保存总内存占用的一部分,但它们仍然协同工作以计算完整的更新。这使得原本大于单个 GPU 内存容量的模型能够高效地进行训练。
简单的示例(DeepSpeed)
下面是一个启用 ZeRO 优化的基本 DeepSpeed 配置片段:
|
1
2
3
4
5
6
7
8
9
|
{
"train_batch_size": 64,
"fp16": { "enabled": true },
"zero_optimization": {
"stage": 2,
"offload_optimizer": { "device": "cpu", "pin_memory": true },
"offload_param": { "device": "cpu" }
}
}
|
然后在您的脚本中:
|
1
2
|
import deepspeed
model, optimizer, _, _ = deepspeed.initialize(model=model, optimizer=optimizer, config='ds_config.json')
|
它的作用:
- 为更快的计算启用混合精度(fp16)
- 激活ZeRO 阶段 2,跨设备分片优化器状态和梯度
- 在 GPU 内存紧张时将不使用的张量卸载到 CPU 内存
何时使用:
- 您正在训练一个大型模型(数亿或数十亿参数)
- 即使使用混合精度,您也会耗尽 GPU 内存
- 您正在使用多个 GPU 或分布式节点
附加技巧
上述三种主要方法——混合精度、梯度累积和 ZeRO 卸载——提供了在不增加硬件的情况下可以实现的大部分性能提升。但是,有一些更小的、经常被忽视的优化措施可以产生明显的不同,尤其是在与主要方法结合使用时。
让我们来看看在几乎所有训练设置中都适用的几个优化。
1. 优化数据管道
GPU 利用率通常会下降,因为在准备好下一个批次之前,模型已经完成了计算。解决方法是并行化和预取数据。
在 PyTorch 中,您可以通过调整 DataLoader 来提高数据吞吐量:
|
1
|
train_loader = DataLoader(dataset, batch_size=64, num_workers=8, pin_memory=True, prefetch_factor=4)
|
num_workers使用多个 CPU 线程进行加载pin_memory=True加速主机到 GPU 的传输prefetch_factor确保在 GPU 请求之前批次已准备就绪
如果您处理大型数据集,请将它们存储在针对顺序读取优化的格式中,例如 WebDataset、TFRecord 或 Parquet,而不是纯图像或文本文件。
2. 优化前进行分析
在应用高级技术之前,请找出您的训练循环实际花费时间的地方。框架提供了内置分析器:
您通常会发现最大的瓶颈不是 GPU,而是像数据增强、日志记录或缓慢的损失计算等问题。修复这些问题可以立即加速,而无需进行任何算法更改。
3. 使用提前停止和课程学习
并非所有样本在整个训练过程中都做出同等贡献。提前停止 (Early stopping) 可防止在性能趋于平稳后进行不必要的周期。课程学习 (Curriculum learning) 从更简单的示例开始训练,然后引入更难的示例,帮助模型更快地收敛。
|
1
2
3
4
|
if validation_loss > best_loss:
patience_counter += 1
if patience_counter >= patience_limit:
break # early stop
|
这个小小的模式可以在大型数据集上节省数小时的训练时间,而对准确率的影响极小。
4. 定期监控内存和利用率
了解模型实际使用了多少内存有助于您平衡批次大小、累积和卸载。在 PyTorch 中,您可以使用以下方法记录 GPU 内存统计信息:
|
1
|
print(f"Max memory used: {torch.cuda.max_memory_allocated() / 1e9:.2f} GB")
|
像 nvidia-smi、GPUtil 或 Weights & Biases 系统指标这样的监控工具可以帮助您尽早发现利用率不足的 GPU。
5. 智能组合技术
最大的收获来自于堆叠这些策略:
- 混合精度 + 梯度累积 = 更快、更稳定的训练
- ZeRO 卸载 + 数据管道优化 = 在没有内存错误的情况下训练更大的模型
- 提前停止 + 分析 = 减少浪费的周期
何时使用每种方法
为了更方便地决定哪种方法适合您的设置,以下是迄今涵盖的三种主要技术的比较总结表,包括其预期收益、最佳适用场景和权衡。
| 方法 | 最适合 | 如何提供帮助 | 典型速度提升 | 内存影响 | 复杂性 | 关键工具/文档 |
|---|---|---|---|---|---|---|
| 混合精度与内存优化 | 任何刚好能装入 GPU 内存的模型 | 使用较低精度(FP16/BF16)和更轻的张量来减少计算和传输开销 | 1.5 – 2 倍更快的训练 | 减少 30–50% 内存 | 低 | PyTorch AMP, NVIDIA Apex |
| 梯度累积与有效批次大小 | 受 GPU 内存限制但需要大批次大小的模型 | 通过在较小批次中累积梯度来模拟大批次训练 | 提高收敛稳定性;通过更少的重启实现间接速度提升 | 中等额外内存(临时梯度) | 低 – 中 | DeepSpeed 文档, PyTorch 论坛 |
| 智能卸载与分片训练 (ZeRO) | 无法装入 GPU 内存的超大模型 | 跨设备或 CPU 分片优化器状态、梯度和参数 | 10–30% 吞吐量提升;训练 2–4 倍更大的模型 | 释放大部分 GPU 内存 | 中 – 高 | DeepSpeed ZeRO, Hugging Face Accelerate |
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区