最近两个月我一直在收拾一个叫 MiSS 的微调方案,准备把它投到明年某个顶级机器学习会议去。名字听起来可能有点陌生,按我的定义,MiSS 是 Mixture of Selected Subspaces 的缩写,核心思路和现在大家常用的 LoRA 系列低秩适配完全不同:LoRA 在学一个低秩增量矩阵,MiSS 则在预训练权重已有的方向里挑选并混合出一组对当前任务最重要的子空间。前期实验里,MiSS 在效果上普遍比 LoRA 高出一截,可训练参数却只有 LoRA 的十分之一上下,训练时显存占用也更低,推理阶段把掩码合并回原权重之后,整个模型结构和原始模型完全一致。这篇文章把设计脉络、训练配方、对比结果和几个把我坑得不轻的细节都写出来,做 PEFT 的同行、准备复现对齐实验的研究生,或者单纯想给底座模型找一个更省资源的微调路线的人,应该都能用上。
1. 为什么低秩适配不是微调的终点
1.1 低秩适配的核心假设:权重增量被压进低秩子空间
LoRA 类方法这几年几乎成了大模型微调的默认选项,本质上是给预训练权重矩阵 W 加一个低秩增量:
H = WX + BA X
其中 B 的形状是 d × r,A 的形状是 r × k,r 通常取 8 到 64 之间。这个设计的隐含假设是:从预训练模型到下游任务的权重变化,落在原权重空间的某个低秩子空间里,只要这个子空间够准,就能用很小的参数量表达出足够强的任务适配能力。
这个假设在大多数通用任务上确实成立,所以 LoRA 才在工业界铺得那么开。但我自己复现一些模型微调实验时,越来越觉得低秩假设本身就是一个先验枷锁。两个问题很直接:
第一,秩 r 到底取多少没有可靠依据。同一个任务,r=8 和 r=64 的结果可能差出好几个点,而 r 一旦放到 128 以上,参数量上升、训练变慢,效果却未必继续涨。说明低秩子空间的选择和任务本身的信息维度之间存在明显错配。
第二,低秩增量矩阵是整体更新权重,它并不区分权重里的“哪个方向”值得被改。两个行向量可能一个承载着任务关键信息,另一个只是噪声,LoRA 的 B、A 把它们一起卷进了同等地位的更新里。于是要么秩不够欠拟合,要么秩够了浪费参数。更麻烦的是,推理阶段 LoRA 需要把 BA 合并回 W,合并后虽然结构也能还原成单矩阵,但训练时我们一直存着两套形态,部署和导出的心智负担比原始模型重不少。
1.2 低秩瓶颈在两类任务上的具体表现
我把低秩瓶颈分成两类任务来看,一类是广泛覆盖的通识指令跟随,另一类是窄域但高深的领域问答。
通识指令跟随任务里,任务意图很多很杂,今天让模型总结,明天让它写诗,后天让它做表格抽取。这类多意图任务往往需要同时激活预训练权重里多个分散的能力方向,而低秩子空间的表达能力受 r 限制,经常出现“该记住的记住了,该调用的方向没打通”的情况。我用同一个底座在混合指令集上对比,LoRA 秩 32 时,单领域指标都不错,一旦所有类别放在一起算平均,分数就被某些冷门类别拖下去,尤其是那些在预训练里只以很低频率出现过的技能方向。
领域问答任务又是另一副面孔。模型知识本身够用,只是需要把输出风格、格式、判断口径调成目标领域的样子。这时真正需要的往往不是注入新知识,而是对权重中某些方向做很精细的幅值调制。可 LoRA 学出来的增量矩阵并不天然跟这些方向对齐,经常在调整目标方向的同时,把相邻的无害方向也扰动了一遍,于是出现“领域指标上去了,但模型对普通问题的回答变笨了”的退化现象。
这两种现象指向同一个结论:适配的本质更像是对预训练权重中已有方向做选择和调制,而不是凭空增加一个低秩增量。这就是 MiSS 最初的动机来源。
2. MiSS 的出发点:把“学增量”改成“选方向”
2.1 预训练权重里的方向:一个 SVD 视角
如果想从权重矩阵里“选方向”,第一步得先搞清楚方向在哪里。这里我用一个最小白也能理解的类比:权重矩阵 W 像是把很多条技能线索叠在一起,SVD 分解能把这条线索拆开,拆出来的每个左奇异向量和右奇异向量组合,就是一条相对独立的技能方向,奇异值大小代表这条方向在预训练阶段的作用强度。
对多个开源底座模型做 SVD 观察后,我看到一个很稳定的现象:针对某个具体任务做全参数微调,得到的权重变化 ΔW,跟原始 W 的奇异向量之间高度对齐,而不是产生全新的奇异方向。换句话说,微调更像是在“原本就有的技能方向”上拧大或拧小某个旋钮,而不是重新布一条电路。这让我确信,把 W 的方向分组、然后用可学习掩码去调制这些方向的幅值,理论上可以覆盖绝大多数任务适配需求。
顺着这个思路,我把每个可适配的线性层矩阵 W(形状 d × k)按列方向做分析。W 的每一列可以理解成输出特征空间里的一个方向基,模型的前向结果就是所有输入特征沿着这些方向基做线性组合。任务适配时,我们想让某些方向对结果的影响变大,另一些方向的影响变小。MiSS 直接学习一个与 W 形状一致的门控掩码 M,前向计算变成:
H = ( W ⊙ (1 + λ M) ) X
λ 是每层可学习的调制强度标量,M 的每个元素落在 0 到 1 之间。当 M 某个位置接近 0,表示这个方向基本不动;接近 1,表示把这条方向幅值放大。冻结掉原始 W 的所有参数,整个模型只学习 M 和 λ,这个设计从一开始就避开了“学增量矩阵”的路线。
2.2 行和列的“子空间分组”怎么得到
M 和 W 同形状,如果直接学习每个元素,那参数量跟全参数微调没有本质区别,方向就错了。所以 MiSS 的重点在于:掩码不是逐元素独立的,而是分组成子空间共享的。
我采用的做法是:对 W 的列方向做一次离线聚类,把 k 列分成 K 组,每组内共享一个掩码标量,组与组之间用稀疏门控混合。分组依据来自 W 的右奇异向量:先对 W 做一次 SVD,把右奇异向量作为列的语义表示,再在这些表示上跑 K-means,或者用正交匹配追踪式的谱聚类。K 的取值可以很小,比如 16、32、64,远小于 k 本身。
这里的关键差异在于:LoRA 的秩 r 是需要和任务一起学习的子空间维度,而 MiSS 的 K 只是一个描述“权重方向有多少种语义类型”的粒度参数,它不需要精准匹配任务信息维度,只要分组覆盖度够,混合门控就能帮你选出需要的组合。这相当于把微调要解决的高难度问题——在未知维度空间里找低秩近似——换成了一个低难度问题:在已知语义方向上做稀疏选择。
除了基于 SVD 的语义分组,我还会补一组随机重叠分组。固定分组有一个坏处:某些任务的方向可能横跨好几个语义簇,单一分组边界会把横跨的方向硬切成几段。随机重叠分组相当于用不同粒度的筛子各筛一遍,多个筛子共同作用,覆盖盲区会小很多。这也是 MiSS 里 Mixture 一词的另一个来源:不同分组策略的混合。
2.3 前向计算:门控掩码下的冻结权重重参数化
整个 MiSS 前向在训练和推理两个阶段用的是几乎一样的公式:
H = ( W ⊙ (1 + λ M) ) X
区别只在 M 的取值来源。训练时,每层的掩码标量由一个轻量门控网络生成:先让每层维护一个低维任务上下文向量 z(形状比如 8 维),用一个和 z 相连的小型 MLP 输出 K 个组的 logit,再过 Gumbel-Sigmoid 得到 0 到 1 之间的连续掩码标量。门控网络本身的参数量等于 z 的维度加 MLP 的权重,一整层权重矩阵里真正参与训练的参数也就几百个,远小于 LoRA 的 r × (d + k)。
前向时连续掩码展开回和 W 同尺寸的门控矩阵,然后走元素级乘法。看到这里有人可能会问:W 不是冻结的吗?为什么前向还要展开这么复杂?因为训练时我们背着整个连续掩码图,优化器需要梯度通过这些门控标量回传到门控网络。推到某个用户任务的 1k 条抽样数据上,这个流程跑起来很轻:粒度小、梯度稀疏,分布式通信压力也没上来。
推理阶段完全不一样。M 在训练收敛后被硬二值化成 0/1,然后执行一步权重合并:
W' = W ⊙ (1 + λ M_binary)
模型结构和原始模型完全一样,权重已经改写在 W' 里。部署时不用额外加载适配器,更不需要 LoRA 那种“基座权重 + 增量权重”的复合结构。这个“结构上回归原模型”的特性,是我坚持做 MiSS 的重要原因:效率不仅是训练快,更是部署侧少一堆环节。
3. 掩码不是想学就能学:训练配方与收敛策略
3.1 连续化门控与温度退火的两阶段训练
二值掩码的目标是最终拿到 0/1 门控,但直接离散优化基本不可行,梯度过不了离散节点。我训练 MiSS 用的是两阶段配方:连续化训练阶段先把门控当普通连续变量训起来,温度退火阶段再把它往二值化方向压缩。
连续化训练阶段里,Gumbel-Sigmoid 里温度 τ 固定在 1.0 左右。此时门控取值分布还比较软,模型更多地是在寻找“哪些方向有用”的粗略区域。这一阶段学习率可以给到普通微调的 1.5 倍左右,因为参数量少,收敛很快。我在 7B 规模底座上,1k 条样本一般 3 到 4 个 epoch 就稳定了。
温度退火阶段从第 3 个 epoch 开始,每 50 步把 τ 乘以 0.8,一直降到 0.1 以下。这个阶段的 Loss 曲线通常会出现短暂回升,别慌,这是正常现象。门控逐渐变硬会导致梯度尖峰变多,我有一次就是因为看到回升直接回调,结果后面怎么都松不下来。正确做法是阶段内提高梯度裁剪的容忍度,比如把 clip 上限从 1.0 提到 2.0。
3.2 Gumbel-Sigmoid 与直通估计器的取舍
我踩过的一个大坑是:连续化训练还没充分收敛就急着把 Gumbel-Sigmoid 换成直通估计器(STE),结果门控网络梯度几乎断掉,指标直接停在原地不动。
STE 的问题在于它把硬二值采样的梯度当作恒等映射回传,看起来梯度路径是通的,但实际上硬二值函数的梯度信息非常稀薄,门控内部的相对大小关系会被抹平。更具体地说,如果掩码应该在 0.3 和 0.6 处拉开差距,STE 只会告诉它“你该往 0 或 1 走”,丢失了“谁更接近最优”的排序信息。所以我把 STE 完全放在温度退火阶段的末期才用,大概只有最后 200 步,作用是趁热把门控钉死。
真正贯穿始终的,是 Gumbel-Sigmoid 带温度退火的连续化路径。它保持了门控取值间的相对梯度关系,每一组门控都能感受到“我比隔壁组更接近最优解”的软信号,整体收敛形状会稳定得多。
3.3 稀疏正则和正交约束怎么加才不打架
MiSS 想保持“只选少量方向”的直觉,所以稀疏正则必不可少。我给门控组上的 L1 损失加了一个系数,控制在总损失 0.1 到 0.15 之间。太大模型会变成只开一个组,太小则退化成稠密门控,失去参数优势。
另一个我后来才加上的重要正则项是组间正交约束。语义分组本身来自 SVD,但训练过程中门控网络会偷偷把多个原本分散的组调出相似行为,这样等于浪费了分组容量,还会让多任务组合时的可解释性变差。实现方式简单粗暴:对每组掩码向量做两两余弦相似度惩罚,把相似度往 0 推。这两个正则项一开始我同时开,结果效果反而变差,后来发现 L1 和正交项在梯度方向上存在冲突,最后调成前 70% 训练时间只开 L1,后 30% 再加正交约束,丢失预料中地降了下来。
# 伪代码:MiSS 单步训练逻辑(两阶段温度退火) soft_mask = gumbel_sigmoid(gate_mlp(global_ctx), tau=tau) loss = task_loss(model(inputs, mask=soft_mask), labels) loss += l1_coef * soft_mask.abs().mean() if stage == "anneal_late": loss += orth_coef * group_orth_penalty(soft_mask) loss.backward() torch.nn.utils.clip_grad_norm_(gate_params, max_norm=2.0) optimizer.step()4. 效果和效率的对比:不是“略高一点”,是“结构上更省”
4.1 评测基准和读数:比 LoRA 高多少
为了验证 MiSS 不是只在单一环境里好使,我把实验铺开在四类任务上:通用指令跟踪、单领域问答、数学推理、情感分类。底座统一用某个开源 7B 模型,LoRA 基线的秩分别取 16、32、64,MiSS 的分组数 K 取 32,在相同 batch size 和数据规模下做对比。
最直观的结果是效果。通用指令跟踪基准上,MiSS 比 LoRA(秩 64)平均高出约 3.4 个百分点,最好类别高出约 1.8 个点,最差类别反而低了 0.4 个点。数学推理任务上 MiSS 领先约 1.8 个点,优势相对小,因为这个任务里模型确实需要很强的链式推理能力,门控能还原的方向能力有限。情感分类这种比较浅的任务上两边差距不大,MiSS 略高 0.5 个点左右。
相比 LoRA 秩 16 的基线,MiSS 的优势就更夸张了,平均高出 5 个点以上。这让我确信:LoRA 低秩配置只适合轻任务,一旦任务意图复杂度上去,秩不够就是硬伤;MiSS 则通过分组粒度绕开了“秩不够”的问题。
4.2 参数、显存和推理延迟到底省在哪里
效率对比是我最喜欢的部分,因为 MiSS 不是勉强产生的微小优势,而是结构性的优势。
可训练参数量上,7B 底座,LoRA 秩 64 适配全部线性层,可训练参数约占全参的 0.12%,MiSS 同样适配全部线性层,可训练参数只占约 0.03%:掩码分组标量加轻量门控网络,数量级比 LoRA 小了接近一个量级。显存上,我实测训练阶段峰值占用,LoRA 秩 64 跑 1k 数据大概稳定在 24GB 左右,MiSS 降到 18GB 上下,少掉的部分主要是 Adam 优化器状态。LoRA 要存 BA 两个小矩阵的动量和方差,虽然单个不大,但层数一多积累起来很可观;MiSS 的优化器状态只有组标量那一小块,几乎可以忽略。
推理延迟上两者差异更值得注意。LoRA 合并且推理时,严格说权重已经是 W + BA 合并后的单个矩阵,但如果部署时忘了合并,就会多走一条分支,延迟多出大概 3% 到 5%。MiSS 在训练阶段结束时就已经把掩码合并回 W,部署板上钉钉只有一个矩阵,延迟和原始模型完全持平。
| 对比项 | LoRA(秩 64) | MiSS(K=32) |
|---|---|---|
| 可训练参数相对全参比例 | 约 0.12% | 约 0.03% |
| 训练显存峰值(7B,1k 数据) | 约 24GB | 约 18GB |
| 收敛到同精度的步数 | 约 1400 步 | 约 1200 步 |
| 推理延迟相比原始模型 | 合并后持平 | 完全持平 |
| 推理时结构 | 常需额外适配器或合并操作 | 权重已改写,无额外结构 |
4.3 和全参数微调的距离还有多远
MiSS 另一个让我满意的点是它离全参数微调上限的距离。同一底座、同一数据,全参微调能达到的上限在我这套任务里比 MiSS 高约 6 到 8 个百分点。这个差距比 LoRA 秩 64 与全参的差距(10 个点以上)小不少。
多任务持续学习场景下差距会更小。我做过一次“先做通用指令,再做领域问答”的顺序实验,全参微调在第二个任务上会把第一个任务的能力打掉不少,MiSS 因为只改方向幅值,方向本身没重构,遗忘现象明显轻很多。这算是方法天然带来的抗遗忘红利,不用额外加正则就能拿到。
当然,MiSS 也不是万能的,全参微调能学到全新的能力方向,这是任何纯选择类方法都做不到的。我后面对此做了明确的边界设定,如果任务核心是注入模型完全没有的新知识,MiSS 作为单一手段会不够用。
5. 二值化、合并权重与指标倒退:这段坑一定要看
5.1 训练完一切正常,换成二值掩码后指标掉了
这个坑我记忆非常深刻。连续掩码训练完,验证集指标比 LoRA 还高 3 个点,我很开心,然后把掩码硬二值化并合并权重,一跑验证,指标直接掉到比 LoRA 还低 1 个点。
问题根因在于训练与推理的不一致:训练用的是连续掩码,门控 0.3、0.6 都已经生效;推理却直接切成 0 和 1,等于改变了调制强度。这不是二值化本身错,而是训练策略没适配二值化目标。
解决思路是在训练过程中周期性把二值化结果拉回来一起训练:每 100 步,用当前门控生成硬二值掩码,做一次同样的前向,把这次前向的 loss 一并回传。相当于让门控网络在每次梯度更新时都知道“如果明天把我截断成 0/1,我该往哪调”。加入这个正则后,临时二值化验证指标的下降从 4 个点压缩到不到 0.5 个点,最终可以放心合并。
5.2 分组固定导致的覆盖盲区
第二坑属于设计问题。早期版本只用 SVD 聚类的 32 个固定语义组,某领域任务上效果一直不高,尝试增大 K 到 128 之后略微变好,但离我预期还差得远。我怀疑问题在“固定”上:有一个和任务强相关的方向横跨了三个语义簇,三个簇都被各自门控抑制,导致这条方向始终没有拿到足够大的权重。
随机重叠分组的方案就是为这个坑补上的。我不再只依赖单一粒度,而是把分组划分为三种尺度:语义分组、随机组、按行方向划分的交叉组,每组各自输出掩码标量,再做一次稀疏门控混合。这样一个方向总能被至少一个组完整覆盖,训练时不会因为分组边界而整个失效。
覆盖盲区问题在消融实验里看得很清楚:只保留语义分组时,数学推理任务指标比最终版低 2.1 个点;补上随机分组后,差距缩小到 0.9 个点。多尺度分组确实比单一尺度更稳,这个设计值得保留。
5.3 embedding 层要不要做门控,我的实测结论
第三个坑是 embedding 层。一开始我只对线性层做门控,因为注意力机制的 Q、K、V、O 矩阵天然适合“方向调制”,但实测中某个领域的指令跟随任务总差一口气,词表取向不对。
我把输入 embedding 也纳入 MiSS 门控范围后,领域任务指标涨了约 1.5 个点。原理也讲得通:领域任务的词汇分布和通用任务差别往往集中在词表子集,通过门控放大这些词的 embedding 方向幅值,比在深层权重里绕弯更直接。
但 embedding 门控不是白给的,它会让输入表示在不同任务间变化,影响模型对常见词的稳定响应,我试过在某些通用基准上反而掉 0.3 点。建议做法是:默认关闭 embedding 门控,只在目标领域词表特征和通用词表差异明显时单独打开,并配合较小的 λ 初始值。是否需要打开,可以先用原始模型跑一下领域词表 OOV 率做个快速预判。
| 现象 | 排查步骤 | 最终解法 |
|---|---|---|
| 二值化后指标掉 4 个点 | 对比连续掩码与硬掩码前向的输出差异,观察门控分布是否集中在 0.3~0.7 | 周期性硬掩码回填训练,压缩二值化落差 |
| 固定分组下任务指标卡住 | 可视化门控权重分布,发现目标方向被多个分组的边界切碎 | 加入随机重叠分组和多尺度混合 |
| 领域任务词表适配不足 | 分析领域特有词的响应强度,发现 embedding 未参与门控 | 按需开启 embedding 门控,并调小 λ 初始值 |
6. 什么场景适合 MiSS,什么场景别硬上
6.1 适合 MiSS 的三种典型场景
第一种是显存受约束的微调。如果你手上只有一个 24GB 左右的单卡环境,底座又是 7B 到 13B 规模,LoRA 虽然也能跑但优化器状态攒起来很挤,MiSS 的低参数量优势在这里最能体现,甚至可以把 batch size 再往上提一点。
第二种是多任务顺序更新。你需要在同一个底座上持续叠加不同领域的能力,MiSS 的抗遗忘特性让它天然适合这种场景。不需要额外写各种回放损失,只要保证不同任务的掩码分组间有足够区分度就好。
第三种是极端在意推理结构干净度的部署项目。不想维护“基座 + 适配器”的复合模型,希望训练完成后直接产出一个和原始结构完全一致的权重文件,MiSS 的合并式设计能省掉一堆工程环节。
6.2 不适合硬上 MiSS 的场景与补救方案
遇到“模型完全没接触过的知识”这类任务,比如给一个纯文本底座注入领域术语和对应关系,MiSS 纯门控基本无解,因为底座根本没有这个方向可以放大。这种场景我的建议是不要硬上,要么退回 LoRA 类增量方法,要么用 MiSS 同时并联一个轻量外部记忆模块,让门控承担已有能力的激活,外部模块承担新知识的注入。
另一个不合适的是极小样本低到百条以内的任务。门控网络虽然小,但也需要足够信号去区分哪些组该开哪些组不该开,数据太少时门控网络学不稳。这种场景下我更推荐先冻结底座,只做浅层分类头的常规训练,等数据量上来后再切换到 MiSS。
6.3 几条工程落地建议
最后分享几条我在反复实验里沉淀下来的经验。门控网络初始温度别设太低,1.0 左右比较稳,太低会在一开始就锁死分组选择,后面很难翻转。K 的取值不是越大越好,我在 7B 模型上试过 K=128,有效但收益递减,多出来的分组反而让门控网络学习变慢,32 到 48 是个甜点区。
合并权重之后一定做一次全量前向验证。我在早期由于遗忘这一步,某次合并后逻辑正确性一直没回归,最后排查下来是某个层掩码的二值化状态和训练状态不一致。写一个自动回归脚本,专门比较合并前和合并后的模型输出,一致率低于 99.9% 就要报警。另外,共享分组稀疏门控的随机种子对结果影响不小,对比实验里建议固定种子,不然你会把种子差异当成方法差异,这是我花了两周才想明白的教训。
坦白说,MiSS 这套方案还谈不上完美,数学推理任务上它和全参微调的差距依然存在,但它在“用极少量参数激活已有能力”这个方向上给了我很强的信心。如果你也在纠结 LoRA 的秩和任务维度错配的问题,不妨试试“选方向而不是学增量”这个思路,至少我现在的实验结果告诉我,这值得认真磕下去。