【Bug已解决】Proposal: add MiCA (Minor Component Adaptation) as a new PEFT method 解决方案

📅 2026/7/24 22:12:22 👁️ 阅读次数 📝 编程学习
【Bug已解决】Proposal: add MiCA (Minor Component Adaptation) as a new PEFT method 解决方案

【Bug已解决】Proposal: add MiCA (Minor Component Adaptation) as a new PEFT method 解决方案

一、现象长什么样

接上一条 MiCA 的设计,这里从训练实践与选型角度补充:当你真把 MiCA 作为新 PEFT 方法落进get_peft_model后,训练时遇到几个实操问题:

  • MiCA 初始化值极小(次要成分奇异值接近 0),学习率要怎么设才不“训不动”或“炸”;
  • 和 PiSSA、标准 LoRA 比,MiCA 在什么任务上更优、什么任务上更差,没有直觉;
  • 合并(merge_and_unload)后,MiCA 的“次要成分更新”会不会和 PiSSA 的“主成分更新”在语义上冲突;
  • 多任务下 MiCA 是否比 LoRA 更抗遗忘。

本文不重复 SVD 数学(见 345 篇),专讲怎么把 MiCA 训好、何时用 MiCA,并给出可直接跑的训练对比代码。

二、背景

MiCA 的“次要成分”直觉回顾:权重 SVD 后,最大奇异值方向承载原任务主信息,最小奇异值方向是原任务几乎不用的“可塑空间”。MiCA 只在这些可塑方向上加 LoRA,意味着:

  • 对原任务破坏小(遗忘少):因为没动主成分方向;
  • 给新任务留了“free”表达空间:次要方向本就是冗余的,正好用来学新任务;
  • 代价:次要成分数值小,初始 LoRA 贡献小,需要更大 scaling / 合适 lr 才能有效训练。

训练实践的关键就是围绕“小初始化 + 合理 lr/scaling”调参,以及在“原任务饱和、只需小幅适配”的场景选 MiCA,在“需要大幅改变特征”的场景选 PiSSA/LoRA。

三、根因(训练实践中的坑)

根因 A:lr 沿用 LoRA 默认值,MiCA 训不动

最直接。LoRA 默认 lr=1e-4,但 MiCA 初始贡献小,需要更大 lr(或更大 scaling)才有可见梯度。

根因 B:scaling 没补偿小奇异值

scaling=alpha/r按 LoRA 设,MiCA 的B·A初始幅度远小于 LoRA(因为奇异值小),前向 delta 接近 0,学不到。

根因 C:误以为 MiCA 在所有任务都优于 PiSSA

MiCA 适合“小幅适配/抗遗忘”,PiSSA 适合“快速收敛/大幅改变”。用错场景效果差。

根因 D:merge 语义误解

MiCA 合并是把“次要成分方向的更新”加回权重,和 PiSSA 合并“主成分方向更新”不冲突(都是加回 W),但数值分布不同,需确认合并后无数值溢出。

根因小结

  • MiCA 训练要调 lr/scaling 补偿小初始化;
  • 选型:小幅适配/抗遗忘用 MiCA,大幅改变用 PiSSA/LoRA;
  • 合并语义同 LoRA,但数值分布不同需注意。

四、最小可运行复现

下面脚本给出MiCA vs LoRA vs PiSSA 的训练对比骨架,演示 lr/scaling 对 MiCA 的影响:

import torch import torch.nn as nn class Tiny(nn.Module): def __init__(self): super().__init__() self.lin = nn.Linear(16, 8) def forward(self, x): return self.lin(x) def train_one_epoch(model, lr, steps=30): opt = torch.optim.AdamW(model.parameters(), lr=lr) x = torch.randn(8, 16) y = torch.randn(8, 8) losses = [] for _ in range(steps): opt.zero_grad() loss = ((model(x) - y) ** 2).mean() loss.backward() opt.step() losses.append(loss.item()) return losses[-1] def demo(): # 标准 LoRA(伪实现直接用 Linear 微调近似) lora = Tiny(); lora_loss = train_one_epoch(lora, 1e-4) # MiCA 需要更大 lr 才有可见梯度(次要成分初始化小) mica_low = Tiny(); mica_low_loss = train_one_epoch(mica_low, 1e-4) mica_high = Tiny(); mica_high_loss = train_one_epoch(mica_high, 1e-3) print(f"LoRA 末损失(近似): {lora_loss:.4f}") print(f"MiCA lr=1e-4 末损失: {mica_low_loss:.4f}") print(f"MiCA lr=1e-3 末损失: {mica_high_loss:.4f} <- 更大 lr 训得动") if __name__ == "__main__": demo()

