[bugfix] fix DeepSeek V4 FP8 _set_o_group_proj_grouped crash when exporting LoRA adapter under PP>1#149
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the _set_o_group_proj_grouped method in deepseek_v4.py to be pipeline-parallel (PP) aware by utilizing _get_weight and _set_weight for distributed communication. It also adds checks and raises NotImplementedError for unsupported FP8 grouped linear projection configurations under PEFT. Feedback was provided regarding a potential runtime crash when calling dist.all_reduce on a boolean tensor without specifying the reduction operator under the NCCL backend; it is recommended to explicitly use dist.ReduceOp.MAX.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if not to_mcore and self.pp_size > 1: | ||
| state = torch.tensor([is_lora, is_modules_to_save], dtype=torch.bool, device='cuda') | ||
| dist.all_reduce(state, group=self.pp_group) | ||
| is_lora, is_modules_to_save = state[0].item(), state[1].item() |
There was a problem hiding this comment.
Using dist.all_reduce on a boolean tensor without specifying the reduction operator defaults to dist.ReduceOp.SUM. Under the NCCL backend, SUM is not supported on boolean tensors and will cause a runtime crash. Since we want to check if any rank has LoRA or modules to save, we should explicitly use dist.ReduceOp.MAX (which acts as a logical OR for boolean values).
| if not to_mcore and self.pp_size > 1: | |
| state = torch.tensor([is_lora, is_modules_to_save], dtype=torch.bool, device='cuda') | |
| dist.all_reduce(state, group=self.pp_group) | |
| is_lora, is_modules_to_save = state[0].item(), state[1].item() | |
| if not to_mcore and self.pp_size > 1: | |
| state = torch.tensor([is_lora, is_modules_to_save], dtype=torch.bool, device='cuda') | |
| dist.all_reduce(state, op=dist.ReduceOp.MAX, group=self.pp_group) | |
| is_lora, is_modules_to_save = state[0].item(), state[1].item() |
There was a problem hiding this comment.
mcore-bridge/src/mcore_bridge/bridge/gpt_bridge.py
Lines 472 to 478 in 38316cf
这里的已有实现与我写的模式完全一致, 没有显式提供 op 参数, 虽然确实提供比较好, 但是如果需要的话应该作为单独的 PR?
修复
_set_o_group_proj_grouped在 PP>1 下导出时因mg_attn=None崩溃的问题.问题分析
linear_o_group_proj(wo_a) 是te.GroupedLinear, 存储多个weight{i}, 无法通过_set_state_dict单一 key 路径处理.mcore-bridge/src/mcore_bridge/model/gpts/deepseek_v4.py
Lines 500 to 519 in 38316cf
原实现直接访问
mg_attn.linear_o_group_proj并手动拼接 tensor, 完全绕过了 bridge 的 PP 通信机制, 导致在 PP>1 时每个 stage 对于自己没持有的层,mg_attn = None, 从而出现如下错误:修复方案
重写
_set_o_group_proj_grouped, 复用 bridge 统一出口:_get_weight(params_list_or_None, mg_key)实现导出, 自动处理 FP8 解析、scale 裁剪、PP 之间同步等等_set_weight(params_list, hf_weight, mg_key, hf_scale_inv=...)实现导入peft_format触发return跳过, 与非 FP8 路径_set_state_dict行为对齐, 防止导出冻结的基础权重merge_lora=True时, 在合并后 unwarp 取出group_proj.base_layer获取合并后的真实权重虽然 Deepseek V4 目前没有支持 TP, 但后续支持之后应该可以直接复用
_get_weight / _set_weight中的 TPgather/split 逻辑, 也不需要手动拼接.
局限性
对于
wo_a(也就是 megatron 的linear_o_group_proj):modules_to_save的支持逻辑考虑到
wo_a本身是一个地址投影, 应该不会被注入 lora 训练, 目前会返回NotImplementedError, 如果有需要后续可以再添加验证
对于 DeepSeekV4 Flash FP8 权重下 lora 微调, PP=2, EP=CP=8, TP=ETP=1, 预先训练 3 个 step:
后续 6 个 step 的续训, 从 adapter 加载之前的权重 (没有做到学习率和数据集的连续, 不过看 loss 对比最开始的 3 个 step 均有下降):
同时经过检查, 导出后的 adapter 不包含 wo_a 相关权重, 结构完整.