【Bug已解决】Weight converter argument disagreement 解决方案一、现象长什么样在 PEFT 里想把一种格式的 LoRA 权重转换成另一种比如把(lora_A, lora_B)的分解形式合成一个diff_weight或者把 adapter 从一种 target 命名映射到另一种调用 weight converter 时直接炸TypeError: weight_converter() got an unexpected keyword argument weight或者TypeError: weight_converter() missing 1 required positional argument: feature_dim又或者程序不报错但转换出来的权重形状对不上合并进 base 模型后输出全错。根因几乎都是调用 converter 时传的参数和 converter 函数声明的签名不一致——也就是“argument disagreement”参数不同意/对不上。本文把 PEFT 里 weight converter 的常见场景、签名约定和修复讲清。二、背景PEFT 在不同地方用到“权重转换”LoraConfig(weight_converter...)某些版本允许指定一个 converter把加载的lora_A/lora_B在.from_pretrained时转成另一种内部表示例如为了兼容不同实现约定的缩放。合并merge_and_unload时的内部转换把B·A按alpha/r缩放后加到 base 权重。这里涉及scale参数。lora_weight_to_diffweight/diffweight_to_lora_weight在 diffusers / 某些工具里用来在(A,B)分解与(W0BA)整体之间互转。这些 converter 大多是普通函数签名固定。一旦你传了它不认识的 keyword或漏了必需的 positional就会TypeError。更糟的是“签名对但语义错”比如 converter 期望(weight, scaling)你传了(lora_A, lora_B)形状不对但不报错结果权重被错误缩放。三、根因根因 A传了 converter 不认识的 keyword 参数最直接。比如 converter 签名是def convert(weight, scaling)你却调用convert(weightw, alpha...)多出来的alpha触发unexpected keyword argument。根因 B漏了必需的 positional 参数反过来converter 需要feature_dim来 reshape你只传了weight报missing 1 required positional argument。根因 C缩放因子scaling传错LoRA 合并的缩放是scaling alpha / r。如果 converter 内部已经按alpha/r处理你又在外面乘了一次结果双重缩放权重数值错但不报错。这是“参数语义 disagreement”。根因 Dweight_converter与 config 其它字段冲突某些版本里指定weight_converter会与lora_alpha/r的默认合并逻辑冲突导致 converter 拿到的scaling是 None。这种属于版本/接口不匹配。根因小结converter 是固定签名的普通函数传参必须严格匹配多传 keyword、漏传 positional、缩放因子语义错都会出问题静默错误形状对但缩放错比显式 TypeError 更危险。四、最小可运行复现下面脚本演示一个 LoRA(A,B)→ 合并权重diff scaling · B·A的 converter以及两种典型“参数不同意”的报错import torch def lora_to_diffweight(lora_A, lora_B, scaling: float): 把 (A,B) 合成合并增量。签名固定weight_A, weight_B, scaling。 assert lora_A.dim() 2 and lora_B.dim() 2 return scaling * (lora_B lora_A) def demo(): torch.manual_seed(0) A torch.randn(4, 16) # r4, in16 B torch.randn(32, 4) # out32, r4 scaling 16 / 4 # alpha/r # 正确严格匹配签名 diff lora_to_diffweight(A, B, scaling) print(合并增量形状:, tuple(diff.shape)) # (32, 16) # 错误1多传不认识的 keyword try: lora_to_diffweight(A, B, scalingscaling, alpha16) except TypeError as e: print(参数不同意(1):, e) # 错误2漏传 scalingpositional 缺失 try: lora_to_diffweight(A, B) except TypeError as e: print(参数不同意(2):, e) # 静默错误双重缩放不报错但数值错 wrong (scaling * scaling) * (B A) print(正确缩放和:, diff.abs().sum().item(), 错误双重缩放和:, wrong.abs().sum().item()) if __name__ __main__: demo()运行输出清晰展示两种TypeError和“双重缩放”的静默数值偏差。五、解决方案第一层最小直接修复严格按 converter 签名传参并用inspect在运行时校验杜绝“参数不同意”import inspect import torch def safe_convert(converter, *args, **kwargs): sig inspect.signature(converter) try: sig.bind(*args, **kwargs) # 先绑定检查失败立刻暴露 except TypeError as e: raise TypeError(f调用 {converter.__name__} 时参数不匹配: {e}) return converter(*args, **kwargs) # 用法 diff safe_convert(lora_to_diffweight, A, B, scalingscaling)对于 PEFT 的LoraConfig(weight_converter...)确保你传的 converter 签名与 PEFT 版本要求一致不确定就先打印inspect.signature确认再决定怎么包一层适配import inspect from peft import LoraConfig # 假设 PEFT 期望 converter 接收 (weight, scaling) def my_converter(weight, scaling): return weight * scaling print(inspect.signature(my_converter)) # (weight, scaling) - 与预期一致再挂上 config LoraConfig(r8, lora_alpha16, weight_convertermy_converter)六、解决方案第二层结构性改进6.1 用适配器包装不兼容的 converter如果现有 converter 签名和 PEFT 要求不一致写一个薄适配器把 PEFT 给的参数名映射到 converter 要的def adapt_for_peft(old_converter): def wrapper(weight, scaling): # 旧 converter 只认 (w)忽略 scaling 或内部计算 return old_converter(weight) return wrapper6.2 把 scaling 计算收口到一处def lora_scaling(config) - float: return config.lora_alpha / config.r # 所有 converter 调用统一从这里拿 scaling避免双重缩放 scaling lora_scaling(config) diff lora_to_diffweight(A, B, scaling)6.3 转换后做形状/数值断言def assert_convert_ok(diff, base_shape): assert diff.shape base_shape, f转换后形状 {diff.shape} ! base {base_shape} assert torch.isfinite(diff).all(), 转换结果含 NaN/Inf七、解决方案第三层断言 / CI 守护加 pytest确保 converter 签名和数值都正确import inspect import pytest import torch def test_converter_signature_matches(some_peft_converter): params list(inspect.signature(some_peft_converter).parameters) assert weight in params, converter 必须接受 weight 参数 def test_no_double_scaling(): A torch.randn(4, 16) B torch.randn(32, 4) scaling 16 / 4 diff lora_to_diffweight(A, B, scaling) # 合并进 base 后形状正确、数值有限 base torch.randn(32, 16) merged base diff assert merged.shape (32, 16) assert torch.isfinite(merged).all() def test_unexpected_kwarg_rejected(): with pytest.raises(TypeError): lora_to_diffweight(A, B, scalingscaling, alpha16) # 多传应失败CI 跑这些converter 接口一旦改签名或有人双重缩放立刻报警。八、排查清单weight converter 报“参数不同意”时查converter 的签名是什么用inspect.signature看再对着调用处查。有没有多传不认识的 keywordunexpected keyword argument就是这原因。有没有漏传必需的 positionalmissing required positional argument对应。scaling 被乘了几次LoRA 合并只乘一次alpha/r别双重缩放。weight_converter与 config 是否冲突确认 PEFT 版本对该字段的约定。转换后形状对吗用assert diff.shape base.shape兜底。是显式报错还是静默错静默数值错更危险务必加有限性/形状断言。九、小结“Weight converter argument disagreement” 是 converter 调用时参数签名/语义对不上converter 是固定签名的普通函数多传 keyword、漏传 positional 都会TypeError更隐蔽的是“形状对但缩放错”如双重乘alpha/r不报错却毁权重用inspect.signaturesig.bind在运行时校验参数把“参数不同意”提前暴露把scaling lora_alpha / r收口到一处杜绝双重缩放转换后加形状/有限性断言并用 pytest 守护 converter 签名与数值正确性。一句话调用 weight converter 前先inspect.signature对齐参数scaling 只乘一次转换后断言形状与有限性参数不同意就会在 CI 里现形。
【Bug已解决】Weight converter argument disagreement 解决方案
【Bug已解决】Weight converter argument disagreement 解决方案一、现象长什么样在 PEFT 里想把一种格式的 LoRA 权重转换成另一种比如把(lora_A, lora_B)的分解形式合成一个diff_weight或者把 adapter 从一种 target 命名映射到另一种调用 weight converter 时直接炸TypeError: weight_converter() got an unexpected keyword argument weight或者TypeError: weight_converter() missing 1 required positional argument: feature_dim又或者程序不报错但转换出来的权重形状对不上合并进 base 模型后输出全错。根因几乎都是调用 converter 时传的参数和 converter 函数声明的签名不一致——也就是“argument disagreement”参数不同意/对不上。本文把 PEFT 里 weight converter 的常见场景、签名约定和修复讲清。二、背景PEFT 在不同地方用到“权重转换”LoraConfig(weight_converter...)某些版本允许指定一个 converter把加载的lora_A/lora_B在.from_pretrained时转成另一种内部表示例如为了兼容不同实现约定的缩放。合并merge_and_unload时的内部转换把B·A按alpha/r缩放后加到 base 权重。这里涉及scale参数。lora_weight_to_diffweight/diffweight_to_lora_weight在 diffusers / 某些工具里用来在(A,B)分解与(W0BA)整体之间互转。这些 converter 大多是普通函数签名固定。一旦你传了它不认识的 keyword或漏了必需的 positional就会TypeError。更糟的是“签名对但语义错”比如 converter 期望(weight, scaling)你传了(lora_A, lora_B)形状不对但不报错结果权重被错误缩放。三、根因根因 A传了 converter 不认识的 keyword 参数最直接。比如 converter 签名是def convert(weight, scaling)你却调用convert(weightw, alpha...)多出来的alpha触发unexpected keyword argument。根因 B漏了必需的 positional 参数反过来converter 需要feature_dim来 reshape你只传了weight报missing 1 required positional argument。根因 C缩放因子scaling传错LoRA 合并的缩放是scaling alpha / r。如果 converter 内部已经按alpha/r处理你又在外面乘了一次结果双重缩放权重数值错但不报错。这是“参数语义 disagreement”。根因 Dweight_converter与 config 其它字段冲突某些版本里指定weight_converter会与lora_alpha/r的默认合并逻辑冲突导致 converter 拿到的scaling是 None。这种属于版本/接口不匹配。根因小结converter 是固定签名的普通函数传参必须严格匹配多传 keyword、漏传 positional、缩放因子语义错都会出问题静默错误形状对但缩放错比显式 TypeError 更危险。四、最小可运行复现下面脚本演示一个 LoRA(A,B)→ 合并权重diff scaling · B·A的 converter以及两种典型“参数不同意”的报错import torch def lora_to_diffweight(lora_A, lora_B, scaling: float): 把 (A,B) 合成合并增量。签名固定weight_A, weight_B, scaling。 assert lora_A.dim() 2 and lora_B.dim() 2 return scaling * (lora_B lora_A) def demo(): torch.manual_seed(0) A torch.randn(4, 16) # r4, in16 B torch.randn(32, 4) # out32, r4 scaling 16 / 4 # alpha/r # 正确严格匹配签名 diff lora_to_diffweight(A, B, scaling) print(合并增量形状:, tuple(diff.shape)) # (32, 16) # 错误1多传不认识的 keyword try: lora_to_diffweight(A, B, scalingscaling, alpha16) except TypeError as e: print(参数不同意(1):, e) # 错误2漏传 scalingpositional 缺失 try: lora_to_diffweight(A, B) except TypeError as e: print(参数不同意(2):, e) # 静默错误双重缩放不报错但数值错 wrong (scaling * scaling) * (B A) print(正确缩放和:, diff.abs().sum().item(), 错误双重缩放和:, wrong.abs().sum().item()) if __name__ __main__: demo()运行输出清晰展示两种TypeError和“双重缩放”的静默数值偏差。五、解决方案第一层最小直接修复严格按 converter 签名传参并用inspect在运行时校验杜绝“参数不同意”import inspect import torch def safe_convert(converter, *args, **kwargs): sig inspect.signature(converter) try: sig.bind(*args, **kwargs) # 先绑定检查失败立刻暴露 except TypeError as e: raise TypeError(f调用 {converter.__name__} 时参数不匹配: {e}) return converter(*args, **kwargs) # 用法 diff safe_convert(lora_to_diffweight, A, B, scalingscaling)对于 PEFT 的LoraConfig(weight_converter...)确保你传的 converter 签名与 PEFT 版本要求一致不确定就先打印inspect.signature确认再决定怎么包一层适配import inspect from peft import LoraConfig # 假设 PEFT 期望 converter 接收 (weight, scaling) def my_converter(weight, scaling): return weight * scaling print(inspect.signature(my_converter)) # (weight, scaling) - 与预期一致再挂上 config LoraConfig(r8, lora_alpha16, weight_convertermy_converter)六、解决方案第二层结构性改进6.1 用适配器包装不兼容的 converter如果现有 converter 签名和 PEFT 要求不一致写一个薄适配器把 PEFT 给的参数名映射到 converter 要的def adapt_for_peft(old_converter): def wrapper(weight, scaling): # 旧 converter 只认 (w)忽略 scaling 或内部计算 return old_converter(weight) return wrapper6.2 把 scaling 计算收口到一处def lora_scaling(config) - float: return config.lora_alpha / config.r # 所有 converter 调用统一从这里拿 scaling避免双重缩放 scaling lora_scaling(config) diff lora_to_diffweight(A, B, scaling)6.3 转换后做形状/数值断言def assert_convert_ok(diff, base_shape): assert diff.shape base_shape, f转换后形状 {diff.shape} ! base {base_shape} assert torch.isfinite(diff).all(), 转换结果含 NaN/Inf七、解决方案第三层断言 / CI 守护加 pytest确保 converter 签名和数值都正确import inspect import pytest import torch def test_converter_signature_matches(some_peft_converter): params list(inspect.signature(some_peft_converter).parameters) assert weight in params, converter 必须接受 weight 参数 def test_no_double_scaling(): A torch.randn(4, 16) B torch.randn(32, 4) scaling 16 / 4 diff lora_to_diffweight(A, B, scaling) # 合并进 base 后形状正确、数值有限 base torch.randn(32, 16) merged base diff assert merged.shape (32, 16) assert torch.isfinite(merged).all() def test_unexpected_kwarg_rejected(): with pytest.raises(TypeError): lora_to_diffweight(A, B, scalingscaling, alpha16) # 多传应失败CI 跑这些converter 接口一旦改签名或有人双重缩放立刻报警。八、排查清单weight converter 报“参数不同意”时查converter 的签名是什么用inspect.signature看再对着调用处查。有没有多传不认识的 keywordunexpected keyword argument就是这原因。有没有漏传必需的 positionalmissing required positional argument对应。scaling 被乘了几次LoRA 合并只乘一次alpha/r别双重缩放。weight_converter与 config 是否冲突确认 PEFT 版本对该字段的约定。转换后形状对吗用assert diff.shape base.shape兜底。是显式报错还是静默错静默数值错更危险务必加有限性/形状断言。九、小结“Weight converter argument disagreement” 是 converter 调用时参数签名/语义对不上converter 是固定签名的普通函数多传 keyword、漏传 positional 都会TypeError更隐蔽的是“形状对但缩放错”如双重乘alpha/r不报错却毁权重用inspect.signaturesig.bind在运行时校验参数把“参数不同意”提前暴露把scaling lora_alpha / r收口到一处杜绝双重缩放转换后加形状/有限性断言并用 pytest 守护 converter 签名与数值正确性。一句话调用 weight converter 前先inspect.signature对齐参数scaling 只乘一次转换后断言形状与有限性参数不同意就会在 CI 里现形。