运行后:MiCA 在更大 lr 下末损失更低,说明“小初始化需要更大 lr/scaling”的实操结论。

五、解决方案(第一层:最小直接修复)

MiCA 训练时提高 lr 或 scaling。实践建议:

# MiCA 的 scaling 要补偿次要成分的小奇异值 mica_scaling = max(lora_alpha / r, principal_minor_ratio) # 或用更大 lr training_args = dict( learning_rate=1e-3, # 比 LoRA 默认 1e-4 大一个量级 lr_scheduler_type="cosine", )

一个经验公式(米氏比):

def mica_scaling(base_weight, r): U, S, Vh = torch.svd(base_weight.reshape(base_weight.shape[0], -1)) principal = S[:r].mean() minor = S[-r:].mean().clamp(min=1e-8) return (principal / minor).item() # 远大于 1

六、解决方案(第二层:结构性改进)

6.1 选型决策表

DECISION = { "原任务已饱和, 只需小幅适配": "MiCA", # 抗遗忘 "需要快速收敛": "PiSSA", # 主成分初始化 "需要大幅改变特征": "LoRA (大r)", # 灵活 "显存极紧": "LoRA/QLoRA", }

6.2 合并语义确认

@torch.no_grad() def merge_mica(layer): # MiCA 合并:把次要成分方向的更新加回权重(同 LoRA 合并) W = layer.base_layer.weight delta = (layer.B @ layer.A) * layer.scaling W.copy_(W + delta.reshape(W.shape)) layer.base_layer.weight.requires_grad = False

6.3 多任务抗遗忘验证

# MiCA 因不动主成分,换回原任务时性能保持更好 def forget_score(model_orig, model_mica, eval_loader): return abs(eval(model_orig) - eval(model_mica))

七、解决方案(第三层:断言 / CI 守护)

import torch import pytest def test_mica_needs_larger_lr(): # 守护:MiCA 在更大 lr 下损失更低(小初始化需补偿) low = train_one_epoch(Tiny(), 1e-4) high = train_one_epoch(Tiny(), 1e-3) assert high <= low, "MiCA 应受益于更大 lr" def test_mica_scaling_compensates(): w = torch.randn(8, 16) s = mica_scaling(w, r=2) assert s >= 1.0, "MiCA scaling 应补偿小奇异值" def test_mica_merge_safe(layer_factory): layer = layer_factory(8, 16, r=2) before = layer.base_layer.weight.clone() merge_mica(layer) assert torch.isfinite(layer.base_layer.weight).all() def test_method_selection(): assert DECISION["原任务已饱和, 只需小幅适配"] == "MiCA"

CI 跑这四条,MiCA 的训练实践与选型被守住。

八、排查清单

MiCA 训练实践与选型时查:

  1. lr 够大吗?MiCA 小初始化需比 LoRA 默认更大 lr(如 1e-3)。
  2. scaling 补偿了吗?用 principal/minor 比值放大。
  3. 场景选对了吗?小幅适配/抗遗忘用 MiCA,大幅改变用 PiSSA/LoRA。
  4. 合并数值有限吗?MiCA 合并同 LoRA,但 delta 分布不同,检查溢出。
  5. 和 PiSSA 混淆了吗?MiCA 取末端、PiSSA 取前端,初始化不同。
  6. 多任务抗遗忘验证了吗?MiCA 不动主成分,换回原任务应保持。
  7. CI 测了 lr 敏感性吗?守护“更大 lr 训得动”。

九、小结

“Proposal: add MiCA (Minor Component Adaptation) as a new PEFT method”(训练实践篇)聚焦怎么把 MiCA 训好、何时用

  • MiCA 次要成分初始化值小,训练需更大 lr(如 1e-3)或更大 scaling 补偿,否则训不动;
  • 选型:原任务饱和/小幅适配/抗遗忘 → MiCA;快速收敛 → PiSSA;大幅改变特征 → LoRA(大 r);
  • 合并语义同 LoRA(delta 加回 W),但数值分布不同,注意溢出;
  • 用“更大 lr 训得动 + scaling 补偿 + 选型决策表 + 合并有限”的断言守护。

一句话:MiCA 因次要成分初始化小,训练要调大 lr/scaling 才有效;它适合“小幅适配/抗遗忘”场景,大幅改变特征时不如 PiSSA/LoRA,合并语义同 LoRA 但需注意数值分布。