- DeepSpeed 教程
- DeepSpeed - 首页
- DeepSpeed - 入门指南
- DeepSpeed - 模型训练
- DeepSpeed - 优化器
- DeepSpeed - 学习率调度器
- DeepSpeed - 分布式训练
- DeepSpeed - 内存优化
- DeepSpeed - 混合精度训练
- DeepSpeed 有用资源
- DeepSpeed - 资源
- DeepSpeed - 讨论
使用 DeepSpeed 进行内存优化
随着深度学习模型复杂性和大规模计算的不断增长,内存优化在训练过程中至关重要。DeepSpeed 提供了多种节省内存的技术,例如卸载、梯度检查点和 ZeRO。开发人员可以基于这些节省内存的技术,在标准硬件上训练非常庞大的模型。此外,这些技术还可以训练以前受硬件限制的模型。
DeepSpeed 不仅在研究领域取得了令人瞩目的进步,在工业领域也同样取得了成功,因此已成为深度学习从业者不可或缺的工具。这意味着通过这些策略,您可以使模型消耗更少的内存,并真正突破硬件的限制。
为什么需要内存优化?
内存优化是训练深度学习模型中最关键的组成部分之一。对于像 GPT 和 BERT 这样拥有数十亿参数的模型,在可用硬件上进行训练时,必须有效地管理内存。DeepSpeed 是一个开源库,用于训练深度学习模型,它具有 ZeRO 优化器、卸载技术和梯度检查点等功能,以避免训练过程中的主要内存占用。
深度学习中的内存问题
深度学习模型的规模和复杂性近年来显著增加。这些大型模型需要大量的内存进行训练。微软开发的深度学习优化库 DeepSpeed 为这些挑战提供了强大的解决方案。
深度学习模型有点像艺术,它们代表着新兴事物,随着模型规模的增长,内存相关的问题也会随之出现。一些最常见的内存相关问题包括:
- 模型参数 - 大型模型,如 GPT-3,拥有数千亿个参数,因此需要大量的内存来存储。
- 梯度 - 在训练过程中计算每个参数的梯度也必须计算并保存在内存中,这会消耗更多的内存。
- 激活映射 - 前向传递过程中产生的所有中间值都需要存储,直到反向传递仅用于梯度计算,这称为激活映射。
- 批大小 - 更大的批大小可以提高收敛速度,但会消耗更多的内存。
- 数据并行 - 将数据在多个 GPU 之间进行分发是减少训练时间的好策略,但毫无疑问,它确实会消耗大量的内存,除非得到控制。
除非识别出这些陷阱,否则即使在消费级硬件上训练大型模型也变得不可能。DeepSpeed 通过使用创新的节省内存技术克服了这些挑战。
DeepSpeed 的内存优化技术
DeepSpeed 在训练模型时有多种方法可以优化内存使用。一些方法包括 ZeRO(零冗余优化器)、梯度检查点和激活重计算。
1. 零冗余优化器 (ZeRO)
ZeRO 主要关注在优化器状态、梯度和模型参数的冗余副本被移除的地方进行内存优化。ZeRO 经历以下三个阶段:
- 阶段 1 - 将优化器状态跨 GPU 分片,每个 GPU 存储一部分优化器状态。
- 阶段 2 - 进一步减少内存,因为梯度跨 GPU 分片。
- 阶段 3 - 模型参数被分片,现在可以训练高达万亿参数的模型。
示例
import deepspeed model = MyModel() # your dl model optimizer = torch.optim.Adam(model.parameters(), lr=1e-4) # DeepSpeed configuration for ZeRO ds_config = { "train_batch_size": 8, "zero_optimization": { "stage": 2, # adjust the stage of ZeRO here "allgather_partitions": True, "reduce_scatter": True, "allgather_bucket_size": 5e8, "overlap_comm": True, "contiguous_gradients": True } } # Initialize DeepSpeed model_engine, optimizer, _, _ = deepspeed.initialize( model=model, optimizer=optimizer, config_params=ds_config ) # Training loop for batch in train_dataloader: loss = model_engine(batch) model_engine.backward(loss) model_engine.step()
您会注意到内存使用量大大降低,特别是对于大型模型。然后,内存分析器可以突出显示 ZeRO 优化启动的位置。
2. 梯度检查点
梯度检查点通过不在缓冲区中存储前向传递期间的激活来减少内存。相反,它们在反向传递中被重建,牺牲一点计算来节省一些内存。
示例
import torch from torch.utils.checkpoint import checkpoint def custom_forward(*inputs): return model(*inputs) # Gradient checkpointing outputs = checkpoint(custom_forward, input_data) loss = criterion(outputs, labels) loss.backward()
在这种情况下,节省的内存将取决于中间激活的大小。
3. 卸载技术
DeepSpeed 还提供另一种形式的卸载。它允许您将模型的部分内容(如优化器状态和梯度)移动到 CPU 甚至 NVMe 存储器,从而释放 GPU 内存以供其他用途使用。
CPU 卸载
DeepSpeed 允许我们将优化器状态和梯度卸载到 CPU。这也释放了宝贵的 GPU 内存。如果 GPU 上的内存有限,但 CPU 上的内存相当多,这将非常有用。
示例
ds_config = { "train_batch_size": 8, "zero_optimization": { "stage": 2, "offload_optimizer": { "device": "cpu", "pin_memory": True }, "offload_param": { "device": "cpu", "pin_memory": True } } } model_engine, optimizer, _, _ = deepspeed.initialize( model=model, optimizer=optimizer, config_params=ds_config )
由于卸载到 CPU 的传输涉及设备间通信成本,因此训练速度相对较慢,但在那些原本无法适应内存受限 GPU 的模型大小上,它仍然很有用。
NVMe 卸载
对于大型模型,这还不够。DeepSpeed 还将优化器状态和梯度卸载到 NVMe 存储器。这将进一步提高模型的规模,即使在训练中也能实现。
示例
ds_config = { "train_batch_size": 8, "zero_optimization": { "stage": 2, "offload_optimizer": { "device": "nvme", "nvme_path": "/local_nvme" }, "offload_param": { "device": "nvme", "nvme_path": "/local_nvme" } } } model_engine, optimizer, _, _ = deepspeed.init( model=model, optimizer=optimizer, config_params=ds_config )
使用 NVMe 进行卸载将能够训练海量模型,尽管速度将在很大程度上取决于 NVMe 驱动器的 I/O 速度。
内存优化案例研究
让我们讨论一些使用 DeepSpeed 进行内存优化的真实案例研究 -
案例研究 1:使用 ZeRO 优化训练 GPT-2
使用 DeepSpeed,一个研究团队将这个拥有 15 亿个参数的 GPT-2 模型的训练扩展到了消费级 GPU。使用 ZeRO 阶段 3,可以在 4 个 NVIDIA RTX 3090 GPU 上对其进行训练,每个 GPU 的总内存为 24 GB。如果没有使用 ZeRO,训练将是不可能的,因为该模型每个 GPU 需要超过 50 GB 的内存。
案例研究 2:使用 NVMe 卸载 1750 亿参数模型
微软利用 DeepSpeed 的卸载功能,在一个内存有限的 GPU 集群上训练了一个 1750 亿参数的模型。在模型训练期间,几乎没有内存瓶颈来卸载优化器状态和参数,这表明即使 GPU 资源有限,卸载也可以为超大型模型铺平道路。