【Bug已解决】Adding support of DEFT, a decompositional efficient fine-tuning framework 解决方案一、现象长什么样你想在 PEFT 里加上DEFTDecompositional Efficient Fine-Tuning——一种把权重拆成“共享基 低秩残差”来做高效微调的框架。但动手时发现 PEFT 没有现成的 DEFT tuner而直接塞进现有 LoRA 通道又不匹配它的“分解”语义。问题表现为不知道该继承BaseTuner还是BaseTunerLayer前向里“基 残差”的组合方式写错输出和 base 模型不一致保存/加载时state_dict键结构和 PEFT 约定对不上from_pretrained报错合并merge_and_unload逻辑没实现推理还要带 adapter 壳。本文给出把 DEFT 作为新 tuner 接入 PEFT 的完整落地方案。二、背景DEFT 的核心思想是把每个目标线性层的权重看成W W_base Δ但Δ不是简单B·A而是先对W_base做某种分解如 SVD 得到 U、S、V只微调其中一部分分量例如缩放 S或微调低秩残差块。相比纯 LoRADEFT 利用了对原权重的分解结构往往能用更少参数拿到更好效果。在 PEFT 里接入一个新方法标准路径是继承BaseTunerLayer实现DEFTLayer持有W_base的分解分量冻结 可训练残差继承BaseTuner实现DEFTPModel负责inject/merge/unload提供DEFTConfig继承PeftConfig在peft/mapping.py注册使get_peft_model(base, DEFTConfig)可用实现save/load与merge_and_unload。下面把每步用可运行代码串起来。三、根因为什么接入容易出错根因 A没区分“冻结的分解基”和“可训练残差”DEFT 的W_base或其 SVD 分量必须requires_gradFalse只训残差。若误把基也设可训练参数暴涨失去“高效”意义且 decomposition 会被优化破坏。根因 Bmerge逻辑缺失推理带壳LoRA 有merge_and_unload把B·A合并回W。DEFT 若不做对应合并部署时仍需 adapter 壳且前向组合若每次都重算分解会拖慢。根因 Cstate_dict键结构不合规PEFT 的from_pretrained期望特定键前缀如base_model.model.*.def_residues.*。键不对就加载失败。根因 D没注册到 mapping入口不可用忘了在mapping.py的PEFT_TYPE_TO_CONFIG_MAPPING和 tuner 分发里登记用户调get_peft_model时PeftType.DEFT不存在。根因小结DEFT 接入 新 Layer 新 Model 新 Config 注册 merge/save冻结分解基、只训残差是关键必须实现 merge 与合规 state_dict否则部署/加载失败。四、最小可运行复现下面给出一个自包含、可运行的 DEFT 风格层与合并逻辑不依赖 PEFT 内部便于先验证数学之后再映射到 PEFT 结构import torch import torch.nn as nn class DEFTLinear(nn.Module): def __init__(self, in_f, out_f, r4): super().__init__() # 原权重冻结 self.base nn.Linear(in_f, out_f, biasFalse) self.base.weight.requires_grad False # 可训练低秩残差DEFT 在分解基之上加 B·A self.A nn.Parameter(torch.zeros(r, in_f)) self.B nn.Parameter(torch.zeros(out_f, r)) self.scaling 1.0 def forward(self, x): base_out self.base(x) delta (x self.A.t()) self.B.t() * self.scaling return base_out delta def merge(self): # 把残差合并进 base返回纯 Linear便于部署 with torch.no_grad(): merged self.base.weight (self.B self.A) * self.scaling return nn.Linear(self.base.in_features, self.base.out_features, biasFalse).cuda().to(merged.dtype).requires_grad_(False).cuda().requires_grad_(False) if False else self._make_merged(merged) def _make_merged(self, weight): m nn.Linear(self.base.in_features, self.base.out_features, biasFalse) m.weight.data.copy_(weight) m.weight.requires_grad False return m def demo(): torch.manual_seed(0) layer DEFTLinear(16, 8, r4).cuda() x torch.randn(4, 16, devicecuda) out_adapter layer(x) merged layer.merge().to(cuda) out_merged merged(x) print(adapter 输出与 merged 输出最大差:, (out_adapter - out_merged).abs().max().item()) # 应接近 0说明 merge 正确 if __name__ __main__: demo()运行后“adapter 输出与 merged 输出最大差”接近 0证明前向组合与 merge 一致。五、解决方案第一层最小直接修复把 DEFT 接成 PEFT tuner 的最小骨架# peft/tuners/deft/__init__.py from peft import PeftConfig, PeftType from peft.utils import PeftTunerType class DEFTConfig(PeftConfig): def __init__(self, r4, target_modulesNone, scaling1.0, **kw): super().__init__(**kw) self.peft_type PeftType.DEFT self.r r self.target_modules target_modules or [lin] self.scaling scalingLayer 继承BaseTunerLayer在forward里做base B·A·scaling并把base/分解分量设requires_gradFalse。Model 继承BaseTuner实现inject_adapter、merge_adapter、unload。最关键的一步在peft/mapping.py注册from peft.tuners.deft import DEFTModel, DEFTConfig MODEL_TYPE_TO_PEFT_MODEL_MAPPING[PeftType.DEFT] DEFTModel PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.DEFT] DEFTConfig之后get_peft_model(base, DEFTConfig(r4, target_modules[lin]))即可用。六、解决方案第二层结构性改进6.1 分解基用 SVD 预计算残差只调缩放更接近 DEFT 原意def build_deft_base(weight: torch.Tensor, r: int): # 对原权重做 SVD取前 r 个分量作为可训练缩放其余冻结 U, S, V torch.svd(weight) base (U[:, :r] * S[:r]) V[:, :r].t() # 冻结的“主成分基” return base.detach().requires_grad_(False)6.2 merge 时避免每次重算torch.no_grad() def merge_and_unload(self): for _, module in self.named_modules(): if isinstance(module, DEFTLayer): module.base.weight.copy_(module.base.weight module.B module.A * module.scaling) module.base.weight.requires_grad False # 释放残差 del module.A, module.B6.3 合规 state_dictdef get_peft_model_state_dict(self): return {k: v for k, v in self.state_dict().items() if def_residues in k or A in k or B in k}确保from_pretrained能用标准键加载。七、解决方案第三层断言 / CI 守护加测试保证“adapter 行为 merged 行为”且注册可用import torch import pytest from peft import get_peft_model, DEFTConfig def test_get_peft_model_accepts_deft(base): model get_peft_model(base, DEFTConfig(r4, target_modules[lin])) out model(torch.randn(2, 10)) assert out.shape[0] 2 def test_adapter_equals_merged(layer): x torch.randn(4, 16) a layer(x) m layer.merge()(x) assert torch.allclose(a, m, atol1e-5), merge 后输出必须与原 adapter 一致 def test_base_frozen(layer): assert not layer.base.weight.requires_grad, 分解基必须冻结 assert layer.A.requires_grad and layer.B.requires_grad, 残差必须可训练CI 跑这三条DEFT tuner 接入的正确性被守住。八、排查清单接入 DEFT 到 PEFT 时查分解基冻结了吗base.weight.requires_gradFalse否则失去高效性。只训残差吗A/B或缩放requires_gradTrue。merge 实现了吗merge_and_unload把残差合并回权重部署不带壳。state_dict 键合规吗符合 PEFT 前缀约定from_pretrained才认。注册到 mapping 了吗MODEL_TYPE_TO_PEFT_MODEL_MAPPING config 映射都加。adapter 输出 merged 输出吗用allclose断言一致性。Config 继承 PeftConfig 了吗peft_type字段正确设置。九、小结“Adding support of DEFT, a decompositional efficient fine-tuning framework” 是把一种基于权重分解的高效微调方法接入 PEFT 的工程任务DEFT 冻结的分解基如 SVD 主成分 可训练低秩残差只训残差保高效接入路径新DEFTLayer(BaseTunerLayer) 新DEFTModel(BaseTuner) DEFTConfig(PeftConfig) 在mapping.py注册必须实现merge_and_unload合并残差回权重部署不带壳和合规state_dict键前缀对from_pretrained可用用“adapter 输出 merged 输出”“基冻结/残差可训”的断言守护接入正确性。一句话DEFT 接入 PEFT 的核心是“冻结分解基 只训残差 实现 merge 注册 mapping”四类代码缺一就加载或部署失败。