Megatron-LM学习笔记(8)DDP Model,Optimizer,Scheduler

Part 8. Model,Optimizer,Scheduler初始化和调用

Megatron-LM优化器初始化在pretrain函数中,调用setup_model_and_optimizer函数进行模型、优化器、学习率调度器的初始化,进一步调用get_model

  • get model根据pipeline 并行设置 pre_process / post_process,只在该 rank 构建自己负责的段;如开启虚拟 pipeline,会为本 rank 的多个 virtual stage 分别构建对应段。
  • 构建后会为所有参数设置/修复 tensor-parallel 属性,可以看到会从列表中取出所有的模型分段,GPTModel->LanguageModel->MegatronModule->Torch.nn.Module,自动会有.parameters()属性。默认情况下,没有设置过tensor parallel的都会把属性设置为False。
  • 然后再做 DDP/FSDP 包装。只有当 tensor_model_parallel_size=1 且 pipeline_model_parallel_size=1 时,每个 rank 才是完整模型。
  • 最后把parameters在DP组内广播

这里Megatron实现了自己的DDP。DDP负责了不同DP副本之间的梯度同步,也会负责处理和EP相关的内容。使用DDP.module可以做到unwrap获取到原本的模型。

  • DDP使用了bucket模式。反向传播的时候,不是所有的参数梯度都算完才同步,而是设置一个桶,反向参数算好梯度就装进桶里,桶满了就立刻开始规约同步。这样重叠了梯度计算和通信时间。
  • DDP中会注册grad buffer,并把param.main_grad指向该切片。参数的局部param.grad计算完成后会累加到main_grad中,清空param.grad,按照要求选择触发reduce scatter或者all reduce
  • EP size是MoE专家分到几个GPU上。比如32卡16专家模型,DP=8,TP=4,EP=4。则每8张卡有相同的模型和不同的数据。EP=4,则16个专家分到4张卡上,每张卡4个专家。在DP=8的基础上,EP又可以并行,即EDP,此时8个DP卡中,每两张卡有相同的4个专家。
  • 补充说明CP:CP其实是切的更细的DP,因为他们的模型权重都是一样的。

终于初始化好模型了,现在回来初始化optimizer。调用get_megatron_optimizer.

  • 这个函数会返回一个MegatronOptimizer类的优化器,在torch实现或者Transformer Engine的optimizer的基础上做了一些封装,支持自定义的梯度同步等。在MegatronOptimizer上有MixedPrecision Optimizer和进一步的FP16optimizer,以及FP32全精度Optimizer。

一个Optimizer初始化的时候,相对来说比较简单,主要就是把一些参数准备好,以及准备好一些可用函数

  • 全精度FP32直接用模型参数做更新,不做主副本;step 时仅把 param.main_grad 绑定回 param.grad 再 optimizer.step(),不涉及精度转换;main_grad时Megatron自己维护的梯度缓冲指针,可以指向一个大号的连续buffer切片,能够做梯度积累与all reduce等,提高通信和内存效率

  • 混合精度训练,优化器会在初始化时为所有FP16、BF16参数创造一份FP32的 main param参数,把optimizer里指向的内容替换成main_param,同时让原本的param指向main_param。最后对于有state的optimizer,还要把state(动量等)中的原本参数的state换成main param的。

  • 在保存检查点的时候,只会额外保存一份fp16格式参数。

  • 原本的model的param,有自己的main_grad和grad。其中grad用于反向传播给优化器用,main_grad用于累积和通信用。在混合精度优化器中,半精度参数由于有一份新的单精度副本main_param,因此main_param有自己的grad用于给优化器更新。

  • 提供reload和同步参数功能。reload把外部参数改变同步到优化器内部,_copy_main_params_to_model_params()把更新后的参数同步给外部模型

optimizer最重要的职责就是step。一个step大致是这样的:

  • 准备梯度prepare_grads,主要是把main_grad交给grad,以及检查可能的inf/nan,用于给scaler使用;然后是可选的clip_grad_norm,计算所有参数的norm并按比例缩放等;数一下grad里有多少个0,最后step_with_ready_grads,内部有optimizer那就让他step就好了,最后把FP32主参数同步给可能有的外部半精度参数 _copy_main_params_to_model_params()
  • FP32 Optimizer中,不需要做什么特别的额外处理;在半精度optimizer中,如果找到了inf,就直接返回step失败
  • 半精度Optimizer有grad_scaler,是MegatronGradScaler类。它主要就是维护scale数值。scale控制loss大小,从而同步控制梯度大小。loss不能太小否则下溢,也不能太大否则上溢。因此,scaler监测到inf或者nan的时候就按比例缩小scale,反之当连续若干步没有溢出,按比例放大scale。如果检测到Nan,则step失败,训练循环中会直接跳过这个iteration,后续训练基于更新后的scale进行。

