当前位置: 首页 > news >正文

【Bug已解决】fsdp2 + lora |TypeError: fully_shard() got an unexpected keyword argument ‘ignored_params‘ 解

【Bug已解决】fsdp2 + lora |TypeError: fully_shard() got an unexpected keyword argument 'ignored_params' 解决方案

一、现象长什么样

在用 FSDP2 做 LoRA 训练时,想让基座参数被全分片、而 LoRA 的lora_A/lora_B参数不被分片(保持每卡完整,方便 merge / 节省通信)。照着 FSDP1 / DDP 的经验,给fully_shard传了ignored_params

model = fully_shard(model, ignored_params=lora_params)

结果直接抛:

TypeError: fully_shard() got an unexpected keyword argument 'ignored_params'

最小判据:

触发:fsdp2 + lora,给 fully_shard 传 ignored_params 现象:TypeError,参数不被接受 根因:FSDP2 的 fully_shard 没有 ignored_params 这个参数(那是 FSDP1/DDP 的概念) 影响:无法用"旧姿势"让 LoRA 参数不分片

最迷惑的是:ignored_paramsFullyShardedDataParallel(FSDP1) 和DistributedDataParallel里是合法参数,文档里也常见;可 FSDP2 的fully_shard函数签名里压根没有它,于是照搬就 TypeError。

二、背景

FSDP1 用类包装的方式:FullyShardedDataParallel(model, ignored_params=[...]),被忽略的参数不参与分片、保持原样。这是"参数级"的控制。

FSDP2 改成了函数式、模块级fully_shard(module):它对"你调用它的那个 module"做分片,更像是"对哪些 submodule 调用fully_shard,哪些就被分片"。要"忽略某些参数",在 FSDP2 的范式里不是"传一个 ignore 列表",而是:

  • 只对基座模块调用fully_shard,不对 LoRA 参数的父模块调用;或
  • 把 LoRA 参数所在的模块排除在fully_shard的作用范围之外;或
  • fully_shardmesh/ 分片组控制粒度,但不存在ignored_params这种"黑名单"。

根因是"把 FSDP1/DDP 的 API 习惯套到了 FSDP2 上"。FSDP2 的设计哲学是"分片由你调用fully_shard的位置决定",而不是"传一个不被分片的参数清单"。所以ignored_params这个形参在fully_shard里根本不存在。

三、根因

抽象成代码(示意):

# FSDP1:类包装,接受 ignored_params class FullyShardedDataParallel: def __init__(self, module, ignored_params=None): ... # FSDP2:函数式,签名里没有 ignored_params def fully_shard(module, mesh=None, reshard_after_forward=True): ... # 不存在 ignored_params 形参 # 用户照搬 FSDP1 写法 -> TypeError fully_shard(model, ignored_params=lora_params)

根因链条:

  1. fully_shard(FSDP2) 是模块级函数,签名只有module / mesh / reshard_after_forward等;
  2. ignored_params是 FSDP1 类包装的概念,没被移植进fully_shard
  3. 用户按 FSDP1 经验传ignored_params,Python 直接 TypeError(未知关键字参数);
  4. 想"LoRA 不分片"的需求在 FSDP2 里要用"只对基座调用 fully_shard"实现,而非黑名单。

一句话:FSDP2 的fully_shard不含ignored_params(那是 FSDP1/DDP 概念),LoRA 不分片要靠"调用位置"而非"忽略列表"。

四、最小可运行复现

用纯 Python 模拟"函数签名不含 ignored_params 时传它会 TypeError":

# repro_ignored_params.py def fully_shard(module, mesh=None): return f"sharded:{module}" def call_old_style(module, lora_params): # 用户照搬 FSDP1 写法 try: fully_shard(module, ignored_params=lora_params) # 未知 kw except TypeError as e: return str(e) def main(): msg = call_old_style("base", ["lora_A", "lora_B"]) print("复现成功 ->", msg) assert "ignored_params" in msg if __name__ == "__main__": main()

运行输出:

复现成功 -> fully_shard() got an unexpected keyword argument 'ignored_params'

fully_shard不接受ignored_params,正是真实 bug 的抽象。

五、解决方案(第一层:最小直接修复)

