Chapter 11

调试与踩坑合集

📌 commit a106ff4c7061 从 trainable=0 到 save/load 不匹配的高频问题

PEFT 大多数错误集中在三个环节:选 target_modules、save/load、量化集成。 本章把这些高频问题列成清单,按"现象 → 原因 → 定位 → 处理"四列写。

11.1定位类问题

现象原因定位处理
print_trainable_parameters() = 0% target_modules 没匹配上任何层 遍历 named_modules,看真实层名 改 target_modules 或用 "all-linear"
ValueError: Target modules ... not found 模型层名跟你写的不一样 print 一遍 nn.Linear 的 name 用真实 suffix 或 regex
同一脚本第二次跑 trainable% 改变 第一次 get_peft_model 把 base 改了,第二次基于已改的 base 查 model.peft_config 是否多了一项 每次重新 from_pretrained 加载 base

11.2训练类问题

现象处理
loss 一直不降 (1) lr 太低(LoRA 通常 1e-4 ~ 2e-4,比全参高 10×)
(2) target_modules 只覆盖 q/v 不够,加 k/o/MLP
(3) r 太小,先翻倍试试
(4) lora_alpha 没相应放大
loss 第一步就特别大 用了 init_lora_weights=False,起点 ΔW ≠ 0;改成 True
训到后期 loss 飙升 lr 过大;上 cosine 衰减 + warmup_steps=100
loss 下降但 eval 效果变差 过拟合,加 lora_dropout=0.1 或减 epoch
QLoRA 训练 NaN 没调 prepare_model_for_kbit_training;或 compute_dtype 用了 fp16,换 bf16
显存比预期高 开了 use_dora=True(多 30% 显存);或 gradient_checkpointing 没开

11.3save/load 类问题

现象处理
save 后只有 adapter_config.json 没有 .safetensors model.save_pretrained(),不要用 torch.save
load 后 print_trainable_parameters() = 0 load 出来默认 inference_mode=True;要训练再加 is_trainable=True
RuntimeError: weight is on the meta device 是 deepspeed/accelerate 加载顺序问题;先把 base 完整加载到 GPU 再 load_adapter
load 后推理结果跟训完一致但接 vLLM 不一致 vLLM 需要的是 merge 后或 LoRA 单文件格式;用 merge_and_unload 或保存 lora_only
save 时报"adapter_name not in model" 检查 active_adapter;或显式 selected_adapters=["xxx"]
load 不同 r 的 adapter 报 size mismatch 同一目录只能放一个 adapter,不同 r 用不同子目录

11.4合并 / 卸载类问题

现象处理
merge_and_unload 后效果变差 (1) base 是量化模型不能直接 merge
(2) DoRA 的合并不等价(数值近似);用 safe_merge=True
merge 后 forward 比未 merge 慢 大概率没真正合并;查 m.merged 是否为 True
4bit base merge 报 NotImplementedError dequantize_module_weight 升回 bf16 base,再 merge
merge 后再切回 4bit 部署 用 AutoAWQ / AutoGPTQ 重新量化合并后的模型

11.5与 transformers Trainer 配合的问题

现象处理
Trainer 自动保存的 ckpt 加载失败 Trainer save 时会调 model.save_pretrained,自动只保 adapter;load 时用 PeftModel.from_pretrained
resume_from_checkpoint 报错 升级 transformers ≥ 4.41;旧版本对 PEFT resume 兼容不完整
开 deepspeed ZeRO-3 时 LoRA 训不起来 ZeRO-3 切了 LoRA 矩阵,PEFT 现在已支持;老版本需要降到 ZeRO-2
多卡(DDP)训完 save 后 base 也被保了 用 rank0 上的 model 调 save_pretrained,不要让所有 rank 都 save

11.6调试小工具

看 LoRA 真的接上了吗

from peft.tuners.lora import LoraLayer
for name, m in model.named_modules():
    if isinstance(m, LoraLayer):
        print(name, "→ active:", m.active_adapters,
              "merged:", m.merged_adapters,
              "r:", m.r if hasattr(m, "r") else "n/a")

看 LoRA 矩阵的范数(训练健康度)

for name, m in model.named_modules():
    if isinstance(m, LoraLayer):
        for adapter in m.active_adapters:
            A = m.lora_A[adapter].weight
            B = m.lora_B[adapter].weight
            print(f"{name}/{adapter}  ||A||={A.norm():.4f}  ||B||={B.norm():.4f}")

