type
status
date
slug
summary
tags
category
icon
password
模型权重显存消耗
模型显存占用分类
模型半精度混合训练阶段,每张卡中显存占用可以分为两类:
- 模型状态模型参数(fp16)、模型梯度(fp16)和 Adam 优化器状态(fp32 的模型参数备份,fp32 的 momentum 和 fp32 的 variance )。 假设模型参数量 ,则共需要 字节存储。
所以 全量微调时,每增加 1B 参数,需要增加 16GB 的显存来存储模型状态。
简单来说就是:只有权重是FP32的,前向计算和反向计算都用FP16,在运行时进行转换
比如执行前向计算时,权重从FP32转成FP16,得到loss之后,用FP16计算梯度,再转成FP32更新到FP32的权重上
用FP32保存权重不是必须的(optional),主要是为了避免溢出,如果加上fp32的梯度总显存会变成
- 剩余状态
除了模型状态之外的显存占用,包括激活值、各种临时缓冲区以及无法使用的显存碎片。
跟
cutoff_len
与 batch_size
很大关系deepspeed ZeRo显存计算
ZeRO 策略只优化模型状态显存占用, 从 ZeRO-1 到 ZeRO-3 优化等级越来越高。
- ZeRO-1 策略针对优化器状态进行分片,模型参数和梯度仍旧是每张卡保持一份,此时,每张卡的模型状态所需显存是 ( N 为 GPU 数目)
- ZeRO-2 策略针对模型梯度进行分片,模型参数仍旧是每张卡保持一份,此时,每张卡的模型状态所需显存是 ( N 为 GPU 数目)
- ZeRO-3 策略针对模型参数进行分片,此时每张卡的模型状态所需显存是 ( N 为 GPU 数目)
以 7B 模型 + 8 GPUs 全量微调为例:
- ZeRO-1 模式下,每张卡上模型状态显存占用约为
- ZeRO-2 模式下,每张卡上模型状态显存占用约为
- ZeRO-3 模式下,每张卡上模型状态显存占用约为
offload_optimizer
:用于将一部分计算任务从GPU转移到CPU,从而减少GPU内存的使用。启用此选项需要stage 2
从左到右训练速度越来越慢,而GPU显存占用越来越低:
Stage 0 (DDP) , Stage 1 , Stage 2 , Stage 2 + offload , Stage 3 , Stage 3 + offloads
在不 OOM 的前提下,使用训练速度快的
deepspeed 参数解释
offload_optimizer
:用于将一部分计算任务从GPU转移到CPU,从而减少GPU内存的使用。启用此选项需要stage 2;
bf16
:开启bf16,可以表示更广的数值范围。但尾数只有7位,所以精度较低
pin_memory
:从CPU到GPU的数据传输速度相对较慢。启用此功能后,将尝试锁定数据在CPU内存中,以提高数据传输效率
overlap_comm
:减少通信延迟,消耗更多内存。- 显存大小:如果如果显存是
8G
,而这两个梯度桶都设置为5e8
,那么需要9GB
的GPU内存空间来执行这个操作(),这会导致OOM(显存溢出)。将其减少至2e8
,则占用的显存是3.6G
。 - batch size:如果训练中某些参数对你很重要,比如你需要更大的batch size,那么也可以设置较小的梯度桶,已分配给其它任务更多的显存,即使这会导致训练时间变慢。
启用此选项时会使用4.5倍的
allgather_bucket_size
和reduce_bucket_size
值,即增这两个阶段中梯度桶的大小。梯度被分片的次数减少,梯度通信次数减少,所以通信延迟降低了。然而,这也会占用更多的GPU内存,因此需要权衡内存使用和通信速度。比如:通信原语小Tips
在数据并行中,训练数据被分成多个批次,并分配给不同的设备。每个设备上的模型副本独立处理自己的数据批次,并计算梯度(因为是在本设备上计算,所以叫本地梯度)。然后,通过All-Reduce(全体度求和),这些梯度被聚合到一个global gradients中,然后用于更新模型参数。这种通信和梯度聚合的方式确保了所有设备上的模型保持同步。
all-reduce包含两个操作,reduce-scatter和all-gather,都是在各设备之间进行梯度分桶、广播和聚合操作。之所以要分桶,是因为在大规模训练中,GPU的数量可能非常大,一次性将不同GPU的本地梯度广播给所有GPU可能导致大量的数据流通信,也会占用大量的GPU内存。通过分桶操作,每次只传递local gradients的部分数据,可以减轻网络负载和内存压力,这样训练可以扩展到更多的GPU或更大的模型规模。
round_robin_gradients
: 优化CPU offload性能。通过细粒度的梯度划分将梯度复制到CPU内存中,以实现CPU和GPU之间的并行处理。CPU offload性能随着梯度累积步骤和CPU数量的增加而增加
gradient_accumulation_steps
:梯度累积
train_micro_batch_size_per_gpu
:单个GPU上每次处理的micro_batch_size大小
train_batch_size
:全局batch size,它等于train_micro_batch_size_per_gpu*gradient_accumulation_steps*GPUs number
。也就是你可以只设置train_micro_batch_size_per_gpu,它会根据GPU数量和梯度累积值,自动算出全局batch size。
wall_clock_breakdown
:默认关闭。开启时,DeepSpeed会详细记录以下每个训练步的时间:
sub_group_size
:控制参数更新的粒度,防止由于内存不足而导致训练中断。
stage3_max_live_parameters
:GPU上保留的参数数量的上限。如果超过这个数量,一些参数可能会被卸载到CPU内存中。
stage3_max_reuse_distance
:参数重用的距离阈值。如果一个参数在不久的将来要再次使用(小于 stage3_max_reuse_distance),可以将其保留以减少通信开销。 使用activation checkpointing时,这一点非常有用
如果遇到OOM,可以减少
stage3_max_live_parameters
和 stage3_max_reuse_distance
,除非正在使用activation checkpointing。这两个值设为1e9
时,二者一共消耗2GB
内存。stage3_gather_16bit_weights_on_model_save
:允许在模型保存时以16位精度收集并保存权重,以减少内存占用和加速训练,特别适用于大型模型和多GPU训练的情况,但可能会导致一定的精度损失。
sub_group_size
:控制在optimizer steps中更新参数的粒度。
参数被分组到 sub_group_size 的桶中,每个桶一次更新一个。 当与 ZeRO-Infinity 中的 NVMe offload一起使用时,sub_group_size 控制模型状态在optimizer steps期间从 NVMe 移入和移出 CPU 内存的粒度, 防止超大模型耗尽 CPU 内存。
不使用NVMe offload时,使其保持默认值。出现OOM时,减小sub_group_size。当优化器迭代很慢时,可以增大sub_group_size 。
- 作者:SimonSun
- 链接:https://simons-blog-eight.vercel.app//article/llm-4
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。