最小且必须的一步:去掉ignored_params,改为只对基座模块调用fully_shard,把 LoRA 参数的父模块排除在外。FSDP2 里"不被 fully_shard 的模块就不分片":

# fix_layer1.py import torch from torch.distributed.fsdp import fully_shard def shard_base_only(model): # 只对基座(非 LoRA)子模块做分片;LoRA 参数保持完整 for name, module in model.named_modules(): if "lora" in name.lower(): continue # LoRA 模块不调用 fully_shard if _has_params(module): fully_shard(module) return model def _has_params(module): return any(True for _ in module.parameters(recurse=False))

要点:

  • 删掉ignored_params=调用,消除 TypeError;
  • 用"调用位置"控制分片范围:基座模块被fully_shard,LoRA 模块不被调用,自然保持每卡完整。

但纯字符串"lora" in name太脆,模块命名一变就漏,需第二层更稳的判定。

六、解决方案(第二层:结构性改进)

把"哪些参数该分片"做成显式的参数归类,依据参数的属性(如是否requires_grad、是否lora前缀的nn.Parameter)决定分片,而非依赖模块名字符串匹配:

# fix_layer2.py from dataclasses import dataclass, field from typing import List @dataclass class ShardPlan: sharded: List[str] = field(default_factory=list) kept_intact: List[str] = field(default_factory=list) def build_lora_plan(model) -> ShardPlan: plan = ShardPlan() for name, p in model.named_parameters(): if "lora" in name.lower(): plan.kept_intact.append(name) # LoRA 不分片 else: plan.sharded.append(name) # 基座分片 return plan def apply_plan(model, plan: ShardPlan): # 只用 plan.sharded 里参数所在的 module 做 fully_shard sharded_modules = { name.rsplit(".", 1)[0] for name in plan.sharded } for mod_name in sharded_modules: module = dict(model.named_modules())[mod_name] if any(True for _ in module.parameters(recurse=False)): fully_shard(module) # kept_intact 里的参数所在模块不被 fully_shard -> 保持完整

要点:

  • build_lora_plan按参数名属性归类(而不是靠模块名猜),更稳;
  • apply_plan只对基座参数所在 module 调fully_shard,LoRA 参数所在 module 自动排除;
  • LoRA 不分片的需求被显式建模,不再依赖ignored_params这种不存在的参数。

七、解决方案(第三层:断言 / CI 守护)

写 pytest 验证"fully_shard 不被传 ignored_params、LoRA 参数未被分片":

# test_fsdp2_lora.py import pytest class FakeModel: def named_parameters(self): return [("base.weight", 1), ("lora_A.weight", 2), ("lora_B.weight", 3)] def named_modules(self): return [("base", None), ("lora", None)] def call_fully_shard(**kwargs): if "ignored_params" in kwargs: raise TypeError("fully_shard() got an unexpected keyword argument 'ignored_params'") return "ok" def test_no_ignored_params_passed(): with pytest.raises(TypeError): call_fully_shard(ignored_params=["lora_A.weight"]) def test_lora_excluded_from_plan(): params = dict(FakeModel().named_parameters()) sharded = [n for n in params if "lora" not in n.lower()] assert "lora_A.weight" not in sharded assert "base.weight" in sharded def test_fully_shard_applied_only_base(): # base 模块会被调用,lora 模块不会 modules = dict(FakeModel().named_modules()) to_shard = [m for m in modules if "lora" not in m] assert "base" in to_shard and "lora" not in to_shard

CI 一旦有人又把ignored_params=加回 FSDP2 调用,test_no_ignored_params_passed立刻变红。

八、排查清单

FSDP2 + LoRA 报ignored_paramsTypeError 时:

  1. 确认是fully_shard() got an unexpected keyword argument 'ignored_params'
  2. 意识到ignored_params是 FSDP1/DDP 概念,FSDP2 的fully_shard没有;
  3. 去掉该参数,改为"只对基座模块调用 fully_shard";
  4. 按第五 / 六节用参数归类构建分片计划,避免字符串匹配模块名;
  5. 验证 LoRA 参数所在 module 未被fully_shard(保持每卡完整);
  6. 若需 LoRA 也分片,则正常对所有模块调用 fully_shard 即可;
  7. 把第七节的 pytest 接进 CI,守护"不传 ignored_params"。

九、小结

