目 录CONTENT

文章目录

如何在不增加新GPU的情况下,通过优化精度、内存和数据流来加速模型训练的三个方法

Administrator
2025-10-19 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

📢 转载信息

原文链接: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
3 Ways to Speed Up Model Training Without More GPUs
Image by Editor
 

引言

训练大型模型可能会非常缓慢,人们的第一反应通常是要求更多的GPU。但是额外的硬件并不总是一个可行的选项。预算和云限制等问题会阻碍这一进程。好消息是,有一些方法可以在不增加任何GPU的情况下显著加快训练速度。

加速训练不仅仅是关于原始计算能力;它还关乎更有效地利用您已有的资源。大量的训练时间浪费在了内存交换、空闲的GPU和未优化的数据管道上。通过改善代码与硬件的通信方式,您可以从训练运行中节省数小时甚至数天的时间。

方法一:混合精度和内存优化

在不增加新GPU的情况下加速训练的最简单方法之一是使用混合精度。现代GPU设计用于比标准的32位浮点数(FP32)更快地处理半精度(FP16)或bfloat16运算。通过使用更小的数据类型进行存储和计算,您可以减少内存使用和带宽,从而一次性将更多数据装入GPU,这意味着运算可以更快完成。

核心思想很简单:

  • 对大多数运算使用较低精度(FP16BF16
  • 将关键部分(如损失缩放和少数累加操作)保留为全精度(FP32)以保持稳定性

如果操作得当,混合精度通常可以带来1.5 – 2 倍的训练速度提升,而准确率几乎没有下降。它在PyTorchTensorFlowJAX中原生支持,并且大多数NVIDIAAMDApple 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() 会自动为每次运算选择 FP16FP32
  • GradScaler() 通过动态调整损失缩放因子来防止下溢
  • GPU执行速度更快,因为它在每次运算中移动和计算的字节更少

您也可以通过PyTorch的自动混合精度(AMP)或用于传统设置的Apex 库来全局激活它。对于较新的设备(A100H100RTX 40 系列),bfloat16 (BF16) 通常比 FP16 更稳定。
内存优化与混合精度相辅相成。两种常见的技巧是:

  • 梯度检查点(Gradient checkpointing):仅保存关键激活值,并在反向传播期间重新计算其他激活值,以计算换取内存
  • 激活值卸载(Activation offloading):将不常用的张量暂时移动到CPU内存中

在PyTorch中,可以通过以下方式启用它们:

1
from torch.utils.checkpoint import checkpoint

或者使用DeepSpeedHugging Face Acceleratebitsandbytes自动配置。

何时使用:

  • 如果您的模型紧凑地装入GPU内存,或者您的批次大小较小
  • 您正在使用较新的GPU(RTX 20 系列或更新版本)
  • 您可以容忍训练期间轻微的数值差异

根据模型大小和硬件的不同,通常可以预期获得30%–100% 更快的训练速度和高达50%的内存节省

方法二:梯度累积和有效批次大小技巧

有时,训练速度的最大障碍不是计算能力,而是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内存的情况下提高稳定性和潜在的收敛速度。

重要性:

  • 更大的有效批次可以减少梯度更新中的噪声,从而改善复杂模型的收敛性
  • 您可以将其与混合精度结合使用以获得额外的好处
  • 当内存而不是计算是您的限制因素时,它尤其有效

何时使用:

  • 您遇到大批次的“内存不足”错误
  • 您希望在不更换硬件的情况下获得大批次的好处
  • 您的数据加载器或增强管道可以跟上每次更新所需的多步微小处理

方法三:智能卸载和分片训练(ZeRO)

随着模型规模的增长,GPU内存成为主要的瓶颈,甚至先于计算。您可能有能力训练模型,但没有足够的内存来同时容纳其所有参数、梯度和优化器状态。这时就需要智能卸载和分片训练了。

其思想是智能地分割和分配内存使用,而不是在每张GPU上复制所有内容。像DeepSpeedHugging 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请求之前批次已准备就绪

如果您处理大型数据集,请将它们存储在优化用于顺序读取的格式中,例如WebDatasetTFRecordParquet,而不是纯图像或文本文件。

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-smiGPUtilWeights & 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+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。

0

评论区