lora线性层必须替换原nn.linear而非仅forward插入,核心是冻结weight并显式计算weight + lora_b @ lora_a;手动实现需严格对齐peft的loraconfig参数、命名与初始化,否则加载和推理失效。

LoRA线性层旁路必须替换原 nn.Linear,不能只靠 forward 插入
LoRA 的核心是冻结主权重、用两个小矩阵 A 和 B 构成低秩增量:ΔW = B @ A。但若只在 forward 里临时加偏移,梯度不会回传到 A/B,训练完全失效。
正确做法是继承 nn.Module,把原 weight 冻结(requires_grad = False),并在 forward 中显式计算 self.weight + self.lora_B @ self.lora_A。注意:lora_A 通常初始化为随机正交,lora_B 初始化为零,否则训练初期扰动过大。
- 务必调用
self.weight.data(而非self.weight)来冻结——避免意外参与反向传播 -
lora_A形状应为(r, in_features),lora_B为(out_features, r),顺序反了会导致matmul失败或结果错位 - 不要在
__init__里写self.weight.requires_grad = False—— 这样会断开参数注册,model.parameters()拿不到它
手动 LoRA 层和 PEFT 的 LoraConfig 参数必须严格对齐
PEFT 的 get_peft_model 会按配置自动注入 LoRA 层,但如果你手动实现了自己的 LoRA 模块,又想混用 PEFT 的保存/加载逻辑(比如 model.save_pretrained),就必须让手动层的属性名、形状、初始化方式和 LoraConfig 完全一致。
关键对齐点:
-
r:秩,必须和LoraConfig(r=8)一致;否则peft_config.inference_mode切换时维度不匹配 -
lora_alpha:缩放系数,手动实现中需显式做(lora_B @ lora_A) * (lora_alpha / r),PEFT 默认启用该缩放 - 可训练参数名必须是
lora_A和lora_B—— PEFT 的get_peft_model_state_dict只认这两个前缀 - 若启用
target_modules=["q_proj", "v_proj"],你的手动层必须也只挂在这几个子模块上,不能多也不能少
混合使用手动 LoRA 和 PEFT 时,load_adapter 不会重载你自定义的模块
PEFT 的 load_adapter 只负责加载 peft_config 描述的结构,并将权重填入它自己创建的 LoRA 层。如果你手动替换了某层为自定义 LoRA 类,load_adapter 完全感知不到它,也不会尝试赋值 —— 结果就是加载后那层仍是随机初始化的 lora_A/lora_B。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
解决办法只有两个:
- 彻底放弃手动实现,统一用
get_peft_model注入 —— 推荐,省心且兼容所有 PEFT 工具链 - 若必须手动,就别用
load_adapter,改用torch.load+ 手动state_dict映射,例如:custom_layer.lora_A.data.copy_(sd["lora_A.weight"]) - 切记:手动层的
state_dict键名必须和保存时一致,PEFT 默认保存为"base_model.model.layers.0.self_attn.q_proj.lora_A.weight"这类路径
推理时 inference_mode=True 对手动 LoRA 层无自动生效机制
PEFT 的 set_adapter 和 disable_adapter 是通过在 LoRA 层内部开关一个 self.disable_adapters 标志实现的。但这是 PEFT 自己的 LoRA 层才有的逻辑。你手写的类如果没有这个字段和对应判断,model.eval() 或 peft_config.inference_mode=True 都不会让它跳过 LoRA 计算。
必须在手动层的 forward 中显式检查:
def forward(self, x):
result = F.linear(x, self.weight, self.bias)
if not self.disable_adapters:
result += (self.lora_B @ self.lora_A) @ x.T).T * (self.lora_alpha / self.r)
return result
而且得确保初始化时有 self.disable_adapters = False,并在外部调用 model.base_model.model.set_adapter("default") 前,手动同步这个标志 —— 否则推理结果永远带 LoRA 偏移。
最易忽略的一点:即使你只用 LoRA 微调,原始 weight 仍可能因量化、offload 等操作被转成 int8 或卸载,而 lora_A/lora_B 是 float16,类型不一致会静默报错或数值溢出。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