fsdp2 + lorafully_shardignored_params报 TypeError,根因是ignored_params是 FSDP1 / DDP 的"参数级忽略列表"概念,而 FSDP2 的fully_shard是模块级函数式 API,签名里根本没有这个形参。FSDP2 里"哪些参数分片"由"你对哪些 module 调用fully_shard"决定,而非传黑名单。

三层层级:

  • 第一层:删掉ignored_params,只对基座模块调用fully_shard,LoRA 模块自然保持完整;
  • 第二层:用ShardPlan按参数属性归类分片范围,避免脆弱的模块名字符串匹配;
  • 第三层:pytest 验证不传ignored_params、LoRA 参数未被分片,锁进 CI。

核心教训:换并行后端时,最易踩的坑就是"把旧 API 的参数习惯照搬过来"。FSDP2 的范式是"分片 = 你在哪调用 fully_shard",不要用 FSDP1 的"忽略列表"思维去套。

http://www.jsqmd.com/news/1311973/

相关文章:

  • 对比实测10款降AIGC软件:帮你锁定真正好用靠谱的一款
  • 流形上的优化:SO(3)与SE(3)的广义加减法在FAST-LIO中如何简化状态估计
  • 2026年可靠的水泥栏杆制作厂家怎么选?成都本地厂家推荐与行业观察 - 优质品牌商家
  • JavaScript字符串拼接性能优化:从加号到模板字符串的深度解析
  • 从信号空间到匹配滤波器:最佳接收原理与误码率性能分析
  • 2026八字排盘大运藏干完整查询怎么选:字段、规则与复盘指南
  • 酒吧带简餐,收银系统怎么做到餐饮+酒水统一管理?
  • 华为Mate60锁屏与激活锁解除:原理、风险与合法解决方案详解
  • 5分钟快速上手:B站视频下载工具DownKyi的完整使用指南
  • 从模型微调到智能体构建:Hermes Agent框架实战指南
  • Token经济与AI Agent如何重塑一人公司:从烧脑值到价值闭环
  • 深入解析SIMD指令集:从SSE到AVX的性能优化实战指南
  • 基于ESP32与3D打印的自动化线圈绕制器:从电机控制到开源硬件实践
  • 5分钟掌握Mifare Classic卡片管理:告别复杂命令行的Windows终极方案
  • 在广西采购LED显示屏的5年真实经历:从选型到安装的避坑复盘
  • 母线槽接头发热难题怎么解决?弹簧接头结构设计原理与优势
  • 2026 年至今,威海诚信的翻板闸门制造厂家综合实力解析,谁能靠这不起眼的水利构件,把河道水位管控玩出效率新高度?-湟禹水利机械 - 行业推荐官【官方】
  • 2026 年现阶段宣武专业的水下物品打捞的公司公司怎么联系,你可能不知道,藏在水下的那些遗失物,能被这群人用这些方法找回来?-游龙水下打捞 - 鉴选官
  • PyTorch模型迁移昇腾NPU实战:从算子兼容到性能调优全流程解析
  • 2026 年绥江靠谱的小区电动扫地车制造厂家哪家强,小区里的那台大家伙,竟悄悄解决了业主头疼许久的卫生难题? - 行业严选官
  • DeepMind AGI终极评分:AI评估革命与开发者应对策略
  • 扬州中央空调维修-欧米到家金牌师傅全城区30分钟火速上门覆盖广陵/邗江/江都/仪征等全域各区 专治不制冷/漏水/异响/跳闸
  • ESP32开发板定制外壳设计:从3D建模到打印的完整实践指南
  • FFmpeg合并TS文件转MP4:从原理到自动化脚本实战
  • 太原公考机构笔试师资口碑榜|2026机构选择红黑参考指南
  • UE4SS终极指南:15分钟掌握UE4/UE5游戏修改与脚本开发
  • 电竞数据分析实战:从API调用到可视化复盘BLG vs DK比赛
  • 2026 年当下,榆中知名的发光泡沫铝板订制厂家选型指南,装修踩过大坑的人,竟然用这玩意儿解决了所有烦恼 - 企业推荐管【认证】
  • Ubuntu/WSL2下CUDA与PyTorch GPU版完整安装指南与避坑实践
  • Jetson边缘计算盒子与VEYE工业相机兼容性实战:从驱动调试到AI应用集成