【大模型训练】zero 学习及deepseed实战

文章目录

  • [1. ZeRO-1 流程。,特别是关于通信量和为什么 `Reduce-Scatter` 就足够了的思考。](#1. ZeRO-1 流程。,特别是关于通信量和为什么 Reduce-Scatter 就足够了的思考。)
      • 图一:前向与后向传播阶段 (Forward & Backward)
      • 图二:梯度同步与参数更新阶段 (Gradient Sync & Weight Update)
        • [**步骤 (2): 梯度同步 (Gradient Synchronization)**](#步骤 (2): 梯度同步 (Gradient Synchronization))
        • [**对通信量的思考:为什么 `Reduce-Scatter` 就够了?**](#对通信量的思考:为什么 Reduce-Scatter 就够了?)
        • [**步骤 (3): 参数更新 (Weight Update)**](#步骤 (3): 参数更新 (Weight Update))
        • [**缺失的最后一步:参数同步 (Weight Synchronization)**](#缺失的最后一步:参数同步 (Weight Synchronization))
      • [完整 ZeRO-1 流程总结](#完整 ZeRO-1 流程总结)
  • [2. 在混合精度训练的前提下,这边我们对【只能将相应的W(蓝色部分)进行更新】这句话做更严谨的说明, 什么意思?](#2. 在混合精度训练的前提下,这边我们对【只能将相应的W(蓝色部分)进行更新】这句话做更严谨的说明, 什么意思?)
      • [1. 混合精度训练的标准流程](#1. 混合精度训练的标准流程)
      • [2. 将混合精度训练与 ZeRO-1 结合](#2. 将混合精度训练与 ZeRO-1 结合)
      • [3. 对"只能将相应的W(蓝色部分)进行更新"的严谨说明](#3. 对“只能将相应的W(蓝色部分)进行更新”的严谨说明)
  • 代码实战
  • 3.火焰图分析
      • 图表解读 (时间线分析)
        • [**阶段一:时间 0ms - ~280ms (静态内存布局)**](#阶段一:时间 0ms - ~280ms (静态内存布局))
        • [**阶段二:时间 ~280ms - ~420ms (反向传播 -> 梯度分配)**](#阶段二:时间 ~280ms - ~420ms (反向传播 -> 梯度分配))
        • [**阶段三:时间 ~420ms - ~480ms (优化器步骤)**](#阶段三:时间 ~420ms - ~480ms (优化器步骤))
        • [**阶段四:时间 ~480ms 之后 (后续的训练步骤)**](#阶段四:时间 ~480ms 之后 (后续的训练步骤))
      • 总结:为什么图是这个样子的?
      • [1. `model_engine.step()` 是在什么时候执行的?](#1. model_engine.step() 是在什么时候执行的?)
      • [2. 蓝色块和"10条":是什么?为什么这么大?](#2. 蓝色块和“10条”:是什么?为什么这么大?)
        • [A. 巨大的蓝色块 (GRADIENT)](#A. 巨大的蓝色块 (GRADIENT))
        • [B. "10条"细长的尖峰 (多个训练 Step)](#B. “10条”细长的尖峰 (多个训练 Step))
      • [第一步:计算我们代码中 `SimpleModel` 的总参数量](#第一步:计算我们代码中 SimpleModel 的总参数量)
      • [第二步:根据参数量计算 `SimpleModel` 的梯度大小](#第二步:根据参数量计算 SimpleModel 的梯度大小)
      • 第三步:对比理论计算与图中观测结果
      • 最终结论
  • 问题定位
  • 稳定后内存分析
  • 启动前内存
  • 稳定后

https://zhuanlan.zhihu.com/p/618865052
学习这篇文章遇到的问题

https://docs.pytorch.org/memory_viz 分析pickle 内存占用

torch memory 工具使用https://pytorch.org/blog/understanding-gpu-memory-1/

https://pytorch.cadn.net.cn/docs_en/2.5/torch_cuda_memory.html

chrome://tracing/ json 分析

1. ZeRO-1 流程。,特别是关于通信量和为什么 Reduce-Scatter 就足够了的思考。


图一:前向与后向传播阶段 (Forward & Backward)

这张图清晰地展示了 ZeRO-1 在进行 loss.backward() 之后,但在 optimizer.step() 之前的状态。

  • GPU 数量: N=3 (GPU1, GPU2, GPU3)
  • 输入数据 (X) : 一个 batch 的数据被分成了三份 (X1, X2, X3),每个 GPU 处理一份。这是数据并行 (Data Parallelism) 的核心。
  • 模型参数 (W) : 每个 GPU 都拥有一份完整的、相同的模型参数副本 (W)。这是标准数据并行和 ZeRO-1/2 的共同点。在图中用 FP16 表示,是混合精度训练的体现。
  • 优化器状态 (O) : 这是 ZeRO-1 的关键创新点! 优化器状态被分区 (Partitioned)
    • GPU1 只存储优化器状态的前 1/3 (O1)。
    • GPU2 只存储中间的 1/3 (O2)。
    • GPU3 只存储最后的 1/3 (O3)。
    • 由于优化器状态 (O) 是 FP32,并且通常是参数量的两倍(Adam),这个分区操作极大地节省了每个 GPU 的显存。
  • 梯度 (G) :
    • 经过前向传播 (forward) 和后向传播 (backward),每个 GPU 都基于自己的数据分片 (X1, X2, X3) 计算出了一份完整的梯度
    • 我们称之为本地梯度 G1, G2, G3。这三份梯度内容不同,但形状和大小都与完整模型参数 W 相同。

小结: 图一的状态是"万事俱备,只欠同步"。每个 GPU 都有了本地梯度,但需要将这些本地梯度聚合成一个全局一致的梯度,才能进行统一的参数更新。


图二:梯度同步与参数更新阶段 (Gradient Sync & Weight Update)

这张图展示了 optimizer.step() 内部发生的核心操作。

步骤 (2): 梯度同步 (Gradient Synchronization)
  • 操作 : 对本地梯度 G1, G2, G3 执行一次 All-Reduce 操作。
  • All-Reduce 的过程 :
    1. Reduce : 所有 GPU 将它们的本地梯度相加,得到一个全局的总梯度 G_sum = G1 + G2 + G3
    2. Broadcast : 将这个 G_sum (或者除以N后的平均梯度 G_avg) 分发回每一个 GPU。
  • 结果 : All-Reduce 完成后,GPU1, GPU2, GPU3 上都拥有了同一份完整的、全局同步好的梯度 G
  • 通信量 :
    • 如您所述,一次高效的 Ring-AllReduce 的通信量大约是 2 * (N-1)/N * M,其中 M 是梯度张量的总大小。为了简化,我们常说它与模型参数量成正比,约为 2 * M
对通信量的思考:为什么 Reduce-Scatter 就够了?

这是您提到的一个非常深刻的洞见!

  • All-Reduce 的问题 : 它给了每个 GPU 一份完整的梯度 G,但实际上每个 GPU 真的需要完整的 G 吗?
  • 回顾 ZeRO-1 的本质 : GPU1 只负责更新 W 的前 1/3,因为它只持有 O1。因此,理论上 GPU1 只需要 G 的前 1/3 就可以完成它的更新任务。
  • Reduce-Scatter 的优势 : 这个操作正是为此而生。它会计算出全局总梯度 G_sum,然后立即将其切分 ,只把每个 GPU 需要的那一部分发回给它。
    • GPU1 收到 G_sum 的前 1/3。
    • GPU2 收到 G_sum 的中间 1/3。
    • GPU3 收到 G_sum 的后 1/3。
  • Reduce-Scatter 的通信量 : 大约是 (N-1)/N * M,几乎是 All-Reduce一半
  • 结论 : 没错,对于 ZeRO-1 来说,使用 Reduce-Scatter 来同步梯度是完全足够且更高效 的。这实际上已经预示了 ZeRO-2 的核心思想!可以说,ZeRO-2 就是将 ZeRO-1 中这个"理论上可以但早期没实现"的优化给正式化和系统化了。
步骤 (3): 参数更新 (Weight Update)
  • 操作 : 每个 GPU 使用它拥有的完整的全局梯度 G 和它自己保管的部分的优化器状态 O,来更新它所负责的那部分参数 W。
  • GPU1 :
    • 输入: 完整的 G, 部分的状态 O1。
    • 计算: 使用 O1 和 G 的前 1/3,计算出 W 的前 1/3 的更新量。
    • 更新: 将 W 的前 1/3(图中蓝色部分)进行更新。
  • GPU2 和 GPU3: 同理,分别更新 W 的中间 1/3 和最后 1/3。
  • 问题出现 : 更新后,每个 GPU 上的 W 变得不一致 了!
    • GPU1 上的 W: [W1_new, W2_old, W3_old]
    • GPU2 上的 W: [W1_old, W2_new, W3_old]
    • GPU3 上的 W: [W1_old, W2_new, W3_new]
缺失的最后一步:参数同步 (Weight Synchronization)

为了开始下一次的前向传播(它要求所有 GPU 上的模型参数 W 必须是相同的),我们还需要最后一步,这在图二中没有画出来:

  • 操作 : 对更新后的、不一致的 W 执行一次 All-Gather 操作。
  • 过程: 每个 GPU 将自己更新的那一小部分 W 发送给所有其他 GPU。
  • 结果 : All-Gather 结束后,所有 GPU 都重新拥有了一份完整的、最新的、完全一致的模型参数 W。
  • 通信量 : All-Gather 的通信量也大约是 (N-1)/N * M

完整 ZeRO-1 流程总结

  1. Fwd/Bwd : 各自计算本地完整梯度 Gi
  2. 梯度同步 : All-Reduce 梯度 (或者更优的 Reduce-Scatter)。
  3. 本地更新: 各自用完整的 G 和部分的状态 O,更新部分参数 W。
  4. 参数同步 : All-Gather 更新后的参数 W,使所有 GPU 上的 W 再次保持一致。

这套流程清晰地展示了 ZeRO-1 是如何通过分区优化器状态来节省显存,以及为了维护数据一致性所引入的通信开销。您对于通信量和 Reduce-Scatter 优化的思考非常到位,这正是从 ZeRO-1 迈向 ZeRO-2 的关键一步。

2. 在混合精度训练的前提下,这边我们对【只能将相应的W(蓝色部分)进行更新】这句话做更严谨的说明, 什么意思?

好的,我们来对"只能将相应的W(蓝色部分)进行更新 "这句话,在混合精度训练 (Mixed-Precision Training) 的背景下,做一个更严谨和深入的说明。

这句话的背后,其实隐藏了一个混合精度训练的关键实现细节:FP32主参数副本 (FP32 Master Parameters)

1. 混合精度训练的标准流程

在标准的混合精度训练中(不考虑 ZeRO),一个 optimizer.step() 的流程是这样的:

  1. FP16 前向/后向传播:

    • 模型参数 W 是 FP16 格式,以节省显存和加速计算。
    • loss.backward() 计算出的梯度 G 也是 FP16 格式。
  2. 梯度解缩放 (Unscaling):

    • 为了防止数值下溢,梯度在计算时被一个大的缩放因子 S 乘以了。在更新前,需要将梯度除以 S 恢复原值。
  3. 参数更新 (在 FP32 上进行):

    • 关键 : 为了避免在多次迭代中微小的梯度更新量因为 FP16 精度不足而被"舍入"为零,参数的更新操作是在一个高精度 (FP32) 的主副本上进行的。
    • 优化器维护着一份与模型参数 W 完全相同的、但数据类型为 FP32主参数副本 (我们称之为 W_master_fp32)。
    • 更新公式 : W_master_fp32_new = optimizer_update(W_master_fp32_old, G_fp16, O_fp32)
      • 优化器读取旧的 W_master_fp32
      • 将 FP16 的梯度 G_fp16 转换为 FP32。
      • 结合 FP32 的优化器状态 O_fp32 (momentum, variance)。
      • 计算出新的 FP32 主参数 W_master_fp32_new
  4. 同步 FP16 参数:

    • 将更新后的 W_master_fp32_new 拷贝并转换 回 FP16 格式,覆盖模型原本的 FP16 参数 W
    • W_fp16 = cast_to_fp16(W_master_fp32_new)
    • 这样,下一次的前向传播就可以使用最新的 FP16 参数了。

2. 将混合精度训练与 ZeRO-1 结合

现在,我们把这个流程应用到 ZeRO-1 的场景中。

  • ZeRO-1 的核心 : 分区 (Partition) 了优化器状态 O_fp32
  • 混合精度的要求 : 仍然需要一份 W_master_fp32 来进行精确更新。

那么,这份 W_master_fp32 应该如何存放呢?ZeRO-1 的设计者做出了一个合乎逻辑的选择:

既然优化器状态 O_fp32 已经被分区了,那么与之紧密相关的 W_master_fp32 也应该以同样的方式进行分区。

所以,在 ZeRO-1 中

  • GPU1 持有: 完整的 W_fp16, 完整的 G_fp16, 分区的 O1_fp32 , 以及分区的 W1_master_fp32
  • GPU2 持有: 完整的 W_fp16, 完整的 G_fp16, 分区的 O2_fp32 , 以及分区的 W2_master_fp32
  • GPU3 同理。

3. 对"只能将相应的W(蓝色部分)进行更新"的严谨说明

结合以上背景,我们可以对这句话进行精确的拆解和说明了。

这句话实际上描述了两个层面的更新:

  1. FP32 主参数副本 的更新。
  2. FP16 模型参数 的更新。

以 GPU1 为例,它的更新流程是

  1. 准备更新主参数:

    • GPU1 拥有完整的、全局同步好的梯度 G_fp16
    • 它从 G_fp16 中取出前 1/3 的部分,即 G1_fp16
    • 它拥有自己的优化器状态分片 O1_fp32
    • 它拥有自己的主参数分片 W1_master_fp32
  2. 执行主参数更新 (在 FP32 上):

    • GPU1 只能 使用它拥有的这三样东西 (G1_fp16, O1_fp32, W1_master_fp32) 来计算新的主参数分片。
    • W1_master_fp32_new = optimizer_update(W1_master_fp32_old, G1_fp16, O1_fp32)
    • 由于 GPU1 缺少 O2_fp32, O3_fp32 以及 W2_master_fp32, W3_master_fp32,它无法 计算出模型参数中间 1/3 和最后 1/3 的精确更新。这就是"只能"的根本原因。
  3. 更新 FP16 模型参数 W:

    • 此时,GPU1 计算出了 W1_master_fp32_new
    • 它将这个新的 FP32 分片转换成 FP16 格式,即 W1_fp16_new
    • 然后,它用 W1_fp16_new覆盖 它本地存储的那份完整 W_fp16前 1/3 部分
    • 这就是图中"将相应的W(蓝色部分)进行更新"的直观体现。

所以,更严谨的说法是:

"在混合精度训练的背景下,由于每个 GPU 只持有一部分(1/N)的 FP32 优化器状态FP32 主参数副本 ,因此在 optimizer.step() 阶段,它只能计算出并更新自己所负责的那一部分主参数副本。随后,这个更新后的 FP32 参数分片被转换回 FP16,并用于覆盖本地存储的、完整的 FP16 模型参数 W 中相应的位置(即图中的蓝色部分)。"

这个过程确保了参数更新的数值稳定性(在 FP32 上进行),同时通过分区 O_fp32W_master_fp32 实现了显存的节省。而代价就是,更新后每个 GPU 上的 W_fp16 变得不一致,需要后续的 All-Gather 来同步。

好的,我们来详细解读这张 PyTorch Memory Snapshot 的火焰图。这张图非常直观地展示了 ZeRO-1 在混合精度训练下的内存布局和动态变化,完全印证了我们之前的理论分析。

作者的核心观点:

实际上,zero1并不是直接更新蓝色权重(fp16),而是直接更新红色优化器中维护的权重(fp32),蓝色权重是由红色权重cast而来。

这个观点是完全正确 的,它精准地描述了混合精度训练中"FP32主参数副本"的工作机制。图中的"红色权重"就是我们之前讨论的 W_master_fp32


解读火焰图的各个部分

这张图是一个时间线上的内存占用图,Y轴代表显存占用大小,X轴代表时间。不同的色块代表不同的张量(Tensor)占用的内存。

底部的、持久存在的内存块
  1. 灰色块: fp16 完整的weight,参与fwd计算

    • 内容 : 这就是模型本身的参数,W_fp16
    • 特点 :
      • 它是 FP16 格式,所以相对较小。
      • 它是完整的,每个 GPU 都有一份。
      • 它在整个训练过程中持久存在于显存中,因为每一次前向和后向传播都需要用到它。
      • 这就是图中的"蓝色权重"所指的实体。
  2. 橙色块: fp32 切块的weight,由optimizer维护,只负责做更新 (detach了)

    • 内容 : 这就是我们讨论的FP32主参数副本 W_master_fp32分片
    • 特点 :
      • 它是 FP32 格式,用于保证更新的精度。
      • 它是切块的 (partitioned) 。这张图是 rank1 的快照,所以这个橙色块只代表了 W_master_fp32 的一部分(例如,在一共2个GPU的情况下,是后半部分)。这就是 ZeRO-1 节省显存的关键之一。
      • 它由优化器维护 ,并且是持久存在的。
      • detach() 表示这个张量不参与梯度计算(它不是计算图的一部分),它的唯一作用就是作为一个高精度的"账本",在 optimizer.step() 时被读取和更新。
    • 这就是图中的"红色权重"所指的实体。
  3. 红色块: 切块的adam动量1

    • 内容 : 这是 Adam 优化器的一阶矩 (momentum) 状态的分片
    • 特点 :
      • 它是 FP32 格式。
      • 它是切块的 ,与橙色块的 W_master_fp32 分片一一对应。rank1 只持有自己负责的那部分动量。
      • 这也是 ZeRO-1 节省显存的核心。
  4. 绿色块: 切块的adam动量2

    • 内容 : 这是 Adam 优化器的二阶矩 (variance) 状态的分片
    • 特点: 与红色块完全相同,都是分区的、FP32 的优化器状态。

小结: 火焰图的底部稳定区域完美地展示了 ZeRO-1 在一个 GPU 上的静态内存布局:

  • 一份完整的、低精度的模型副本 (灰色 W_fp16) 用于计算。
  • 一份分区的、高精度的模型主副本 (橙色 W_master_fp32_partition) 用于精确更新。
  • 一份分区的、高精度的优化器状态 (红色/绿色 O_fp32_partition) 用于历史信息记录。

追踪整个过程(结合火焰图的动态变化)

现在,让我们沿着时间轴(X轴)来看那些动态出现和消失的、更高层的色块。这些通常是梯度、激活值和临时缓冲区。

  1. 前向/后向传播阶段 (Forward/Backward):

    • 当训练开始一个迭代时,你会看到内存占用开始出现许多尖峰(spikes)
    • 这些尖峰主要是激活值 (activations)。前向传播时,每一层的输出(激活值)被创建并保留在显存中,导致内存占用逐步上升。
    • 后向传播时,这些激活值被用来计算梯度。一旦某个激活值用完,它占用的内存就可以被释放,所以你会看到内存占用有升有降。
    • 在后向传播结束时,会产生一份完整的 FP16 梯度,它也会短暂地占用一块较大的显存。
  2. 梯度同步阶段 (All-Reduce):

    • optimizer.step() 之前,会有一个 All-Reduce 操作。
    • 这个操作会创建通信缓冲区,你可能会在火焰图上看到一些与通信相关的、短暂的大块内存分配。
    • All-Reduce 结束后,每个 GPU 都有了一份完整的、同步好的 FP16 梯度
  3. 参数更新阶段 (optimizer.step()):

    • 这是最关键的阶段,它解释了作者的观点。
    • 读取 : optimizer 读取:
      • 本地的橙色块 (W_master_fp32_partition)。
      • 本地的红色/绿色块 (O_fp32_partition)。
      • 完整的、同步好的梯度 (FP16),并将其转换为 FP32。
    • 计算 : optimizer 只在 GPU 内部 ,使用上述数据,计算出新的 W_master_fp32_partition_new
    • 更新 :
      1. 这个新的 W_master_fp32_partition_new 直接覆盖 了旧的橙色块这是对"红色权重"的直接更新。
      2. 同时,新的优化器状态也覆盖了旧的红色/绿色块
  4. 参数同步阶段 (FP16 Weight Synchronization):

    • optimizer.step() 之后,需要将 FP32 主参数的更新反映到 FP16 模型参数上。
    • cast 操作 :
      • optimizer 会将更新后的橙色块 (W_master_fp32_partition_new) 转换 (cast) 为 FP16 格式,得到 W_fp16_partition_new
      • 这个 W_fp16_partition_new 是一个临时的张量。
    • All-Gather 操作 :
      • 所有 GPU 对它们各自的 W_fp16_partition_new 执行 All-Gather
      • 操作结束后,每个 GPU 都拥有了一份完整的、最新的 FP16 参数 W_fp16_new_full
    • 覆盖 :
      • 最后,用这份 W_fp16_new_full 完全覆盖 掉底部的灰色块 (W_fp16)。
      • 这样,用于下一次前向传播的模型参数就更新了。

结论

这张火焰图通过可视化的方式,完美地展示了:

  • ZeRO-1 的内存节省原理 : W_master_fp32O_fp32 都是分区的(橙色、红色、绿色块都很小)。
  • 混合精度训练的流程 : 更新发生在 FP32 的主参数(橙色块)上,然后通过 castAll-Gather 同步回用于计算的 FP16 参数(灰色块)。
  • 作者观点的正确性 : 更新的核心目标是橙色块 ,而不是直接修改灰色块。灰色块的更新只是整个流程最后一步的同步结果。

通过将理论与这张实际的内存快照相结合,我们可以对 ZeRO-1 的工作机制有一个非常具体和深刻的理解。

代码实战

cpp 复制代码
import torch
import torch.nn as nn
import deepspeed
import argparse
import os

# 导入 Profiler 相关的类
from torch.profiler import profile, record_function, ProfilerActivity

# 1. 定义模型 (和之前一样)
class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        # 增加一层以创建更多事件
        self.layer1 = nn.Linear(1024, 2048)
        self.relu1 = nn.ReLU()
        self.layer2 = nn.Linear(2048, 4096)
        self.relu2 = nn.ReLU()
        self.layer3 = nn.Linear(4096, 1024)

    def forward(self, x):
        x = self.relu1(self.layer1(x))
        x = self.relu2(self.layer2(x))
        x = self.layer3(x)
        return x

def get_args():
    parser = argparse.ArgumentParser(description='Profiled DeepSpeed ZeRO-1 Example')
    parser.add_argument('--local_rank', type=int, default=-1,
                        help='local rank passed from distributed launcher')
    parser = deepspeed.add_config_arguments(parser)
    return parser.parse_args()

def main():
    args = get_args()

    # 2. 初始化 DeepSpeed
    deepspeed.init_distributed()
    torch.cuda.set_device(args.local_rank)
    rank = torch.distributed.get_rank()

    # 3. 创建模型
    model = SimpleModel()

    # 4. 使用 deepspeed.initialize
    model_engine, _, _, _ = deepspeed.initialize(
        args=args,
        model=model,
        model_parameters=model.parameters()
    )

    # 5. 准备 Profiler
    # 我们只在 rank 0 上进行 profiling,以生成一个清晰的 trace 文件
    if rank == 0:
        print("Profiler is configured on rank 0. Starting training...")
        prof = profile(
            activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
            record_shapes=True,
            profile_memory=True,  # 开启内存分析
            with_stack=True,
            with_flops=True,
            with_modules=True
        )
    else:
        # 其他 rank 创建一个空的上下文管理器,什么都不做
        prof = torch.profiler.ExecutionTrace()


    # 6. 开始 profiling 上下文
    with prof:
        # 运行几个 step 来捕获有意义的数据
        for step in range(5):
            if rank == 0:
                print(f"--- Step {step} ---")

            # 模拟输入数据
            inputs = torch.randn(
                model_engine.train_micro_batch_size_per_gpu(), 
                1024, 
                device=model_engine.device
            )

            # 前向、后向、更新
            loss = model_engine(inputs).mean()
            model_engine.backward(loss)
            model_engine.step()

    # 7. Profiling 结束后,导出 JSON 文件
    if rank == 0:
        print("Profiling finished. Exporting trace to JSON file...")
        # 导出的 JSON 文件可以被 chrome://tracing 或 Netron 打开
        prof.export_chrome_trace("ds_zero1_trace.json")
        print("Trace saved to ds_zero1_trace.json")
        
        # 也可以打印一个总结
        print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))

    print(f"Rank {rank} finished.")

if __name__ == "__main__":
    main()

好的,这个 RuntimeError: mat1 and mat2 must have the same dtype, but got Float and Half 是一个在进行混合精度训练时非常典型的错误。

问题根源 : 这个错误发生在 F.linear (矩阵乘法) 函数中。它告诉你,参与矩阵乘法的两个张量(mat1mat2)的数据类型不匹配:一个是 Float (FP32) ,另一个是 Half (FP16)。在 CUDA kernel 中,矩阵乘法要求两个输入的数据类型必须完全相同。


错误分析

让我们来追踪一下数据流,看看哪里出了问题:

  1. 配置文件 : 你的 ds_config_zero1.json 中设置了 "fp16": {"enabled": true}

  2. deepspeed.initialize : 当你调用这个函数时,DeepSpeed 会自动将你的模型(SimpleModel)的参数(self.weight, self.bias)转换为 FP16 (Half)。这是混合精度训练的第一步,目的是节省模型参数占用的显存。

  3. 输入数据 (inputs) : 在你的训练循环中,你创建了输入数据:

    python 复制代码
    inputs = torch.randn(
        model_engine.train_micro_batch_size_per_gpu(), 
        1024, 
        device=model_engine.device
    )

    这里的 torch.randn 默认创建的是 torch.float32 (Float) 类型的张量。

  4. 前向传播 :

    • 当模型执行 self.layer1(inputs) 时,F.linear 函数被调用。
    • mat1 就是你的输入数据 inputs,它的类型是 FP32 (Float)
    • mat2layer1 的权重 self.weight,它已经被 DeepSpeed 转换成了 FP16 (Half)
  5. 冲突 : F.linear 接收到一个 FP32 的输入和一个 FP16 的权重,于是抛出了数据类型不匹配的 RuntimeError


解决方案

解决方案非常直接:确保传递给模型的输入数据也和模型参数的数据类型一致,即 FP16 (Half)。

你需要对创建 inputs 的那一行代码做一个小小的修改。

修改前的代码:

python 复制代码
inputs = torch.randn(
    model_engine.train_micro_batch_size_per_gpu(), 
    1024, 
    device=model_engine.device
)

修改后的代码:

python 复制代码
inputs = torch.randn(
    model_engine.train_micro_batch_size_per_gpu(), 
    1024, 
    device=model_engine.device
).half() # <--- 在这里添加 .half()

或者,使用 to() 方法,这样更通用一些:

python 复制代码
inputs = torch.randn(
    model_engine.train_micro_batch_size_per_gpu(), 
    1024, 
    device=model_engine.device
).to(model_engine.dtype) # <--- 使用 model_engine 的 dtype,这是最佳实践

为什么 .to(model_engine.dtype) 是最佳实践?

  • model_engine.dtype 会自动反映 DeepSpeed 配置的数据类型。
  • 如果你在 ds_config.json 中将 fp16 改为 bf16,或者禁用了混合精度,这段代码不需要任何修改,它会自动创建正确类型的输入数据。
  • 而硬编码 .half().bfloat16() 会使得代码在配置改变时需要手动修改,不够灵活。

修正后的完整 main 函数

cpp 复制代码
import torch
import torch.nn as nn
import deepspeed
import argparse

import os
import contextlib
# 导入 Profiler 相关的类
from torch.profiler import profile, record_function, ProfilerActivity

# 1. 定义模型 (和之前一样)
class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        # 增加一层以创建更多事件
        self.layer1 = nn.Linear(1024, 2048)
        self.relu1 = nn.ReLU()
        self.layer2 = nn.Linear(2048, 4096)
        self.relu2 = nn.ReLU()
        self.layer3 = nn.Linear(4096, 1024)

    def forward(self, x):
        x = self.relu1(self.layer1(x))
        x = self.relu2(self.layer2(x))
        x = self.layer3(x)
        return x

def get_args():
    parser = argparse.ArgumentParser(description='Profiled DeepSpeed ZeRO-1 Example')
    parser.add_argument('--local_rank', type=int, default=-1,
                        help='local rank passed from distributed launcher')
    parser = deepspeed.add_config_arguments(parser)
    return parser.parse_args()

def main():
    args = get_args()

    # 2. 初始化 DeepSpeed
    deepspeed.init_distributed()
    torch.cuda.set_device(args.local_rank)
    rank = torch.distributed.get_rank()

    # 3. 创建模型
    model = SimpleModel()

    # 4. 使用 deepspeed.initialize
    model_engine, _, _, _ = deepspeed.initialize(
        args=args,
        model=model,
        model_parameters=model.parameters()
    )

    # 5. 准备 Profiler
    # 我们只在 rank 0 上进行 profiling,以生成一个清晰的 trace 文件
    if rank == 0:
        print("Profiler is configured on rank 0. Starting training...")
        prof = profile(
            activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
            record_shapes=True,
            profile_memory=True,  # 开启内存分析
            with_stack=True,
            with_flops=True,
            with_modules=True
        )
    else:
        # 其他 rank 创建一个空的上下文管理器,什么都不做
        # 用标准的空上下文管理器,兼容旧版 PyTorch
        prof = contextlib.nullcontext()


    # 6. 开始 profiling 上下文
    with prof:
        # 运行几个 step 来捕获有意义的数据
        for step in range(5):
            if rank == 0:
                print(f"--- Step {step} ---")
            model_dtype = next(model_engine.module.parameters()).dtype
            # 模拟输入数据
            inputs = torch.randn(
                model_engine.train_micro_batch_size_per_gpu(), 
                1024, 
                device=model_engine.device
            ).to(model_dtype)

            # 前向、后向、更新
            loss = model_engine(inputs).mean()
            model_engine.backward(loss)
            model_engine.step()

    # 7. Profiling 结束后,导出 JSON 文件
    if rank == 0:
        print("Profiling finished. Exporting trace to JSON file...")
        # 导出的 JSON 文件可以被 chrome://tracing 或 Netron 打开
        prof.export_chrome_trace("ds_zero1_trace.json")
        print("Trace saved to ds_zero1_trace.json")
        
        # 也可以打印一个总结
        print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))

    print(f"Rank {rank} finished.")

if __name__ == "__main__":
    main()

这里是修正了输入数据类型的完整 `main` 函数部分,你可以直接替换。

这个修改非常简单,但它点出了混合精度训练中一个非常核心的原则:**不仅模型参数需要转换,输入数据流也必须匹配。**

修改后,再次运行你的 `deepspeed` 启动命令,程序应该就能正常执行了。




rofiler is configured on rank 0. Starting training...
--- Step 0 ---
[2025-11-02 12:11:31,049] [INFO] [loss_scaler.py:190:update_scale] [deepspeed] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 65536, but hysteresis is 2. Reducing hysteresis to 1
--- Step 1 ---
[2025-11-02 12:11:31,053] [INFO] [loss_scaler.py:183:update_scale] [deepspeed] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 65536, reducing to 32768
--- Step 2 ---
--- Step 3 ---
--- Step 4 ---
Rank 1 finished.
Profiling finished. Exporting trace to JSON file...
Trace saved to ds_zero1_trace.json
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
                                                   Name    Self CPU %      Self CPU   CPU total %     CPU total  CPU time avg     Self CUDA   Self CUDA %    CUDA total  CUDA time avg       CPU Mem  Self CPU Mem      CUDA Mem  Self CUDA Mem    # of Calls   Total FLOPs  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
                                     record_param_comms         0.27%     964.681us         0.36%       1.280ms      39.987us       1.865ms        41.29%       1.865ms      58.277us           0 b           0 b           0 b           0 b            32            --  
                                       c10d::allreduce_         0.08%     294.774us         0.36%       1.294ms      99.551us       0.000us         0.00%       1.472ms     113.269us           0 b           0 b           0 b           0 b            13            --  
                                        nccl:all_reduce         0.00%       0.000us         0.00%       0.000us       0.000us       1.472ms        32.60%       1.472ms     113.269us           0 b           0 b           0 b           0 b            13            --  
ncclDevKernel_AllReduce_Sum_f16_RING_LL(ncclDevKerne...         0.00%       0.000us         0.00%       0.000us       0.000us       1.258ms        27.85%       1.258ms     251.550us           0 b           0 b           0 b           0 b             5            --  
                                            aten::copy_         0.17%     621.840us         0.45%       1.596ms      12.668us     848.852us        18.79%     866.195us       6.875us           0 b           0 b           0 b           0 b           126            --  
                          Optimizer.step#FusedAdam.step         0.00%       0.000us         0.00%       0.000us       0.000us     635.036us        14.06%     635.036us     211.679us           0 b           0 b           0 b           0 b             3            --  
                                               aten::to         0.03%     122.049us         0.52%       1.849ms      19.886us       0.000us         0.00%     508.376us       5.466us           0 b           0 b     533.52 Mb           0 b            93            --  
                                         aten::_to_copy         0.12%     427.348us         0.48%       1.727ms      23.662us       0.000us         0.00%     508.376us       6.964us           0 b           0 b     533.52 Mb           0 b            73            --  
                                 c10d::_allgather_base_         0.02%      60.331us         0.08%     291.778us      97.259us       0.000us         0.00%     392.379us     130.793us           0 b           0 b           0 b           0 b             3            --  
ncclDevKernel_AllGather_RING_LL(ncclDevKernelArgsSto...         0.00%       0.000us         0.00%       0.000us       0.000us     392.379us         8.69%     392.379us     130.793us           0 b           0 b           0 b           0 b             3            --  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
Self CPU time total: 357.859ms
Self CUDA time total: 4.517ms

Rank 0 finished.
[rank0]:[W1102 12:11:32.188913801 ProcessGroupNCCL.cpp:1496] Warning: WARNING: destroy_process_group() was not called before program exit, which can leak resources. For more info, please see https://pytorch.org/docs/stable/distributed.html#shutdown (function operator())
notebook-76ec3aa0aa0d-worker-0:90410:91095 [0] NCCL INFO [Service thread] Connection closed by localRank 1
[2025-11-02 12:11:33,088] [INFO] [launch.py:351:main] Process 90410 exits successfully.
[2025-11-02 12:11:33,089] [INFO] [launch.py:351:main] Process 90411 exits successfully.
(python3.10) bash-4.4$ 




代码`import torch
import torch.nn as nn
import deepspeed
import argparse

import os
import contextlib
# 导入 Profiler 相关的类
from torch.profiler import profile, record_function, ProfilerActivity

# 1. 定义模型 (和之前一样)
class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        # 增加一层以创建更多事件
        self.layer1 = nn.Linear(1024, 2048)
        self.relu1 = nn.ReLU()
        self.layer2 = nn.Linear(2048, 4096)
        self.relu2 = nn.ReLU()
        self.layer3 = nn.Linear(4096, 1024)

    def forward(self, x):
        x = self.relu1(self.layer1(x))
        x = self.relu2(self.layer2(x))
        x = self.layer3(x)
        return x

def get_args():
    parser = argparse.ArgumentParser(description='Profiled DeepSpeed ZeRO-1 Example')
    parser.add_argument('--local_rank', type=int, default=-1,
                        help='local rank passed from distributed launcher')
    parser = deepspeed.add_config_arguments(parser)
    return parser.parse_args()

def main():
    args = get_args()

    # 2. 初始化 DeepSpeed
    deepspeed.init_distributed()
    torch.cuda.set_device(args.local_rank)
    rank = torch.distributed.get_rank()

    # 3. 创建模型
    model = SimpleModel()

    # 4. 使用 deepspeed.initialize
    model_engine, _, _, _ = deepspeed.initialize(
        args=args,
        model=model,
        model_parameters=model.parameters()
    )

    # 5. 准备 Profiler
    # 我们只在 rank 0 上进行 profiling,以生成一个清晰的 trace 文件
    if rank == 0:
        print("Profiler is configured on rank 0. Starting training...")
        prof = profile(
            activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
            record_shapes=True,
            profile_memory=True,  # 开启内存分析
            with_stack=True,
            with_flops=True,
            with_modules=True
        )
    else:
        # 其他 rank 创建一个空的上下文管理器,什么都不做
        # 用标准的空上下文管理器,兼容旧版 PyTorch
        prof = contextlib.nullcontext()

    torch.cuda.memory._record_memory_history()
    # 6. 开始 profiling 上下文
    with prof:
        # 运行几个 step 来捕获有意义的数据
        for step in range(5):
            if rank == 0:
                print(f"--- Step {step} ---")
            model_dtype = next(model_engine.module.parameters()).dtype
            # 模拟输入数据
            inputs = torch.randn(
                model_engine.train_micro_batch_size_per_gpu(), 
                1024, 
                device=model_engine.device
            ).to(model_dtype)

            # 前向、后向、更新
            loss = model_engine(inputs).mean()
            model_engine.backward(loss)
            model_engine.step()
    if rank == 0:    
        torch.cuda.memory._dump_snapshot("my_snapshottest2.pickle")

    print(f"Rank {rank} finished.")

if __name__ == "__main__":
    main()

在这里插入图片描述

cpp 复制代码
import torch
import torch.nn as nn
import deepspeed
import argparse
import os
import socket
from datetime import datetime

# --- Model 和 get_args 定义 (与之前相同) ---
class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(1024, 2048)
        self.relu1 = nn.ReLU()
        self.layer2 = nn.Linear(2048, 4096)
        self.relu2 = nn.ReLU()
        self.layer3 = nn.Linear(4096, 1024)

    def forward(self, x):
        return self.relu2(self.layer2(self.relu1(self.layer1(x))))

def get_args():
    parser = argparse.ArgumentParser(description='Profile All Ranks with DeepSpeed ZeRO-1')
    parser.add_argument('--local_rank', type=int, default=-1, help='local rank passed from distributed launcher')
    parser = deepspeed.add_config_arguments(parser)
    args = parser.parse_args()
    return args

# --- 【核心修改】trace_handler 现在需要知道当前的 rank ---
def get_trace_handler(rank):
    """
    返回一个为特定 rank 定制的 on_trace_ready handler.
    """
    # 获取主机名和时间戳,用于生成唯一的文件前缀
    host_name = socket.gethostname()
    timestamp = datetime.now().strftime("%b_%d_%H_%M_%S")
    
    # 每个 rank 都有自己独立的文件前缀
    file_prefix = f"{host_name}_{timestamp}_rank{rank}"
    
    def trace_handler(prof: torch.profiler.profile):
        print(f"Rank {rank}: Profiler is ready. Exporting traces to files with prefix: {file_prefix}")

        # 1. 导出 Chrome trace JSON 文件 (可选,但推荐)
        try:
            prof.export_chrome_trace(f"{file_prefix}.json.gz")
        except Exception as e:
            print(f"Rank {rank}: Failed to export chrome trace: {e}")
        
        # 2. 【关键】导出该 rank 对应的 GPU 的内存时间线 HTML 文件
        try:
            # device 参数在这里不需要,Profiler 会自动使用它正在监控的设备
            prof.export_memory_timeline(f"{file_prefix}.html")
        except Exception as e:
            # 在某些旧版本或特定配置下,这个函数可能不可用
            print(f"Rank {rank}: Failed to export memory timeline: {e}")

    return trace_handler

def main():
    args = get_args()

    deepspeed.init_distributed()
    rank = torch.distributed.get_rank()
    torch.cuda.set_device(args.local_rank)

    model = SimpleModel()
    model_engine, _, _, _ = deepspeed.initialize(
        args=args, model=model, model_parameters=model.parameters()
    )

    # --- Profiler 设置 (所有 rank 都开启) ---
    # 每个 rank 使用自己定制的 trace handler
    profiler_handler = get_trace_handler(rank)

    # 我们让 profiler 只在第2和第3步激活,以跳过初始化的内存波动
    profiler_schedule = torch.profiler.schedule(wait=0, warmup=0, active=6, repeat=1)

    print(f"Rank {rank}: Initializing profiler...")
    
    # 使用 with 语句来管理 profiler 的生命周期
    with torch.profiler.profile(
        activities=[
            torch.profiler.ProfilerActivity.CPU,
            torch.profiler.ProfilerActivity.CUDA,
        ],
        # schedule=profiler_schedule,
        on_trace_ready=profiler_handler,
        record_shapes=True,
        profile_memory=True,
        with_stack=True,
    ) as prof:
        # 运行足够的 step 来触发 profiler
        for step in range(10):
            print(f"Rank {rank}, starting Step {step}...")
            
            model_dtype = next(model_engine.module.parameters()).dtype
            inputs = torch.randn(
                model_engine.train_micro_batch_size_per_gpu(),
                1024,
                device=model_engine.device
            ).to(model_dtype)

            loss = model_engine(inputs).mean()
            model_engine.backward(loss)
            model_engine.step()

            # 手动通知 profiler 完成了一个 step
            prof.step()

    print(f"Rank {rank} finished.")

if __name__ == "__main__":
    main()

3.火焰图分析

工具https://pytorch.cadn.net.cn/docs_en/2.5/torch_cuda_memory.html
https://pytorch.org/blog/understanding-gpu-memory-1

太棒了!这张由 prof.export_memory_timeline() 生成的图,是理解 DeepSpeed ZeRO-1 内存工作原理的绝佳教材。它看起来是这个样子,是因为它以一种非常直观的方式,完美地捕捉了一个训练 step 中各个阶段的内存动态变化。

让我们像看电影慢放一样,从左到右(按时间顺序)来解剖这张图。


图表解读 (时间线分析)

这张图展示了 GPU 显存的使用情况。Y 轴是显存占用量(GB),X 轴是时间(ms)。不同的颜色代表不同类型的内存分配,图例在右上角。

阶段一:时间 0ms - ~280ms (静态内存布局)

在这个阶段,我们看到两条持久存在的、平坦的内存块:

  • 绿色 (PARAMETER) : 这是完整的 FP16 模型参数 。根据 ZeRO-1 的原理,每个 GPU 都需要一份完整的、低精度的模型参数来执行前向和后向计算。这块内存在 deepspeed.initialize 时被分配,并一直存在于整个训练过程中。
  • 灰色 (Unknown) : 这是 ZeRO-1 的核心优化所在 。这块内存代表了被分区(Partitioned)的 FP32 主参数被分区的 FP32 优化器状态 (Adam 的 momentum 和 variance)。
    • 为什么是灰色的 "Unknown"? 因为这些内存是由 DeepSpeed 的底层 C++ 或 CUDA 代码直接管理的,PyTorch Profiler 的自动分类器有时无法精确地识别它们的用途,就将它们归为"未知"。
    • 关键点 :这块灰色内存的大小,仅仅是完整 FP32 参数和优化器状态的 1/N(N是你的 GPU 数量,这里是 1/2)。这就是 ZeRO-1 节省大量静态内存的地方。
阶段二:时间 ~280ms - ~420ms (反向传播 -> 梯度分配)

我们看到了一个巨大的蓝色方块拔地而起,然后又消失。

  • 深蓝色 (GRADIENT) : 这是在执行 model_engine.backward(loss) 时,为模型参数分配的完整的 FP16 梯度
    • 为什么是完整的? 因为在计算梯度时,每个 GPU 仍然需要计算出对应于完整模型参数的梯度。
    • 为什么会消失?model_engine.step() 执行完毕后,优化器已经使用完这些梯度,会通过 optimizer.zero_grad() 将其释放。所以它是一个动态的、临时的大块内存
    • 内存峰值 : 这个蓝色方块的出现,导致了总内存占用达到峰值。图顶部的 Max memory allocated: 1.14 GiB 就是在这个时刻达到的。
阶段三:时间 ~420ms - ~480ms (优化器步骤)

在巨大的蓝色梯度块消失后,出现了一些小而杂乱的内存块。

  • 黄色 (OPTIMIZER_STATE) , 灰色 (Unknown) , 浅蓝色 (AUTOGRAD_DETAIL) : 这个阶段对应 model_engine.step()
    • 在这个过程中,DeepSpeed 会执行梯度的 AllReduce、更新分区后的 FP32 主参数、更新分区后的优化器状态、再将更新后的参数 AllGather 回完整的 FP16 模型参数。
    • 这个过程涉及到大量的临时缓冲区(用于通信、类型转换等),因此你会看到很多短暂的、不同类型的内存分配。
阶段四:时间 ~480ms 之后 (后续的训练步骤)

我们看到了一系列重复的、更窄的"尖峰"图案。

  • 重复的模式 : 每一个"尖峰"都代表一个新的训练 step (step=1, step=2, ...)。
  • 模式内容: 每个尖峰内部,都重复着"阶段二"和"阶段三"的模式:分配巨大的蓝色梯度块 -> 释放梯度块 -> 杂乱的优化器步骤。
  • 为什么第一个 step 看起来更宽? 第一个训练 step 通常会包含一些一次性的操作,比如 CUDA kernel 的 JIT 编译、缓存预热等,所以它的耗时会比后续稳定状态下的 step 更长。

总结:为什么图是这个样子的?

这张图完美地回答了"ZeRO-1 是如何工作的?"这个问题:

  1. 持久内存 (图的底部):

    • 绿色 : 每个 GPU 都有一份完整的 FP16 参数 (用于计算)。
    • 灰色 : 每个 GPU 只持有1/N 的 FP32 参数和优化器状态 (用于更新,这是省内存的关键)。
  2. 动态内存 (图中的尖峰):

    • 蓝色 : 在 backward() 期间,会临时分配一块完整的 FP16 梯度,这是训练过程中的内存峰值点。
    • step() 之后,这块巨大的梯度内存会被释放,为下一个 step 腾出空间。

你的图清晰地展示了: ZeRO-1 的内存优化不是 在梯度计算阶段(因为梯度仍然是完整的),而是在静态的、持久的优化器状态和主参数上。通过将这部分巨大的内存分区存储,它极大地降低了模型训练的基线内存占用。

好的,这个问题提得非常深入,直指这张图的核心!你观察到的"蓝色块"和"10条"现象,是解密 backward()step() 内部机制的关键。

让我们来详细拆解。

1. model_engine.step() 是在什么时候执行的?

在你的代码中,每个循环的顺序是:

  1. loss = model_engine(inputs).mean() (前向传播)
  2. model_engine.backward(loss) (反向传播)
  3. model_engine.step() (优化器更新)
  4. prof.step() (通知 Profiler 这一步结束了)

现在,我们把这个顺序和图上的事件对应起来:

  • backward() 阶段 : 对应图中巨大的蓝色方块 (GRADIENT) 出现和存在的时期。在这个阶段,PyTorch 的自动求导引擎计算出每一层参数的梯度,并为它们分配显存。
  • step() 阶段 : 对应蓝色方块之后 ,那一小段混乱、包含很多细小尖峰的区域(包括你说的"10条"可能就位于此)。在这个阶段,DeepSpeed ZeRO 优化器会执行以下复杂操作:
    1. 规约梯度 (Reduce Gradients) : 将每个 GPU 上完整的梯度进行 AllReduce 操作,使得每个 GPU 最终只得到它负责的那部分参数的、聚合后的梯度。
    2. 更新参数 (Update Parameters): 使用聚合后的梯度,去更新它自己持有的那一小块 FP32 主参数和优化器状态。
    3. 同步模型 (Update Model Weights) : 将更新后的一小块 FP32 参数转换回 FP16,然后通过 AllGather 操作广播给所有 GPU,让每个 GPU都能重建一份完整且最新的 FP16 模型参数,为下一次前向传播做准备。
  • optimizer.zero_grad() : 这个操作通常在 step() 之后隐式或显式调用,它会释放巨大的蓝色梯度块,所以我们能看到蓝色块在 step() 阶段结束后就消失了。

结论 : model_engine.step() 发生在巨大蓝色梯度块出现之后,消失之前的那段"混乱"时期。


2. 蓝色块和"10条":是什么?为什么这么大?

你观察到的现象可以分为两部分:一个主要的、巨大的蓝色块,以及一系列重复出现的窄"尖峰"(你可能数了大约10个,但实际上是 active=6 步,只是第一个 step 比较宽)。

A. 巨大的蓝色块 (GRADIENT)
  • 它是什么?

    这个蓝色块代表为模型所有参数的梯度 (.grad) 所分配的内存。当 backward() 被调用时,PyTorch 会为每一个需要梯度的 nn.Parameter 创建一个和它形状、大小完全相同的张量来存储梯度值。

  • 为什么这么大?

    这是理解 ZeRO-1 的一个最关键、最核心的知识点!

    1. 梯度大小 = 参数大小: 一个模型的梯度所占用的内存,和它的参数本身占用的内存是一样大的。如果你的模型有 10 亿个 FP16 参数(占用 2GB),那么它的梯度也会占用 2GB。
    2. ZeRO-1 中梯度是完整的 : ZeRO-1 的优化点在于分区存储优化器状态和 FP32 主参数 ,但是,在 backward() 这一步,每个 GPU 仍然需要计算和存储一份完整的、未分区的梯度
    3. 内存峰值来源 : 这就导致了训练过程中的内存峰值。总内存 = (持久内存:FP16完整参数 + 分区FP32参数 + 分区优化器状态) + (动态内存:完整的梯度)
    4. 你的模型有多大? : 我们来估算一下你代码中模型的梯度大小:
      • layer1 (1024, 2048): ~2.1M params
      • layer2 (2048, 4096): ~8.4M params
      • layer3 (4096, 1024): ~4.2M params
      • 总参数量: ~14.7M
      • FP16 梯度大小 : 14.7M * 2 bytes/param ≈ 29.4 MB
      • 你的图显示了什么? : 图中的蓝色块高度大约是 0.9 GiB (约 920 MB) !这说明你生成这张图时所用的模型,比我们代码里的 SimpleModel 大得多(大约是 30 多倍)。这完全正常,对于像 GPT 这样的大模型,梯度本身占用几个 GB 是很常见的。

一句话总结蓝色块 : 它是完整的模型梯度,在 ZeRO-1 中它并未被分区,因此非常巨大,构成了训练时的内存峰值。

B. "10条"细长的尖峰 (多个训练 Step)

你看到的重复的、像柱子一样的"尖峰",其实是多个训练 step 的内存使用模式 。由于你的 schedule 设置为 active=6,Profiler 记录了 6 个 step(或者你的循环只跑了5步,就记录了5个)。

  • 每一"条"/"尖峰"是什么?
    每一条尖峰就是一个完整的 backward() + step() 过程。

    它内部的模式都是一样的:

    1. 内存突然飙升(巨大的蓝色梯度块被分配)。
    2. 内存维持在高位一小段时间(梯度计算和 step 准备)。
    3. 内存突然下降(梯度被释放)。
  • 为什么第一个尖峰比后面的宽?

    这是典型的"热身"现象。第一个被分析的 step (在你的例子中是 step=0) 通常会包含一些一次性的 CUDA 操作(如 kernel JIT 编译、缓存初始化等),所以它的执行时间会比后续已经"热身"完毕的 step 更长。

结论 : 你看到的"10条"或"6条"蓝色相间的尖峰,是 Profiler 记录的多个连续训练步骤的内存快照。每一个尖峰都代表一次"分配完整梯度 -> 使用梯度 -> 释放梯度"的循环,直观地展示了模型训练的动态内存行为。

好的,您提出的这个验证非常关键,它揭示了一个核心事实:我们代码中的 SimpleModel 和生成这张图所用的模型,根本不是同一个模型

计算结果与图中完全不吻合。这恰好证明了我们之前的推断是正确的------这张图分析的是一个参数量大得多的模型。

下面是详细的计算过程,它将清晰地展示这个巨大的差异。


第一步:计算我们代码中 SimpleModel 的总参数量

我们来逐层计算 Simple-Model 的参数数量。公式是:参数量 = 输入维度 × 输出维度 + 输出维度 (偏置项)

  1. self.layer1 = nn.Linear(1024, 2048)

    • 权重 (Weights): 1024 × 2048 = 2,097,152
    • 偏置 (Bias): 2048
    • 该层参数 : 2,097,152 + 2048 = 2,099,200
  2. self.layer2 = nn.Linear(2048, 4096)

    • 权重 (Weights): 2048 × 4096 = 8,388,608
    • 偏置 (Bias): 4096
    • 该层参数 : 8,388,608 + 4096 = 8,392,704
  3. self.layer3 = nn.Linear(4096, 1024)

    • 权重 (Weights): 4096 × 1024 = 4,194,304
    • 偏置 (Bias): 1024
    • 该层参数 : 4,194,304 + 1024 = 4,195,328

SimpleModel 的总参数量 :
2,099,200 + 8,392,704 + 4,195,328 = 14,687,232

约等于 14.7M (1470万) 个参数。


第二步:根据参数量计算 SimpleModel 的梯度大小

现在我们用这个参数量来计算它在 FP16 模式下应该产生的梯度大小。

  • 公式 : 梯度大小 (Bytes) = 总参数量 × 2 Bytes/参数
  • 计算 : 14,687,232 × 2 = 29,374,464 Bytes

接下来,我们将这个结果转换为更易读的单位 (MB):

  • 1 MB = 1024 × 1024 = 1,048,576 Bytes
  • 梯度大小 (MB) : 29,374,464 / 1,048,576 ≈ 28.01 MB

理论计算结论 : 如果我们运行代码中的 SimpleModel,它产生的梯度大小应该大约是 28 MB


第三步:对比理论计算与图中观测结果

现在,我们把两个数字放在一起对比:

  • 理论计算出的梯度大小 (SimpleModel) : 28 MB
  • 图中观测到的梯度大小 (蓝色块) : ~1.08 GiB (约 1106 MB)

对比结果
28 MB (理论) vs 1106 MB (观测)

两者相差了近 40 倍!


最终结论

这个巨大的差异无可辩驳地证明了:
您提供的内存分析图,并非由我们示例代码中的 SimpleModel 生成的。

这张图是分析一个参数量约为 5.8 亿 的大模型时得到的。我们代码中的 SimpleModel(约 1500 万参数)只是一个用于演示如何运行 Profiler 的"玩具"模型。

这个验证过程非常有价值,它告诉我们:

  1. 实践出真知: 不能想当然地认为示例代码和示例结果是匹配的。
  2. 量化分析的力量: 通过精确计算,我们可以从 Profiler 的结果中反推出模型的关键属性(如参数量),并验证我们的假设。
  3. 梯度是内存大户: 这个对比也再次强调了,梯度是深度学习训练中动态内存消耗的主要来源,其大小与模型参数量严格挂钩。

#以上代码不小心注释掉了

schedule=profiler_schedule,

on_trace_ready=profiler_handler,

问题定位

根据你提供的内存分配信息,我看到了问题的关键所在。让我分析一下为什么14.7M参数的模型会分配1GB的梯度内存。

问题分析

从堆栈跟踪可以看到,这个1GB的内存分配发生在:

复制代码
/home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/runtime/zero/stage_1_and_2.py:2050:backward

这行代码在DeepSpeed的ZeRO阶段1和2实现中。让我们计算一下:

参数计算

  • 你的模型参数:14.7M (14,700,000个参数)
  • 如果使用FP16训练:每个参数2字节
  • 预期梯度大小:14.7M × 2字节 ≈ 29.4MB

但实际分配了1GB (953.7MiB ≈ 1,000,000,000字节)

可能的原因

  1. ZeRO-2的梯度分区:在ZeRO-2中,梯度是分区的,每个rank可能分配了完整的梯度缓冲区

  2. 内存对齐和预分配:DeepSpeed可能为了性能进行了内存预分配

  3. 优化器状态:可能包含了优化器状态的内存分配

诊断和验证代码

让我们修改代码来验证实际的内存使用情况:

python 复制代码
import torch
import torch.nn as nn
import deepspeed
import argparse
import os
import socket
from datetime import datetime

class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(1024, 2048)
        self.relu1 = nn.ReLU()
        self.layer2 = nn.Linear(2048, 4096)
        self.relu2 = nn.ReLU()
        self.layer3 = nn.Linear(4096, 1024)

    def forward(self, x):
        return self.relu2(self.layer2(self.relu1(self.layer1(x))))

def get_args():
    parser = argparse.ArgumentParser(description='Profile All Ranks with DeepSpeed ZeRO-1')
    parser.add_argument('--local_rank', type=int, default=-1, help='local rank passed from distributed launcher')
    parser = deepspeed.add_config_arguments(parser)
    args = parser.parse_args()
    return args

def print_memory_usage(rank, message):
    if torch.cuda.is_available():
        allocated = torch.cuda.memory_allocated(rank) / 1024**3  # GB
        reserved = torch.cuda.memory_reserved(rank) / 1024**3    # GB
        print(f"Rank {rank}: {message} - Allocated: {allocated:.2f} GB, Reserved: {reserved:.2f} GB")

def main():
    args = get_args()

    deepspeed.init_distributed()
    rank = torch.distributed.get_rank()
    torch.cuda.set_device(args.local_rank)

    # 清空GPU内存
    torch.cuda.empty_cache()
    print_memory_usage(rank, "After empty cache")

    model = SimpleModel()
    
    # 计算理论参数和梯度大小
    total_params = sum(p.numel() for p in model.parameters())
    print(f"Rank {rank}: Model parameters: {total_params:,} = {total_params/1e6:.2f}M")
    print(f"Rank {rank}: Expected gradient size (FP16): {total_params * 2 / 1024**2:.2f} MB")
    print(f"Rank {rank}: Expected gradient size (FP32): {total_params * 4 / 1024**2:.2f} MB")
    
    print_memory_usage(rank, "After model creation")

    # 初始化DeepSpeed
    model_engine, optimizer, _, _ = deepspeed.initialize(
        args=args, model=model, model_parameters=model.parameters()
    )
    
    print_memory_usage(rank, "After DeepSpeed initialize")
    
    # 检查DeepSpeed配置
    zero_stage = model_engine.zero_optimization_stage()
    print(f"Rank {rank}: ZeRO stage: {zero_stage}")
    
    # 打印优化器信息
    if hasattr(optimizer, 'param_groups'):
        print(f"Rank {rank}: Optimizer has {len(optimizer.param_groups)} parameter groups")
    
    # 检查实际管理的参数数量
    total_params_in_engine = sum(p.numel() for p in model_engine.module.parameters())
    print(f"Rank {rank}: Parameters in engine: {total_params_in_engine:,}")

    # 运行一个训练步骤来观察内存变化
    model_dtype = next(model_engine.module.parameters()).dtype
    print(f"Rank {rank}: Model dtype: {model_dtype}")
    
    inputs = torch.randn(32, 1024, device=model_engine.device).to(model_dtype)
    
    print_memory_usage(rank, "Before forward")
    
    outputs = model_engine(inputs)
    loss = outputs.mean()
    
    print_memory_usage(rank, "After forward")
    
    model_engine.backward(loss)
    
    print_memory_usage(rank, "After backward")
    
    # 检查梯度状态
    for name, param in model_engine.module.named_parameters():
        if param.grad is not None:
            grad_size = param.grad.numel() * param.grad.element_size()
            print(f"Rank {rank}: Gradient {name}: {grad_size / 1024**2:.2f} MB")
            break  # 只打印第一个以节省空间
    
    model_engine.step()
    
    print_memory_usage(rank, "After step")

if __name__ == "__main__":
    main()

可能解决方案

  1. 检查DeepSpeed配置文件
json 复制代码
{
  "zero_optimization": {
    "stage": 1,
    "reduce_bucket_size": 5e8,
    "allgather_bucket_size": 5e8
  }
}
  1. 调整bucket大小 :减少reduce_bucket_sizeallgather_bucket_size的值

  2. 使用ZeRO-1而不是ZeRO-2:ZeRO-1的梯度内存使用更接近理论值

  3. 检查是否意外使用了ZeRO-Offload:这可能会导致额外的内存分配

运行上面的诊断代码,它会告诉你:

  • 每个阶段的实际内存使用
  • DeepSpeed的ZeRO阶段
  • 梯度的实际大小
  • 模型的数据类型

这样我们就能确定1GB分配的具体原因。

Rank 0: After empty cache - Allocated: 0.00 GB, Reserved: 0.00 GB

Rank 0: Model parameters: 14,687,232 = 14.69M

Rank 0: Expected gradient size (FP16): 28.01 MB

Rank 0: Expected gradient size (FP32): 56.03 MB

Rank 0: After model creation - Allocated: 0.00 GB, Reserved: 0.00 G

Loading extension module fused_adam...

Time to load fused_adam op: 27.383007049560547 seconds

Rank 1: After DeepSpeed initialize - Allocated: 0.05 GB, Reserved: 0.11 GB

Rank 1: ZeRO stage: 1

Rank 1: Optimizer has 1 parameter groups

Rank 1: Parameters in engine: 14,687,232

Rank 1: Model dtype: torch.float16

Rank 1: Before forward - Allocated: 0.05 GB, Reserved: 0.11 GB

Loading extension module fused_adam...

Time to load fused_adam op: 27.499696254730225 seconds

2025-11-02 14:21:41,985\] \[INFO\] \[logging.py:128:log_dist\] \[Rank 0\] Using DeepSpeed Optimizer param name adam as basic optimizer \[2025-11-02 14:21:41,985\] \[INFO\] \[logging.py:128:log_dist\] \[Rank 0\] Removing param_group that has no 'params' in the basic Optimizer \[2025-11-02 14:21:41,985\] \[INFO\] \[logging.py:128:log_dist\] \[Rank 0\] DeepSpeed Basic Optimizer = FusedAdam \[2025-11-02 14:21:41,986\] \[INFO\] \[utils.py:59:is_zero_supported_optimizer\] Checking ZeRO support for optimizer=FusedAdam type=\ \[2025-11-02 14:21:41,986\] \[INFO\] \[logging.py:128:log_dist\] \[Rank 0\] Creating torch.float16 ZeRO stage 1 optimizer \[2025-11-02 14:21:41,986\] \[INFO\] \[stage_1_and_2.py:149:**init** \] Reduce bucket size 500000000 \[2025-11-02 14:21:41,986\] \[INFO\] \[stage_1_and_2.py:150:**init** \] Allgather bucket size 500000000 \[2025-11-02 14:21:41,986\] \[INFO\] \[stage_1_and_2.py:151:**init** \] CPU Offload: False \[2025-11-02 14:21:41,986\] \[INFO\] \[stage_1_and_2.py:152:**init** \] Round robin gradient partitioning: False Rank 1: After forward - Allocated: 0.09 GB, Reserved: 0.14 GB Rank 1: After backward - Allocated: 0.15 GB, Reserved: 1.12 GB 我们注意到在 backward 之后,Reserved 内存从 0.14 GB 增加到了 1.12 GB,而 Allocated 内存只增加了 0.05 GB。这表明确实有大约 1 GB 的内存被预留(reserved)了,但实际分配(allocated)的并没有那么多。 日志中我们也可以看到,在 backward 之后,分配的内存(allocated)只有0.14GB,而预留(reserved)是1.12GB。这意味着大部分内存并没有被实际使用,而是被缓存(预留)以供后续使用。这通常是深度学习框架的内存管理策略,为了避免频繁分配和释放内存 我们尝试修改配置文件,将 reduce_bucket_size 设置为一个较小的值,比如50MB,然后观察内存变化。 在,我们更关心的是为什么在 backward 之后预留了 1.12 GB 的内存。这很可能是由于 DeepSpeed 为通信桶预留的内存。在 ZeRO-1 中,通信桶用于梯度的 All-Reduce。默认的桶大小是 500MB,但是这里我们看到了两倍于这个值的预留内存,可能的原因如下: 可能同时有两个桶(例如,一个用于当前步骤,一个用于下一步骤)?或者为了重叠通信和计算而预留了多个缓冲区。 可能还包括了优化器状态的内存。在 ZeRO-1 中,优化器状态是分片的,所以每个进程只存储一部分优化器状态。但是,我们的模型只有14.7M参数,即使使用FP16,优化器状态(包括参数、梯度、动量、方差)也不会这么大。 让我们计算一下优化器状态的内存: 参数:14.7M \* 2字节(FP16) = 29.4 MB 梯度:14.7M \* 2字节(FP16) = 29.4 MB 优化器状态(Adam):对于每个参数,有2个状态(动量、方差),每个状态是FP32,所以是4字节*2,所以每个参数需要8字节的优化器状态。 14.7M \* 8字节 = 117.6 MB 所以,即使加上优化器状态,总共也就大约 29.4*2 + 117.6 = 176.4 MB。所以1GB的预留内存显然不是由模型参数、梯度和优化器状态直接引起的。 因此,最可能的原因还是 DeepSpeed 的通信缓冲区。我们可以在配置中显式设置 reduce_bucket_size 和 allgather_bucket_size 来减小这个值。 另外,我们注意到在配置中使用了 ZeRO 阶段1,并且没有指定桶大小。DeepSpeed 默认的 reduce_bucket_size 是 500MB,而 allgather_bucket_size 在阶段1中并不使用(因为阶段1不需要allgather参数)。所以,这里可能只是 reduce_bucket_size 的影响。 修改后的配置: json { "zero_optimization": { "stage": 1, "reduce_bucket_size": 5e7 } } 所以,如果我们减少 reduce_bucket_size,我们应该会看到预留内存的减少。 另外,我们还可以尝试在代码中强制清理缓存,看看预留内存是否会释放: 修改配置 ```cpp { "train_batch_size": 256, "train_micro_batch_size_per_gpu": 128, "steps_per_print": 10, "optimizer": { "type": "Adam", "params": { "lr": 0.001, "betas": [0.9, 0.999], "eps": 1e-08, "weight_decay": 0 } }, "fp16": { "enabled": true, "loss_scale": 0, "loss_scale_window": 1000, "hysteresis": 2, "min_loss_scale": 1 }, "zero_optimization": { "stage": 1, "reduce_bucket_size": 5e7 } } python3.10) bash-4.4$ deepspeed --num_gpus=2 testzerodsTest.py --deepspeed_config ds_config_zero1.json /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/cuda/__init__.py:61: FutureWarning: The pynvml package is deprecated. Please install nvidia-ml-py instead. If you did not install pynvml directly, please report this to the maintainers of the package that installed pynvml for you. import pynvml # type: ignore[import] [2025-11-02 14:41:28,045] [INFO] [real_accelerator.py:222:get_accelerator] Setting ds_accelerator to cuda (auto detect) [2025-11-02 14:41:37,757] [WARNING] [runner.py:215:fetch_hostfile] Unable to find hostfile, will proceed with training with local resources only. [2025-11-02 14:41:37,758] [INFO] [runner.py:607:main] cmd = /opt/conda/envs/python3.10.13/bin/python3.10 -u -m deepspeed.launcher.launch --world_info=eyJsb2NhbGhvc3QiOiBbMCwgMV19 --master_addr=127.0.0.1 --master_port=29500 --enable_each_rank_log=None testzerodsTest.py --deepspeed_config ds_config_zero1.json /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/cuda/__init__.py:61: FutureWarning: The pynvml package is deprecated. Please install nvidia-ml-py instead. If you did not install pynvml directly, please report this to the maintainers of the package that installed pynvml for you. import pynvml # type: ignore[import] [2025-11-02 14:41:40,005] [INFO] [real_accelerator.py:222:get_accelerator] Setting ds_accelerator to cuda (auto detect) [2025-11-02 14:41:49,809] [INFO] [launch.py:139:main] 0 NCCL_IB_GID_INDEX=3 [2025-11-02 14:41:49,809] [INFO] [launch.py:139:main] 0 NCCL_NET_PLUGIN=none [2025-11-02 14:41:49,809] [INFO] [launch.py:139:main] 0 NCCL_DEBUG=INFO [2025-11-02 14:41:49,809] [INFO] [launch.py:146:main] WORLD INFO DICT: {'localhost': [0, 1]} [2025-11-02 14:41:49,809] [INFO] [launch.py:152:main] nnodes=1, num_local_procs=2, node_rank=0 [2025-11-02 14:41:49,810] [INFO] [launch.py:163:main] global_rank_mapping=defaultdict(, {'localhost': [0, 1]}) [2025-11-02 14:41:49,810] [INFO] [launch.py:164:main] dist_world_size=2 [2025-11-02 14:41:49,810] [INFO] [launch.py:168:main] Setting CUDA_VISIBLE_DEVICES=0,1 [2025-11-02 14:41:49,810] [INFO] [launch.py:256:main] process 155969 spawned with command: ['/opt/conda/envs/python3.10.13/bin/python3.10', '-u', 'testzerodsTest.py', '--local_rank=0', '--deepspeed_config', 'ds_config_zero1.json'] [2025-11-02 14:41:49,811] [INFO] [launch.py:256:main] process 155970 spawned with command: ['/opt/conda/envs/python3.10.13/bin/python3.10', '-u', 'testzerodsTest.py', '--local_rank=1', '--deepspeed_config', 'ds_config_zero1.json'] /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/cuda/__init__.py:61: FutureWarning: The pynvml package is deprecated. Please install nvidia-ml-py instead. If you did not install pynvml directly, please report this to the maintainers of the package that installed pynvml for you. import pynvml # type: ignore[import] /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/cuda/__init__.py:61: FutureWarning: The pynvml package is deprecated. Please install nvidia-ml-py instead. If you did not install pynvml directly, please report this to the maintainers of the package that installed pynvml for you. import pynvml # type: ignore[import] [2025-11-02 14:41:54,099] [INFO] [real_accelerator.py:222:get_accelerator] Setting ds_accelerator to cuda (auto detect) [2025-11-02 14:41:54,099] [INFO] [real_accelerator.py:222:get_accelerator] Setting ds_accelerator to cuda (auto detect) [2025-11-02 14:42:11,131] [INFO] [comm.py:658:init_distributed] cdb=None [2025-11-02 14:42:11,131] [INFO] [comm.py:689:init_distributed] Initializing TorchBackend in DeepSpeed with backend nccl [2025-11-02 14:42:11,131] [INFO] [comm.py:658:init_distributed] cdb=None Rank 0: After empty cache - Allocated: 0.00 GB, Reserved: 0.00 GB Rank 0: Model parameters: 14,687,232 = 14.69M Rank 0: Expected gradient size (FP16): 28.01 MB Rank 0: Expected gradient size (FP32): 56.03 MB Rank 0: After model creation - Allocated: 0.00 GB, Reserved: 0.00 GB [2025-11-02 14:42:11,214] [INFO] [logging.py:128:log_dist] [Rank 0] DeepSpeed info: version=0.16.4, git-hash=unknown, git-branch=unknown [2025-11-02 14:42:11,217] [INFO] [config.py:734:__init__] Config mesh_device None world_size = 2 Rank 1: After empty cache - Allocated: 0.00 GB, Reserved: 0.00 GB Rank 1: Model parameters: 14,687,232 = 14.69M Rank 1: Expected gradient size (FP16): 28.01 MB Rank 1: Expected gradient size (FP32): 56.03 MB Rank 1: After model creation - Allocated: 0.00 GB, Reserved: 0.00 GB [2025-11-02 14:42:12,476] [INFO] [config.py:734:__init__] Config mesh_device None world_size = 2 notebook-76ec3aa0aa0d-worker-0:155969:155969 [0] NCCL INFO Bootstrap: Using eth0:33.203.57.184<0> notebook-76ec3aa0aa0d-worker-0:155969:155969 [0] NCCL INFO ACCL_TUNING_LEVEL=2 notebook-76ec3aa0aa0d-worker-0:155969:155969 [0] NCCL INFO cudaDriverVersion 12040 notebook-76ec3aa0aa0d-worker-0:155969:155969 [0] NCCL INFO NCCL version 2.26.5.12-accl-n+cuda12.8, COMMIT_ID 2e6879b700b6cd1510bf9dc29a9db0132609d4cc, BUILD_TIME 2025-05-21 11:34:07 notebook-76ec3aa0aa0d-worker-0:155969:155969 [0] NCCL INFO Comm config Blocking set to 1 notebook-76ec3aa0aa0d-worker-0:155969:155969 [0] NCCL INFO Comm config Traffic class set to 1818586738 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO C4 stats mode none, reduce 1, send/recv 0. notebook-76ec3aa0aa0d-worker-0:155970:155970 [1] NCCL INFO cudaDriverVersion 12040 notebook-76ec3aa0aa0d-worker-0:155970:155970 [1] NCCL INFO Bootstrap: Using eth0:33.203.57.184<0> notebook-76ec3aa0aa0d-worker-0:155970:155970 [1] NCCL INFO ACCL_TUNING_LEVEL=2 notebook-76ec3aa0aa0d-worker-0:155970:155970 [1] NCCL INFO NCCL version 2.26.5.12-accl-n+cuda12.8, COMMIT_ID 2e6879b700b6cd1510bf9dc29a9db0132609d4cc, BUILD_TIME 2025-05-21 11:34:07 notebook-76ec3aa0aa0d-worker-0:155970:155970 [1] NCCL INFO Comm config Blocking set to 1 notebook-76ec3aa0aa0d-worker-0:155970:155970 [1] NCCL INFO Comm config Traffic class set to 1030188649 notebook-76ec3aa0aa0d-worker-0:155970:156634 [0] NCCL INFO C4 stats mode none, reduce 1, send/recv 0. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO NET/Plugin: Could not find: libnccl-net-none.so. Using internal net plugin. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO NET/IB : No device found. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO NET/IB : Using [RO]; OOB eth0:33.203.57.184<0> notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO NET/Socket : Using [0]eth0:33.203.57.184<0> notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO PROFILER/Plugin: Could not find: libnccl-profiler.so. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Using network Socket notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO NET/Plugin: Could not find: libnccl-net-none.so. Using internal net plugin. notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO NET/IB : No device found. notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO NET/IB : Using [RO]; OOB eth0:33.203.57.184<0> notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO NET/Socket : Using [0]eth0:33.203.57.184<0> notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO PROFILER/Plugin: Could not find: libnccl-profiler.so. notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO Using network Socket notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO ncclCommInitRankConfig comm 0x9fac070 rank 0 nranks 2 cudaDev 0 nvmlDev 0 busId 109000 commId 0x62380d103bc2ab46 commHash 7077421177785985862 - Init START notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO ncclCommInitRankConfig comm 0xb34f1a0 rank 1 nranks 2 cudaDev 1 nvmlDev 1 busId 17f000 commId 0x62380d103bc2ab46 commHash 7077421177785985862 - Init START notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO RAS client listening socket at ::1<28028> notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO RAS client listening socket at ::1<28028> notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO Bootstrap timings total 0.001037 (create 0.000026, send 0.000118, recv 0.000212, ring 0.000344, delay 0.000001) notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Bootstrap timings total 0.208179 (create 0.000025, send 0.000126, recv 0.207310, ring 0.000357, delay 0.000001) notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO gTaskid = b839cb210000b7ec. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO gTaskid = b839cb210000b7ec. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO hpn_license_register error: Error occurred during zmq_msg_recv(): Resource temporarily unavailable. notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO hpn_license_register error: Error occurred during zmq_msg_recv(): Resource temporarily unavailable. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO MNNVL busId 0x109000 fabric UUID 0.0 cliqueId 0x0 state 3 healthMask 0x0 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO MNNVL busId 0x17f000 fabric UUID 0.0 cliqueId 0x0 state 3 healthMask 0x0 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Setting affinity for GPU 0 to ffffffff,ffff0000,00000000,ffffffff,ffff0000,00000000 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO Setting affinity for GPU 1 to ffffffff,ffff0000,00000000,ffffffff,ffff0000,00000000 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO comm 0x9fac070 rank 0 nRanks 2 nNodes 1 localRanks 2 localRank 0 MNNVL 0 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO comm 0xb34f1a0 rank 1 nRanks 2 nNodes 1 localRanks 2 localRank 1 MNNVL 0 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 00/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 01/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 02/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 03/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 04/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 05/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO Trees [0] -1/-1/-1->1->0 [1] -1/-1/-1->1->0 [2] -1/-1/-1->1->0 [3] -1/-1/-1->1->0 [4] 0/-1/-1->1->-1 [5] 0/-1/-1->1->-1 [6] 0/-1/-1->1->-1 [7] 0/-1/-1->1->-1 [8] -1/-1/-1->1->0 [9] -1/-1/-1->1->0 [10] -1/-1/-1->1->0 [11] -1/-1/-1->1->0 [12] 0/-1/-1->1->-1 [13] 0/-1/-1->1->-1 [14] 0/-1/-1->1->-1 [15] 0/-1/-1->1->-1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 06/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 07/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO P2P Chunksize set to 524288 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 08/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 09/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 10/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 11/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 12/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 13/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 14/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Channel 15/16 : 0 1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Trees [0] 1/-1/-1->0->-1 [1] 1/-1/-1->0->-1 [2] 1/-1/-1->0->-1 [3] 1/-1/-1->0->-1 [4] -1/-1/-1->0->1 [5] -1/-1/-1->0->1 [6] -1/-1/-1->0->1 [7] -1/-1/-1->0->1 [8] 1/-1/-1->0->-1 [9] 1/-1/-1->0->-1 [10] 1/-1/-1->0->-1 [11] 1/-1/-1->0->-1 [12] -1/-1/-1->0->1 [13] -1/-1/-1->0->1 [14] -1/-1/-1->0->1 [15] -1/-1/-1->0->1 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO P2P Chunksize set to 524288 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Check P2P Type intraNodeP2pSupport 1 directMode 0 notebook-76ec3aa0aa0d-worker-0:155970:156687 [1] NCCL INFO [Proxy Service] Device 1 CPU core 82 notebook-76ec3aa0aa0d-worker-0:155970:156689 [1] NCCL INFO [Proxy Service UDS] Device 1 CPU core 83 notebook-76ec3aa0aa0d-worker-0:155969:156688 [0] NCCL INFO [Proxy Service] Device 0 CPU core 164 notebook-76ec3aa0aa0d-worker-0:155969:156690 [0] NCCL INFO [Proxy Service UDS] Device 0 CPU core 165 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO threadThresholds 8/8/64 | 16/8/64 | 512 | 512 notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO 16 coll channels, 16 collnet channels, 0 nvls channels, 16 p2p channels, 16 p2p channels per peer notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO threadThresholds 8/8/64 | 16/8/64 | 512 | 512 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO 16 coll channels, 16 collnet channels, 0 nvls channels, 16 p2p channels, 16 p2p channels per peer notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO CC Off, workFifoBytes 1048576 notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO TUNER/Plugin: Could not find: libnccl-tuner.so. Using internal tuner plugin. notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO TUNER/Plugin: Could not find: libnccl-tuner.so. Using internal tuner plugin. notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO ncclCommInitRankConfig comm 0x9fac070 rank 0 nranks 2 cudaDev 0 nvmlDev 0 busId 109000 commId 0x62380d103bc2ab46 commHash 7077421177785985862 - Init COMPLETE notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO ncclCommInitRankConfig comm 0xb34f1a0 rank 1 nranks 2 cudaDev 1 nvmlDev 1 busId 17f000 commId 0x62380d103bc2ab46 commHash 7077421177785985862 - Init COMPLETE notebook-76ec3aa0aa0d-worker-0:155969:156631 [0] NCCL INFO Init timings - ncclCommInitRankConfig: rank 0 nranks 2 total 7.23 (kernels 1.31, alloc 0.22, bootstrap 0.21, allgathers 0.01, topo 0.37, graphs 0.00, connections 0.11, rest 5.01) notebook-76ec3aa0aa0d-worker-0:155970:156634 [1] NCCL INFO Init timings - ncclCommInitRankConfig: rank 1 nranks 2 total 6.91 (kernels 1.18, alloc 0.23, bootstrap 0.00, allgathers 0.01, topo 0.37, graphs 0.00, connections 0.09, rest 5.02) notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 00/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 01/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 02/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 00/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 03/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 01/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 04/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 02/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 05/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 03/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 06/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 04/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 07/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 05/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 08/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 06/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 09/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 07/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 10/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 08/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 11/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 09/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 12/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 10/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 13/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 11/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 14/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 12/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Channel 15/0 : 1[1] -> 0[0] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 13/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 14/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Channel 15/0 : 0[0] -> 1[1] via P2P/CUMEM notebook-76ec3aa0aa0d-worker-0:155969:156692 [0] NCCL INFO Connected all rings, use ring PXN 0 GDR 1 notebook-76ec3aa0aa0d-worker-0:155970:156691 [1] NCCL INFO Connected all rings, use ring PXN 0 GDR 1 [2025-11-02 14:42:21,114] [INFO] [logging.py:128:log_dist] [Rank 0] DeepSpeed Flops Profiler Enabled: False Using :/usr/local/ninja as PyTorch extensions root... Using :/usr/local/ninja as PyTorch extensions root... Detected CUDA files, patching ldflags Emitting ninja build file :/usr/local/ninja/fused_adam/build.ninja... /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/utils/cpp_extension.py:2059: UserWarning: TORCH_CUDA_ARCH_LIST is not set, all archs for visible cards are included for compilation. If this is not desired, please set os.environ['TORCH_CUDA_ARCH_LIST']. warnings.warn( Building extension module fused_adam... Allowing ninja to set a default number of workers... (overridable by setting the environment variable MAX_JOBS=N) [1/3] /usr/local/cuda/bin/nvcc --generate-dependencies-with-compile --dependency-output multi_tensor_adam.cuda.o.d -DTORCH_EXTENSION_NAME=fused_adam -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -I/home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/ops/csrc/includes -I/home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/ops/csrc/adam -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include/TH -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include/THC -isystem /usr/local/cuda/include -isystem /opt/conda/envs/python3.10.13/include/python3.10 -D_GLIBCXX_USE_CXX11_ABI=0 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 -DVERSION_GE_1_1 -DVERSION_GE_1_3 -DVERSION_GE_1_5 -lineinfo --use_fast_math -gencode=arch=compute_90,code=sm_90 -gencode=arch=compute_90,code=compute_90 -DBF16_AVAILABLE -U__CUDA_NO_BFLOAT16_OPERATORS__ -U__CUDA_NO_BFLOAT162_OPERATORS__ -U__CUDA_NO_BFLOAT16_CONVERSIONS__ -std=c++17 -c /home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/ops/csrc/adam/multi_tensor_adam.cu -o multi_tensor_adam.cuda.o [2/3] c++ -MMD -MF fused_adam_frontend.o.d -DTORCH_EXTENSION_NAME=fused_adam -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -I/home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/ops/csrc/includes -I/home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/ops/csrc/adam -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include/TH -isystem /opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/include/THC -isystem /usr/local/cuda/include -isystem /opt/conda/envs/python3.10.13/include/python3.10 -D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -std=c++17 -O3 -std=c++17 -g -Wno-reorder -DVERSION_GE_1_1 -DVERSION_GE_1_3 -DVERSION_GE_1_5 -DBF16_AVAILABLE -c /home/wangshangquan.wsq/.local/lib/python3.10/site-packages/deepspeed/ops/csrc/adam/fused_adam_frontend.cpp -o fused_adam_frontend.o [3/3] c++ fused_adam_frontend.o multi_tensor_adam.cuda.o -shared -L/opt/conda/envs/python3.10.13/lib/python3.10/site-packages/torch/lib -lc10 -lc10_cuda -ltorch_cpu -ltorch_cuda -ltorch -ltorch_python -L/usr/local/cuda/lib64 -lcudart -o fused_adam.so Loading extension module fused_adam... Time to load fused_adam op: 27.844947814941406 seconds Rank 1: After DeepSpeed initialize - Allocated: 0.05 GB, Reserved: 0.11 GB Rank 1: ZeRO stage: 1 Rank 1: Optimizer has 1 parameter groups Rank 1: Parameters in engine: 14,687,232 Rank 1: Model dtype: torch.float16 Rank 1: Before forward - Allocated: 0.05 GB, Reserved: 0.11 GB Loading extension module fused_adam... Time to load fused_adam op: 27.943018198013306 seconds [2025-11-02 14:42:49,058] [INFO] [logging.py:128:log_dist] [Rank 0] Using DeepSpeed Optimizer param name adam as basic optimizer [2025-11-02 14:42:49,059] [INFO] [logging.py:128:log_dist] [Rank 0] Removing param_group that has no 'params' in the basic Optimizer [2025-11-02 14:42:49,059] [INFO] [logging.py:128:log_dist] [Rank 0] DeepSpeed Basic Optimizer = FusedAdam [2025-11-02 14:42:49,059] [INFO] [utils.py:59:is_zero_supported_optimizer] Checking ZeRO support for optimizer=FusedAdam type= [2025-11-02 14:42:49,059] [INFO] [logging.py:128:log_dist] [Rank 0] Creating torch.float16 ZeRO stage 1 optimizer [2025-11-02 14:42:49,059] [INFO] [stage_1_and_2.py:149:__init__] Reduce bucket size 50000000 [2025-11-02 14:42:49,059] [INFO] [stage_1_and_2.py:150:__init__] Allgather bucket size 500000000 [2025-11-02 14:42:49,059] [INFO] [stage_1_and_2.py:151:__init__] CPU Offload: False [2025-11-02 14:42:49,060] [INFO] [stage_1_and_2.py:152:__init__] Round robin gradient partitioning: False Rank 1: After forward - Allocated: 0.09 GB, Reserved: 0.14 GB Rank 1: After backward - Allocated: 0.15 GB, Reserved: 0.29 GB [2025-11-02 14:42:49,408] [INFO] [utils.py:781:see_memory_usage] Before initializing optimizer states [2025-11-02 14:42:49,408] [INFO] [utils.py:782:see_memory_usage] MA 0.05 GB Max_MA 0.07 GB CA 0.08 GB Max_CA 0 GB [2025-11-02 14:42:49,408] [INFO] [utils.py:789:see_memory_usage] CPU Virtual Memory: used = 23.44 GB, percent = 2.7% [2025-11-02 14:42:49,533] [INFO] [utils.py:781:see_memory_usage] After initializing optimizer states [2025-11-02 14:42:49,534] [INFO] [utils.py:782:see_memory_usage] MA 0.05 GB Max_MA 0.08 GB CA 0.11 GB Max_CA 0 GB [2025-11-02 14:42:49,534] [INFO] [utils.py:789:see_memory_usage] CPU Virtual Memory: used = 23.44 GB, percent = 2.7% [2025-11-02 14:42:49,534] [INFO] [stage_1_and_2.py:550:__init__] optimizer state initialized [2025-11-02 14:42:49,651] [INFO] [utils.py:781:see_memory_usage] After initializing ZeRO optimizer [2025-11-02 14:42:49,652] [INFO] [utils.py:782:see_memory_usage] MA 0.05 GB Max_MA 0.05 GB CA 0.11 GB Max_CA 0 GB [2025-11-02 14:42:49,652] [INFO] [utils.py:789:see_memory_usage] CPU Virtual Memory: used = 23.44 GB, percent = 2.7% [2025-11-02 14:42:49,652] [INFO] [logging.py:128:log_dist] [Rank 0] DeepSpeed Final Optimizer = DeepSpeedZeroOptimizer [2025-11-02 14:42:49,652] [INFO] [logging.py:128:log_dist] [Rank 0] DeepSpeed using configured LR scheduler = None [2025-11-02 14:42:49,652] [INFO] [logging.py:128:log_dist] [Rank 0] DeepSpeed LR Scheduler = None [2025-11-02 14:42:49,652] [INFO] [logging.py:128:log_dist] [Rank 0] step=0, skipped=0, lr=[0.001], mom=[[0.9, 0.999]] [2025-11-02 14:42:49,652] [INFO] [config.py:1001:print] DeepSpeedEngine configuration: [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] activation_checkpointing_config { "partition_activations": false, "contiguous_memory_optimization": false, "cpu_checkpointing": false, "number_checkpoints": null, "synchronize_checkpoint_boundary": false, "profile": false } [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] aio_config ................... {'block_size': 1048576, 'queue_depth': 8, 'intra_op_parallelism': 1, 'single_submit': False, 'overlap_events': True, 'use_gds': False} [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] amp_enabled .................. False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] amp_params ................... False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] autotuning_config ............ { "enabled": false, "start_step": null, "end_step": null, "metric_path": null, "arg_mappings": null, "metric": "throughput", "model_info": null, "results_dir": "autotuning_results", "exps_dir": "autotuning_exps", "overwrite": true, "fast": true, "start_profile_step": 3, "end_profile_step": 5, "tuner_type": "gridsearch", "tuner_early_stopping": 5, "tuner_num_trials": 50, "model_info_path": null, "mp_size": 1, "max_train_batch_size": null, "min_train_batch_size": 1, "max_train_micro_batch_size_per_gpu": 1.024000e+03, "min_train_micro_batch_size_per_gpu": 1, "num_tuning_micro_batch_sizes": 3 } [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] bfloat16_enabled ............. False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] bfloat16_immediate_grad_update False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] checkpoint_parallel_write_pipeline False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] checkpoint_tag_validation_enabled True [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] checkpoint_tag_validation_fail False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] comms_config ................. [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] communication_data_type ...... None [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] compression_config ........... {'weight_quantization': {'shared_parameters': {'enabled': False, 'quantizer_kernel': False, 'schedule_offset': 0, 'quantize_groups': 1, 'quantize_verbose': False, 'quantization_type': 'symmetric', 'quantize_weight_in_forward': False, 'rounding': 'nearest', 'fp16_mixed_quantize': False, 'quantize_change_ratio': 0.001}, 'different_groups': {}}, 'activation_quantization': {'shared_parameters': {'enabled': False, 'quantization_type': 'symmetric', 'range_calibration': 'dynamic', 'schedule_offset': 1000}, 'different_groups': {}}, 'sparse_pruning': {'shared_parameters': {'enabled': False, 'method': 'l1', 'schedule_offset': 1000}, 'different_groups': {}}, 'row_pruning': {'shared_parameters': {'enabled': False, 'method': 'l1', 'schedule_offset': 1000}, 'different_groups': {}}, 'head_pruning': {'shared_parameters': {'enabled': False, 'method': 'topk', 'schedule_offset': 1000}, 'different_groups': {}}, 'channel_pruning': {'shared_parameters': {'enabled': False, 'method': 'l1', 'schedule_offset': 1000}, 'different_groups': {}}, 'layer_reduction': {'enabled': False}} [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] curriculum_enabled_legacy .... False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] curriculum_params_legacy ..... False [2025-11-02 14:42:49,653] [INFO] [config.py:1005:print] data_efficiency_config ....... {'enabled': False, 'seed': 1234, 'data_sampling': {'enabled': False, 'num_epochs': 1000, 'num_workers': 0, 'curriculum_learning': {'enabled': False}}, 'data_routing': {'enabled': False, 'random_ltd': {'enabled': False, 'layer_token_lr_schedule': {'enabled': False}}}} [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] data_efficiency_enabled ...... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] dataloader_drop_last ......... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] disable_allgather ............ False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] dump_state ................... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] dynamic_loss_scale_args ...... {'init_scale': 65536, 'scale_window': 1000, 'delayed_shift': 2, 'consecutive_hysteresis': False, 'min_scale': 1} [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_enabled ........... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_gas_boundary_resolution 1 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_layer_name ........ bert.encoder.layer [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_layer_num ......... 0 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_max_iter .......... 100 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_stability ......... 1e-06 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_tol ............... 0.01 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] eigenvalue_verbose ........... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] elasticity_enabled ........... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] flops_profiler_config ........ { "enabled": false, "recompute_fwd_factor": 0.0, "profile_step": 1, "module_depth": -1, "top_modules": 1, "detailed": true, "output_file": null } [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] fp16_auto_cast ............... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] fp16_enabled ................. True [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] fp16_master_weights_and_gradients False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] global_rank .................. 0 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] grad_accum_dtype ............. None [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] gradient_accumulation_steps .. 1 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] gradient_clipping ............ 0.0 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] gradient_predivide_factor .... 1.0 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] graph_harvesting ............. False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] hybrid_engine ................ enabled=False max_out_tokens=512 inference_tp_size=1 release_inference_cache=False pin_parameters=True tp_gather_partition_size=8 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] initial_dynamic_scale ........ 65536 [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] load_universal_checkpoint .... False [2025-11-02 14:42:49,654] [INFO] [config.py:1005:print] loss_scale ................... 0 [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] memory_breakdown ............. False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] mics_hierarchial_params_gather False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] mics_shard_size .............. -1 [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] monitor_config ............... tensorboard=TensorBoardConfig(enabled=False, output_path='', job_name='DeepSpeedJobName') comet=CometConfig(enabled=False, samples_log_interval=100, project=None, workspace=None, api_key=None, experiment_name=None, experiment_key=None, online=None, mode=None) wandb=WandbConfig(enabled=False, group=None, team=None, project='deepspeed') csv_monitor=CSVConfig(enabled=False, output_path='', job_name='DeepSpeedJobName') [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] nebula_config ................ { "enabled": false, "persistent_storage_path": null, "persistent_time_interval": 100, "num_of_version_in_retention": 2, "enable_nebula_load": true, "load_path": null } [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] optimizer_legacy_fusion ...... False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] optimizer_name ............... adam [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] optimizer_params ............. {'lr': 0.001, 'betas': [0.9, 0.999], 'eps': 1e-08, 'weight_decay': 0} [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] pipeline ..................... {'stages': 'auto', 'partition': 'best', 'seed_layers': False, 'activation_checkpoint_interval': 0, 'pipe_partitioned': True, 'grad_partitioned': True} [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] pld_enabled .................. False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] pld_params ................... False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] prescale_gradients ........... False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] scheduler_name ............... None [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] scheduler_params ............. None [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] seq_parallel_communication_data_type torch.float32 [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] sparse_attention ............. None [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] sparse_gradients_enabled ..... False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] steps_per_print .............. 10 [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] tensor_parallel_config ....... dtype=torch.float16 autotp_size=0 tensor_parallel=TPConfig(tp_size=1, tp_grain_size=1, mpu=None, tp_group=None) injection_policy_tuple=None keep_module_on_host=False replace_with_kernel_inject=False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] timers_config ................ enabled=True synchronized=True [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] train_batch_size ............. 256 [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] train_micro_batch_size_per_gpu 128 [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] use_data_before_expert_parallel_ False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] use_node_local_storage ....... False [2025-11-02 14:42:49,655] [INFO] [config.py:1005:print] wall_clock_breakdown ......... False [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] weight_quantization_config ... None [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] world_size ................... 2 [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] zero_allow_untested_optimizer False [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] zero_config .................. stage=1 contiguous_gradients=True reduce_scatter=True reduce_bucket_size=50000000 use_multi_rank_bucket_allreduce=True allgather_partitions=True allgather_bucket_size=500000000 overlap_comm=False load_from_fp32_weights=True elastic_checkpoint=False offload_param=None offload_optimizer=None sub_group_size=1000000000 cpu_offload_param=None cpu_offload_use_pin_memory=None cpu_offload=None prefetch_bucket_size=50000000 param_persistence_threshold=100000 model_persistence_threshold=9223372036854775807 max_live_parameters=1000000000 max_reuse_distance=1000000000 gather_16bit_weights_on_model_save=False module_granularity_threshold=0 use_all_reduce_for_fetch_params=False stage3_gather_fp16_weights_on_model_save=False ignore_unused_parameters=True legacy_stage1=False round_robin_gradients=False zero_hpz_partition_size=1 zero_quantized_weights=False zero_quantized_nontrainable_weights=False zero_quantized_gradients=False zeropp_loco_param=None mics_shard_size=-1 mics_hierarchical_params_gather=False memory_efficient_linear=True pipeline_loading_checkpoint=False override_module_apply=True log_trace_cache_warnings=False [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] zero_enabled ................. True [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] zero_force_ds_cpu_optimizer .. True [2025-11-02 14:42:49,656] [INFO] [config.py:1005:print] zero_optimization_stage ...... 1 [2025-11-02 14:42:49,656] [INFO] [config.py:991:print_user_config] json = { "train_batch_size": 256, "train_micro_batch_size_per_gpu": 128, "steps_per_print": 10, "optimizer": { "type": "Adam", "params": { "lr": 0.001, "betas": [0.9, 0.999], "eps": 1e-08, "weight_decay": 0 } }, "fp16": { "enabled": true, "loss_scale": 0, "loss_scale_window": 1000, "hysteresis": 2, "min_loss_scale": 1 }, "zero_optimization": { "stage": 1, "reduce_bucket_size": 5.000000e+07 } } Rank 0: After DeepSpeed initialize - Allocated: 0.05 GB, Reserved: 0.11 GB Rank 0: ZeRO stage: 1 Rank 0: Optimizer has 1 parameter groups Rank 0: Parameters in engine: 14,687,232 Rank 0: Model dtype: torch.float16 Rank 0: Before forward - Allocated: 0.05 GB, Reserved: 0.11 GB Rank 0: After forward - Allocated: 0.09 GB, Reserved: 0.14 GB Rank 0: After backward - Allocated: 0.14 GB, Reserved: 0.28 GB [2025-11-02 14:42:49,890] [INFO] [loss_scaler.py:190:update_scale] [deepspeed] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 65536, but hysteresis is 2. Reducing hysteresis to 1 Rank 1: After step - Allocated: 0.15 GB, Reserved: 0.29 GB Rank 0: After step - Allocated: 0.14 GB, Reserved: 0.28 GB [rank0]:[W1102 14:42:50.589162769 ProcessGroupNCCL.cpp:1496] Warning: WARNING: destroy_process_group() was not called before program exit, which can leak resources. For more info, please see https://pytorch.org/docs/stable/distributed.html#shutdown (function operator()) [2025-11-02 14:42:51,870] [INFO] [launch.py:351:main] Process 155969 exits successfully. [2025-11-02 14:42:51,871] [INFO] [launch.py:351:main] Process 155970 ex ``` ## 稳定后内存分析 在这里插入图片描述 由rofiler_schedule = torch.profiler.schedule(wait=0, warmup=0, active=6, repeat=1) 改为rofiler_schedule = torch.profiler.schedule(wait=2, warmup=2, active=6, repeat=1) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/93640420c7314d11a9b39eb84ec5b242.png) ## 启动前内存 ![分析](https://i-blog.csdnimg.cn/direct/018739f4625d44c1b990ee97a574e13e.png) ```cpp import torch import torch.nn as nn import deepspeed import argparse import os import socket from datetime import datetime # --- Model 和 get_args 定义 (与之前相同) --- class SimpleModel(nn.Module): def __init__(self): super().__init__() self.layer1 = nn.Linear(1024, 2048) self.relu1 = nn.ReLU() self.layer2 = nn.Linear(2048, 4096) self.relu2 = nn.ReLU() self.layer3 = nn.Linear(4096, 1024) def forward(self, x): return self.relu2(self.layer2(self.relu1(self.layer1(x)))) def get_args(): parser = argparse.ArgumentParser(description='Record Memory Snapshot with DeepSpeed') parser.add_argument('--local_rank', type=int, default=-1, help='local rank passed from distributed launcher') parser = deepspeed.add_config_arguments(parser) args = parser.parse_args() return args # --- 从 Meta 示例中适配的辅助函数 --- # 每个快照中记录的最大内存事件数 MAX_NUM_OF_MEM_EVENTS_PER_SNAPSHOT: int = 100000 def start_record_memory_history(): if not torch.cuda.is_available(): print("CUDA unavailable. Not recording memory history") return print("Starting snapshot record_memory_history") torch.cuda.memory._record_memory_history( max_entries=MAX_NUM_OF_MEM_EVENTS_PER_SNAPSHOT ) def stop_record_memory_history(): if not torch.cuda.is_available(): return print("Stopping snapshot record_memory_history") torch.cuda.memory._record_memory_history(enabled=None) def export_memory_snapshot(rank): """ 为特定的 rank 导出内存快照。 """ if not torch.cuda.is_available(): return # 为每个 rank 生成唯一的文件名 host_name = socket.gethostname() timestamp = datetime.now().strftime("%b_%d_%H_%M_%S") file_prefix = f"{host_name}_{timestamp}_rank{rank}" filename = f"{file_prefix}.pickle" try: print(f"Rank {rank}: Saving snapshot to local file: {filename}") torch.cuda.memory._dump_snapshot(filename) except Exception as e: print(f"Rank {rank}: Failed to capture memory snapshot {e}") # --- 主函数 --- def main(): args = get_args() deepspeed.init_distributed() rank = torch.distributed.get_rank() torch.cuda.set_device(args.local_rank) model = SimpleModel() model_engine, _, _, _ = deepspeed.initialize( args=args, model=model, model_parameters=model.parameters() ) # 在所有 rank 上启动内存历史记录 start_record_memory_history() # 运行几个 step for step in range(5): print(f"Rank {rank}, starting Step {step}...") model_dtype = next(model_engine.module.parameters()).dtype inputs = torch.randn( model_engine.train_micro_batch_size_per_gpu(), 1024, device=model_engine.device ).to(model_dtype) loss = model_engine(inputs).mean() model_engine.backward(loss) model_engine.step() # 在所有 rank 上导出各自的内存快照 export_memory_snapshot(rank) # 停止记录 stop_record_memory_history() print(f"Rank {rank} finished.") if __name__ == "__main__": main() ``` ## 稳定后 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/8d88519af1404c2891676b0f68e8e146.png) ```cpp { "train_batch_size": 256, "train_micro_batch_size_per_gpu": 128, "steps_per_print": 10, "optimizer": { "type": "Adam", "params": { "lr": 1e-3, "betas": [0.9, 0.999], "eps": 1e-8, "weight_decay": 0 } }, "fp16": { "enabled": true, "loss_scale": 0, "loss_scale_window": 1000, "hysteresis": 2, "min_loss_scale": 1 }, "zero_optimization": { "stage": 1, "reduce_bucket_size": 5e7 } } ``` ```cpp import torch import torch.nn as nn import deepspeed import argparse import os import socket from datetime import datetime # --- Model 和 get_args 定义 (与之前相同) --- class SimpleModel(nn.Module): def __init__(self): super().__init__() self.layer1 = nn.Linear(1024, 2048) self.relu1 = nn.ReLU() self.layer2 = nn.Linear(2048, 4096) self.relu2 = nn.ReLU() self.layer3 = nn.Linear(4096, 1024) def forward(self, x): return self.relu2(self.layer2(self.relu1(self.layer1(x)))) def get_args(): parser = argparse.ArgumentParser(description='Record Memory Snapshot with DeepSpeed') parser.add_argument('--local_rank', type=int, default=-1, help='local rank passed from distributed launcher') parser = deepspeed.add_config_arguments(parser) args = parser.parse_args() return args # --- 从 Meta 示例中适配的辅助函数 --- # 每个快照中记录的最大内存事件数 MAX_NUM_OF_MEM_EVENTS_PER_SNAPSHOT: int = 100000 def start_record_memory_history(): if not torch.cuda.is_available(): print("CUDA unavailable. Not recording memory history") return print("Starting snapshot record_memory_history") torch.cuda.memory._record_memory_history( max_entries=MAX_NUM_OF_MEM_EVENTS_PER_SNAPSHOT ) def stop_record_memory_history(): if not torch.cuda.is_available(): return print("Stopping snapshot record_memory_history") torch.cuda.memory._record_memory_history(enabled=None) def export_memory_snapshot(rank): """ 为特定的 rank 导出内存快照。 """ if not torch.cuda.is_available(): return # 为每个 rank 生成唯一的文件名 host_name = socket.gethostname() timestamp = datetime.now().strftime("%b_%d_%H_%M_%S") file_prefix = f"{host_name}_{timestamp}_rank{rank}" filename = f"{file_prefix}.pickle" try: print(f"Rank {rank}: Saving snapshot to local file: {filename}") torch.cuda.memory._dump_snapshot(filename) except Exception as e: print(f"Rank {rank}: Failed to capture memory snapshot {e}") # --- 主函数 --- def main(): args = get_args() deepspeed.init_distributed() rank = torch.distributed.get_rank() torch.cuda.set_device(args.local_rank) model = SimpleModel() model_engine, _, _, _ = deepspeed.initialize( args=args, model=model, model_parameters=model.parameters() ) # --- 【核心修改】将训练循环分为预热和记录两个阶段 --- # 1. 预热阶段 (前 10 个 step) warmup_steps = 10 print(f"Rank {rank}: Starting {warmup_steps} warmup steps...") for step in range(warmup_steps): print(f"Rank {rank}, starting Warmup Step {step}...") model_dtype = next(model_engine.module.parameters()).dtype inputs = torch.randn( model_engine.train_micro_batch_size_per_gpu(), 1024, device=model_engine.device ).to(model_dtype) loss = model_engine(inputs).mean() model_engine.backward(loss) model_engine.step() print(f"Rank {rank}: Warmup finished.") # 2. 记录阶段 (在预热后开始) # 先启动内存记录器 start_record_memory_history() # 再运行几个 step 以捕获数据 record_steps = 5 print(f"Rank {rank}: Starting {record_steps} recording steps...") for step in range(record_steps): print(f"Rank {rank}, starting Recording Step {step}...") # 训练逻辑与预热阶段完全相同 model_dtype = next(model_engine.module.parameters()).dtype inputs = torch.randn( model_engine.train_micro_batch_size_per_gpu(), 1024, device=model_engine.device ).to(model_dtype) loss = model_engine(inputs).mean() model_engine.backward(loss) model_engine.step() print(f"Rank {rank}: Recording finished.") # 记录阶段结束后,导出快照 export_memory_snapshot(rank) # 停止记录 stop_record_memory_history() print(f"Rank {rank} finished training.") if __name__ == "__main__": main() ```

相关推荐
大千AI助手6 小时前
分布式奇异值分解(SVD)详解
人工智能·分布式·spark·奇异值分解·svd·矩阵分解·分布式svd
AgeClub6 小时前
当“钢铁护工”进入家庭,Figure 03如何重建老年居家生活?
大数据·人工智能
RWKV元始智能6 小时前
RWKV7-G0a3 13.3B 发布:世界最强纯 RNN 大语言模型
人工智能·机器学习·开源
星辰pid6 小时前
基于ROS与YOLOv3的智能采购机器人设计(智能车创意组-讯飞智慧生活组)
人工智能·opencv·yolo·机器人
weixin_307779136 小时前
基于AWS服务的客户服务电话情感分析解决方案
人工智能·深度学习·机器学习·云计算·aws
极客BIM工作室6 小时前
U-Net 的输入与输出:通用场景与扩散模型场景解析
人工智能·深度学习·计算机视觉
哥布林学者6 小时前
吴恩达深度学习课程二: 改善深层神经网络 第一周:深度学习的实践(六)梯度现象和梯度检验
深度学习·ai
说私域6 小时前
定制开发开源AI智能名片S2B2C商城小程序中的羊群效应应用研究
人工智能·小程序
databook7 小时前
AI辅助编程下的软件分层设计:让生成的代码井然有序
人工智能·程序员·架构