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

【Bug已解决】Adding support of DEFT, a decompositional efficient fine-tuning framework 解决方案

【Bug已解决】Adding support of DEFT, a decompositional efficient fine-tuning framework 解决方案

一、现象长什么样

你想在 PEFT 里加上DEFT(Decompositional 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,或微调低秩残差块)。相比纯 LoRA,DEFT 利用了对原权重的分解结构,往往能用更少参数拿到更好效果。

在 PEFT 里接入一个新方法,标准路径是:

  1. 继承BaseTunerLayer实现DEFTLayer,持有W_base的分解分量(冻结)+ 可训练残差;
  2. 继承BaseTuner实现DEFTPModel,负责inject/merge/unload
  3. 提供DEFTConfig(继承PeftConfig);
  4. peft/mapping.py注册,使get_peft_model(base, DEFTConfig)可用;
  5. 实现save/loadmerge_and_unload

下面把每步用可运行代码串起来。

三、根因(为什么接入容易出错)

根因 A:没区分“冻结的分解基”和“可训练残差”

DEFT 的W_base(或其 SVD 分量)必须requires_grad=False,只训残差。若误把基也设可训练,参数暴涨,失去“高效”意义,且 decomposition 会被优化破坏。

根因 B:merge逻辑缺失,推理带壳

LoRA 有merge_and_unloadB·A合并回W。DEFT 若不做对应合并,部署时仍需 adapter 壳,且前向组合若每次都重算分解会拖慢。

根因 C:state_dict键结构不合规

PEFT 的from_pretrained期望特定键前缀(如base_model.model.*.def_residues.*)。键不对就加载失败。

根因 D:没注册到 mapping,入口不可用

忘了在mapping.pyPEFT_TYPE_TO_CONFIG_MAPPING和 tuner 分发里登记,用户调get_peft_modelPeftType.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, r=4): super().__init__() # 原权重,冻结 self.base = nn.Linear(in_f, out_f, bias=False) 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, bias=False).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, bias=False) m.weight.data.copy_(weight) m.weight.requires_grad = False return m def demo(): torch.manual_seed(0) layer = DEFTLinear(16, 8, r=4).cuda() x = torch.randn(4, 16, device="cuda") 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, r=4, target_modules=None, scaling=1.0, **kw): super().__init__(**kw) self.peft_type = PeftType.DEFT self.r = r self.target_modules = target_modules or ["lin"] self.scaling = scaling

Layer 继承BaseTunerLayer,在forward里做base + B·A·scaling,并把base/分解分量设requires_grad=False。Model 继承BaseTuner,实现inject_adaptermerge_adapterunload

最关键的一步:在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(r=4, 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.B

6.3 合规 state_dict

def 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(r=4, 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, atol=1e-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 时查:

  1. 分解基冻结了吗base.weight.requires_grad=False,否则失去高效性。
  2. 只训残差吗A/B(或缩放)requires_grad=True
  3. merge 实现了吗merge_and_unload把残差合并回权重,部署不带壳。
  4. state_dict 键合规吗?符合 PEFT 前缀约定,from_pretrained才认。
  5. 注册到 mapping 了吗MODEL_TYPE_TO_PEFT_MODEL_MAPPING+ config 映射都加。
  6. adapter 输出 == merged 输出吗?用allclose断言一致性。
  7. 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”,四类代码缺一就加载或部署失败。

http://www.cnnetsun.cn/news/3604023.html

相关文章:

  • 大模型微调技术:从LoRA到QLoRA的实践指南
  • 【限时解密】金融级AI客服合规沙盒实操手册:GDPR+等保2.0双认证下的5层数据脱敏流水线
  • 【计算机毕业设计案例】基于 Django 的卡牌闲置流转与个性化推荐系统设计 潮玩卡牌展示交易与智能推送平台(程序+文档+讲解+定制)
  • 无人机产业链解析:核心技术、应用场景与市场趋势
  • 二维深度卷积网络在轴承故障诊断中的应用与优化
  • 国内开发者如何轻松调用GPT-5和Claude 4.5
  • AI 成本战的隐性成本与降本五层:从“成功率悖论“到“系统复杂度“(中)
  • 超级个体时代:多AI协同工作流实战指南
  • AI Agent协同系统:架构设计与效率提升实战
  • 基于SE-ResNet的航空发动机剩余寿命预测方法
  • 2026最新:哪几款抖音解析工具好用?这4款免费实用神器亲测好用
  • 一根网线玩转全家网络:TP-LINK TL-SG2008D交换机 + OpenWrt软路由VLAN详细配置指南
  • Furion.Pure 事件总线
  • 天津私房蛋糕培训适合人群介绍
  • 计算机毕业设计之基于SpringBoot的美发门店管理系统
  • 飞牛nas安装easynvr,解决监控卡不足问题2026年7月新
  • 前端资源优化实战:合并与压缩技术详解
  • SoapUI进阶:构建四层自动化测试体系与CI/CD集成实战
  • MSPM0 RTC寄存器深度解析:从基础配置到低功耗应用实战
  • BLIP-2多模态模型架构与训练优化详解
  • LoRI与LoRA技术对比:参数高效微调方案解析
  • 深入解析TI bq24765充电管理芯片:DPM、PCB布局与热设计实战
  • AI+虚拟仿真实训教学技术解析与应用
  • 数字孪生≠数智孪生!拆解两代孪生技术的数智化核心差距
  • 2026主流网盘限速破解?如何使用网盘直链下载助手跑满带宽
  • Docker Jenkins 最新版本(2026-07-23)
  • 从 curl 到工程封装:文本相似度 API 集成指南
  • 最小可运行示例:用手机号归属地查询 API 快速获取省份与运营商
  • NVLink带宽优化实战:从60%到90%+的C++多GPU性能提升策略
  • 大模型面试核心考点与RLHF技术解析