在计算参数p范数的时候是需要涉及到通信的。如果有DP则跨DP求和norm^p次方,之后跨grad_stats_parallel_group求和。后者在非分布式优化器的场景下就是TP/PP等模型并行集合,分布式情况下是所有的rank。因为分布式优化器相当于用上了ZeRO,状态被进一步切分,即使是有相同模型参数的DP内也被分到了不同的grad。
state_dict:传统的单体(非分片)状态,直接透传底层 optimizer.state_dict(),混精时再附带 grad_scaler 与 fp32 主参数列表。经典单机/单 rank 或未使用 Megatron 分布式检查点工具时,用 torch.save/torch.load 保存/加载优化器;或与旧版/第三方流程兼容。即使是用了TP/PP也是可以用的。
sharded_state_dict:面向分布式检查点的分片格式,基于模型的 ShardedStateDict,把参数和优化器状态映射成可分片/可并行保存的结构(含 shard 元数据、common_step 等),便于 ZeRO/FSDP/张量并行场景按 rank 拆分和重组。使用 Megatron 的分布式检查点(dist_checkpointing)路径时调用,用于分片保存/加载(每个 rank 写/读自己的 shard,再按映射重组);也支撑 tensor/pipeline/data parallel 以及 FSDP/Zero 场景。要use_dist_ckpt参数启用

get_megatron_optimizer按所有的model 模块轮流赋予optimizer。如果没有虚拟流水线那就只有一块,否则就有多块。总体上来说,先获取param groups与对应的buffer。因为不同的参数可能有自己不同的weight decay、lr mult设定等。通过分组能够统一调整有相同参数的组。另外MoE和Dense也有自己的通信路径。optimizer管理每个group的lr,weight_decay,wd_mult,max_lr,min_lr等。整理好group后,再设置好dense和moe的optimizer组成一个列表。这个列表被包装成ChainedOptimizer并返回,其实就是对多个Optimizer做for循环操作,没啥特别的,比较简单。

set_up_model_and_optimizer之后进行param scheduler调度。传入optimizer,返回一个OptimizerParamScheduler类。

  • Scheduler主要就一个step。step中会get_wd、get_lr。step就是遍历optimizer的param groups,赋予新的学习率和权重衰减
  • get_lr就很简单,没啥特别的,增加减小,warmup cosine linear exponetial等等。
  • 在core_r0.12.1版本中,貌似所有参数都用相同的wd策略。尽管之前说了optimizer的param group会有按照wd分组,不过并没有像lr那样细到per group调整。可能是因为大部分时候也用不上对WD调整那么多吧

最后set_up_model_and_optimizer也会额外导入bert模型的权重,转换可能的检查点格式(传统的torch pt和dist checkpoint的互相转换),返回初始化好的model optimizer和opt_param_scheduler三员大将。

在train step中,每一步:

  • Optimizer.zero_grad(),把param的grad和main_grad_buffer都清空
  • forward_backward_func,用前向函数和模型做forward以及backward,获取losses_reduced
    • 三个操作,forward_step和backward_step后,finalize_model_grads_func()会准备好梯度
    • 在DP之间all reduce梯度,准备layernorm的grads(SP使用),以及embedding流水线头尾的grads,最后把gradient按token数scaling
  • 在backward函数中间,会用config.grad_scale_func,也就是optimizer里的scale_loss,直接loss_scale * loss(真是好大一大圈麻烦)
  • optimizer.step(),调用前面所说的准备梯度,检查和更新grad_scaler,等等
  • scheduler.step(),更新学习率
相关推荐
星火开发设计17 小时前
Python数元组完全指南:从基础到实战
开发语言·windows·python·学习·知识·tuple
冻伤小鱼干17 小时前
《自动驾驶与机器人中的slam技术:从理论到实践》笔记——ch8(1)
笔记·机器人·自动驾驶
maray17 小时前
体验 Neon 产品
数据库·学习
炽烈小老头17 小时前
【每天学习一点算法 2026/01/08】计数质数
学习·算法
One_Piece_Fu17 小时前
2026年node.js最新版下载(24.12.0LTS)安装教程(详细)
vscode·学习·node.js
c76917 小时前
【文献笔记】Mixture-of-Agents Enhances Large Language Model Capabilities
人工智能·笔记·语言模型·自然语言处理·论文笔记·提示工程
HXR_plume17 小时前
【Web信息处理与应用课程笔记8】知识图谱与图计算
人工智能·笔记·知识图谱
非凡ghost18 小时前
12-Ants(轻量级桌面娱乐工具)
windows·学习·娱乐·软件需求
汽车通信软件大头兵18 小时前
Autosar 工具 :Vector Davince 可用于个人学习啦
学习·汽车·uds·isolar