训练初期 B 应该接近 0(零初始化),训练后 B 应该有非平凡 magnitude(如 > 0.01)。如果训完 B 还接近 0,说明梯度没流过来。

用 hook 看 LoRA 输出的贡献

activations = []
def hook(module, x, y):
    base_out = module.base_layer(x[0])
    delta = y - base_out
    activations.append((module, delta.abs().mean().item()))

for name, m in model.named_modules():
    if isinstance(m, LoraLayer):
        m.register_forward_hook(hook)

# 跑一次推理,看每层 LoRA 贡献多少
model(input_ids=...)
for m, d in activations[:5]:
    print(m, d)

11.7常用环境变量

变量作用
PEFT_DEBUG=1 开 PEFT 内部 debug 日志(部分版本有)
TOKENIZERS_PARALLELISM=false 消掉常见的 tokenizer fork warning
TRANSFORMERS_OFFLINE=1 禁止访问 HF Hub(断网时必备)
BNB_FORCE_NO_DOUBLE_QUANT=1 排查 QLoRA 时关掉 double quant

11.8定位思路(通用)

  1. 先在 0.5B 小模型上跑通(Qwen2.5-0.5B 等),再换大模型;
  2. 先纯 LoRA 跑通,再加 QLoRA / DoRA / 多 adapter;
  3. 每加一个开关跑一次,避免一次开 5 个开关定位不到;
  4. 用 print_trainable_parameters() 当 sanity check,没匹配就别往下走;
  5. 断网时TRANSFORMERS_OFFLINE=1 避免无谓重试。

11.9DoRA + ZeRO-3 / FSDP 的已知坑

DoRA 在分布式训练时有几个隐形陷阱。tuners/lora/dora.py:94get_lora_weight 注释里写得明白:"use eye matrix to avoid FSDP issues",这是因为:

现象原因修复
FSDP + DoRA 报 DTensor 相关错误 DoRA forward 直接乘 LoRA 矩阵会触发 FSDP 切片冲突 升 PEFT >= 0.13;FSDP 用 use_orig_params=True
ZeRO-3 + DoRA 训练慢 50%+ DoRA 每 step 要 all-gather 完整权重算 norm 评估是否真需要 DoRA;不需要切回普通 LoRA
DoRA + 4bit quant + ZeRO-3 OOM 三者叠加显存峰值高(dequant 时是 fp16) 用 ZeRO-2 替代 ZeRO-3;或关 DoRA
DoRA + bnb 4bit 训完 merge 后输出乱码 magnitude vector 没正确 dequant 升 PEFT;显式 cast_mixed_precision_params_to_fp32=True

11.10PEFT + FSDP 的 use_orig_params

FSDP 1 默认 use_orig_params=False——FSDP 把多个参数展平打包成一个大张量,分块切到各 rank。这跟 PEFT 的"trainable / frozen 混合"完全不兼容(FSDP 看到一堆 frozen 参数夹着少量 trainable)。两种修复:

flag含义什么时候用
use_orig_params=True FSDP 保留原参数对象的视图,不强行展平PEFT 必开(自 PEFT 0.7 起强制要求)
FSDP2(PyTorch 2.4+) 新架构基于 DTensor,原生支持混合 trainable新项目首选

accelerate config 里写 fsdp_use_orig_params: true(FSDP v1)或 fsdp_version: 2 即可。Trainable Tokens 这种特殊 tuner(tuners/trainable_tokens/)也明确要求 use_orig_params。

11.11量化 + bias 的互斥

有几条 PEFT 不会显式报错但行为反常的"已知不兼容":

不兼容组合原因
GPTQ / AWQ / AQLM / EETQ 的 LoRA 不能 merge_and_unload packed int4 无法回 fp16;ch06.9 详述
LoraConfig(bias="all") + 量化 base 量化模型 bias 通常缺失或被合并
use_dora=True + Prompt Tuning 两个完全不同抽象,不能混用
AdaLoRA + 量化 base SVD 形式 LoRA 与量化矩阵不兼容
PiSSA init + 量化 base PiSSA 需要 fp32 base 做 SVD
多 adapter 中混 use_dora + 不 use_dora variant 分歧,merge 路径无法统一

11.12这章你需要带走的