最近在做优化的时候涉及到了这块内容,觉得值得写下来,方便以后翻阅。
这篇目录
- 1. 先把麻烦讲清楚:模型能力不能像插件一样相加
- 2. 合并前提:不是所有模型都能合
- 3. 权重平均:最简单也最粗糙
- 4. Task Arithmetic:合并相对 base 的变化
- 5. 方向冲突:为什么简单相加会出麻烦
- 6. SLERP:在球面上做平滑插值
- 7. TIES-Merge:先处理符号冲突
- 8. DARE:随机丢弃 delta 再缩放
- 9. LoRA/adapter 合并与全模型合并
- 10. 合并实验怎么做
- 11. 常见失败模式
- 12. 常见误区
- 13. 小结
- 大模型视角
- 下一篇
摘要:模型合并试图把多个同源模型或 adapter 的能力组合起来,减少重新训练成本。它不是“把代码能力模型 + 客服能力模型 = 全能模型”的魔法,而是在权重空间里做有约束的实验。本文会从最简单的权重平均讲起,解释 task vector、delta、SLERP 球面插值、TIES-Merge 的符号冲突处理、DARE 的随机丢弃和缩放,并给出可运行的小代码帮助各位理解这些方法的直觉。最后会重点讨论模型合并的前提条件、评估清单和常见失败模式。
- 前置知识:专栏一的向量、矩阵、线性插值、低秩分解;专栏二 SFT/LoRA 可配合阅读
- 阅读时间:50-60 分钟
- 代码环境:Python 3.10+,示例只依赖标准库
- 读完目标:能解释为什么同源模型才适合合并,能理解 delta/task vector 的意义,能知道 SLERP、TIES、DARE 分别想缓解什么麻烦
1. 先把问题讲清楚:模型能力不能像插件一样相加
假设各位有一个基础模型 Base,又基于它微调了两个模型:
Code-Model:更擅长代码解释和修复
Customer-Model:更擅长客服话术和业务问答
各位可能会问:能不能不重新训练,直接把两个模型合成一个,同时保留代码和客服能力?这就是模型合并想探索的问题。
但权重不是插件市场。模型参数是高维空间里的连续数值,能力分布在大量层和矩阵中。两个微调模型在某些权重方向上可能互相增强,也可能互相冲突。合并结果可能更好,也可能两个能力都下降。
模型合并的价值在于:
价值说明成本低不得完整重新训练实验快可以快速相对多个合并权重适配 adapter多个 LoRA/adapter 可以尝试组合可探索帮助观察任务变化在权重空间里的关系
它的风险也很明显:
风险说明能力冲突两个任务对同一权重方向要求相反格式错乱chat template 或对齐行为被破坏安全退化拒答边界和偏好优化结果被冲淡不可解释单个 prompt 看似好,整体评估下降前提严格架构、tokenizer、base 不一致通常不能直接合并
所以本文的核心态度是:模型合并可以做低成本实验,但必须建立前提检查和评估闭环。
2. 合并前提:不是所有模型都能合
开始讲算法前,先讲更重要的前提。直接合并通常要求:
1. 架构完全一致;
2. 参数形状完全一致;
3. tokenizer 完全一致;
4. 最好来自同一个 base model;
5. chat template 和特殊 token 一致;
6. 微调方法和目标不要严重冲突;
7. 有独立评估集验证合并结果。
如果两个模型一个来自 LLaMA 基座,一个来自 Qwen 基座,直接按权重平均没有意义。即使参数形状碰巧一样,权重空间和 tokenizer 语义也不对齐。
最适合合并的情况通常是:
Base -> FineTune A
Base -> FineTune B
Base -> FineTune C
也就是多个模型都从同一个 Base 出发,只是在不同任务或数据上微调。这样它们的权重差异可以被解释为“相对同一个基座的任务变化”。
3. 权重平均:最简单也最粗糙
最简单的合并是加权平均:
W
m
e
r
g
e
=
α
W
A
+
(
1
−
α
)
W
B
W_{merge} = \alpha W_A + (1 - \alpha) W_B
Wmerge=αWA+(1−α)WB
先用大白话解释:把两个模型同名参数逐元素平均,
α
\alpha
α 控制更偏向哪个模型。
逐项解释:
- W
W_A
WA:模型 A 的某个权重张量;
- W
B
W_B
WB:模型 B 的对应权重张量;
- α
\alpha
α:合并权重,越大越偏向 A;
- W
m
e
r
g
e
W_{merge}
Wmerge:合并后的权重。
用列表模拟:
# Python 3.10+
def weighted_average(a, b, alpha):
return [alpha * x + (1 - alpha) * y for x, y in zip(a, b)]
w_a = [1.0, 2.0, 3.0]
w_b = [1.4, 1.8, 2.6]
for alpha in [0.25, 0.5, 0.75]:
print(alpha, weighted_average(w_a, w_b, alpha))
权重平均的优点是简单。缺点是它完全不知道哪些参数变化重要、哪些方向冲突、哪些层更敏感。它适合做 baseline,不适合作为无需评估的最终答案。
4. Task Arithmetic:合并相对 base 的变化
如果多个模型来自同一个 base,更常见的思路是先计算相对 base 的变化量,也叫 task vector 或 delta。
对于任务 A:
Δ
A
=
W
A
−
W
0
\Delta_A = W_A - W_0
ΔA=WA−W0
对于任务 B:
Δ
B
=
W
B
−
W
0
\Delta_B = W_B - W_0
ΔB=WB−W0
合并时写成:
W
m
e
r
g
e
=
W
0
+
λ
A
Δ
A
+
λ
B
Δ
B
W_{merge} = W_0 + \lambda_A \Delta_A + \lambda_B \Delta_B
Wmerge=W0+λAΔA+λBΔB
逐项解释:
- W
W_0
W0:基础模型权重;
- W
A
,
W
B
W_A, W_B
WA,WB:两个微调模型权重;
- Δ
A
,
Δ
B
\Delta_A, \Delta_B
ΔA,ΔB:微调相对 base 改了什么;
- λ
A
,
λ
B
\lambda_A, \lambda_B
λA,λB:每个任务变化的缩放系数。
直觉上,delta 比完整权重更有意义。完整权重里绝大部分是基础能力,两个模型大部分相同;delta 才是任务微调带来的变化。
代码模拟:
# Python 3.10+
def sub(a, b):
return [x - y for x, y in zip(a, b)]
def add(a, b):
return [x + y for x, y in zip(a, b)]
def scale(a, s):
return [x * s for x in a]
base = [1.0, 1.0, 1.0, 1.0]
model_a = [1.2, 0.9, 1.0, 1.1]
model_b = [0.9, 1.3, 1.0, 0.8]
delta_a = sub(model_a, base)
delta_b = sub(model_b, base)
merged = add(base, add(scale(delta_a, 0.7), scale(delta_b, 0.5)))
print("delta_a", delta_a)
print("delta_b", delta_b)
print("merged", merged)
这段代码告诉我们:模型合并关注的是“每个任务把 base 往哪个方向推”。如果两个任务在同一位置方向相反,就会产生冲突。
5. 方向冲突:为什么简单相加会出问题
看一个 toy 例子:
base 权重某位置 = 1.0
代码微调希望变成 1.3,delta = +0.3
客服微调希望变成 0.7,delta = -0.3
如果直接相加,两个 delta 抵消,回到 base;如果权重设置不当,还可能偏向某一方,损害另一方能力。
在真实模型中,这种冲突发生在高维权重空间里。它不一定对应单个可解释参数,但会影响层的表示、留意力投影、FFN 方向和输出风格。
方向冲突常见于:
冲突来源例子任务目标不同一个模型鼓励详细解释,另一个鼓励短答安全边界不同一个模型拒答更强,另一个更开放数据风格不同代码模型和客服模型输出习惯差异大chat template 不同特殊 token 和角色格式不一致微调强度不同一个 delta 很大,主导合并结果
TIES-Merge 和 DARE 等方法,部分就是为了缓解这些冲突。
6. SLERP:在球面上做平滑插值
SLERP(Spherical Linear Interpolation,球面线性插值)原本常用于在两个向量方向之间平滑插值。与普通线性插值不同,SLERP 更关注方向和范数的平滑变化。
普通线性插值是:
(
1
−
t
)
A
+
t
B
(1-t)A + tB
(1−t)A+tB
SLERP 可以写成:
slerp
(
A
,
B
,
t
)
=
sin
(
(
1
−
t
)
θ
)
sin
θ
A
+
sin
(
t
θ
)
sin
θ
B
\text{slerp}(A,B,t) = \frac{\sin((1-t)\theta)}{\sin\theta}A + \frac{\sin(t\theta)}{\sin\theta}B
slerp(A,B,t)=sinθsin((1−t)θ)A+sinθsin(tθ)B
其中
θ
\theta
θ 是向量
A
A
A 和
B
B
B 的夹角。
不用被公式吓到。它想做的事是:如果两个权重向量可以看成高维空间里的方向,普通插值走直线,SLERP 在球面上沿角度平滑过渡。
可运行代码:
# Python 3.10+
import math
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def norm(a):
return math.sqrt(dot(a, a))
def slerp(a, b, t):
na = norm(a)
nb = norm(b)
if na == 0 or nb == 0:
return [(1 - t) * x + t * y for x, y in zip(a, b)]
cos_theta = dot(a, b) / (na * nb)
cos_theta = max(-1.0, min(1.0, cos_theta))
theta = math.acos(cos_theta)
if abs(theta) < 1e-8:
return a[:]
sin_theta = math.sin(theta)
left = math.sin((1 - t) * theta) / sin_theta
right = math.sin(t * theta) / sin_theta
return [left * x + right * y for x, y in zip(a, b)]
print(slerp([1.0, 0.0], [0.0, 1.0], 0.5))
SLERP 常用于两个模型之间的平滑合并,尤其是希望保持权重方向性质时。但它也不是万能:如果两个模型本身能力冲突严重,平滑插值仍然可能得到中庸甚至退化结果。
7. TIES-Merge:先处理符号冲突
TIES-Merge 的直觉是:多个 task vector 合并时,不应该把所有 delta 直接相加。很多小变化可能是噪声;同一位置方向相反的变化可能会互相破坏。
可以把它简化成三步:
Trim:只保留重要 delta,裁掉小幅变化;
Elect Sign:为每个位置选择主导符号;
Merge:只合并与主导符号一致的变化。
下面写一个非常简化的 toy 版本。它不是论文级实现,只用来解释直觉:
# Python 3.10+
def sign(x):
return 1 if x > 0 else (-1 if x < 0 else 0)
def ties_merge(deltas, threshold=0.05):
merged = []
width = len(deltas[0])
for j in range(width):
values = [d[j] if abs(d[j]) >= threshold else 0.0 for d in deltas]
sign_score = sum(sign(v) for v in values)
elected = sign(sign_score)
if elected == 0:
merged.append(0.0)
continue
aligned = [v for v in values if sign(v) == elected]
merged.append(sum(aligned) / max(len(aligned), 1))
return merged
delta_code = [0.20, -0.10, 0.02, 0.30]
delta_chat = [0.15, 0.12, -0.03, -0.25]
print(ties_merge([delta_code, delta_chat], threshold=0.05))
这个例子里,小于阈值的变化被裁掉;符号冲突的位置会选主导方向;最后只合并方向一致的 delta。
TIES 的核心不是“符号投票一定正确”,而是提醒我们:模型合并要处理冲突,不能无脑相加。
8. DARE:随机丢弃 delta 再缩放
DARE 的直觉是:微调 delta 中有很多变化可能是冗余的。随机丢弃一部分 delta,再对保留部分做缩放,有时可以在保留任务能力的同时减少冲突。
一个简化公式可以理解为:
以概率 p 丢弃 delta 中的元素;
保留的元素除以 (1-p),保持期望幅度不变。
代码模拟:
# Python 3.10+
import random
def dare(delta, drop_rate, seed=0):
random.seed(seed)
keep_prob = 1.0 - drop_rate
result = []
for x in delta:
if random.random() < keep_prob:
result.append(x / keep_prob)
else:
result.append(0.0)
return result
print(dare([0.1, -0.2, 0.05, 0.3, -0.4], drop_rate=0.4, seed=42))
为什么丢弃后还要缩放?因为如果只丢弃不缩放,delta 的整体幅度会变小。除以保留概率,可以让保留下来的元素在期望上维持原来的总强度。
DARE 不意味着随机就一定更好。它是一种降低冗余和冲突的策略,实际效果依赖模型、任务、drop rate 和评估。
9. LoRA/adapter 合并与全模型合并
合并不一定发生在完整模型权重上,也可以发生在 LoRA adapter 上。
LoRA 微调后,基础模型不变,任务变化主要在 adapter 权重里。多个 adapter 如果来自同一个 base 和相近训练设置,可以尝试:
1. 合并 adapter delta;
2. 调整每个 adapter 权重系数;
3. 将 adapter merge 回 base 后再评估;
4. 保留多 adapter 动态路由,而不是强行合成一个。
全模型合并与 adapter 合并的取舍:
方式优点风险全模型合并推理时单模型,部署简单权重冲突和回滚成本高Adapter 合并成本低,易实验仍可能冲突,得同基座多 adapter 路由可按任务选择能力推理系统更繁琐
如果任务边界清晰,像是代码任务用代码 adapter、客服任务用客服 adapter,动态选择 adapter 可能比强行合并更稳。
10. 合并实验怎么做
模型合并应该像实验,而不是像发布流程。建议用步骤:
1. 确认 base、tokenizer、架构、chat template 一致;
2. 准备模型 A、B、C 的单独评估结果;
3. 选择合并方法:平均、task arithmetic、SLERP、TIES、DARE;
4. 设置少量合并权重网格,例如 0.2/0.5/0.8;
5. 生成多个候选合并模型;
6. 跑自动评估和人工样例;
7. 检查安全边界和格式;
8. 与原模型和单任务模型对比;
9. 只保留有明确收益的合并结果。
不要只试一个 prompt。模型合并非常容易出现“演示样例很好,系统评估变差”的情况。
评估表可以这样设计:
评估工程的Base 通用能力是否损害基础能力任务 A 能力是否保留模型 A 优势任务 B 能力是否保留模型 B 优势格式遵循JSON、Markdown、工具调用是否稳定安全拒答是否破坏对齐边界长上下文是否出现位置或记忆退化人工样例是否有明显风格混乱和幻觉
如果合并后每项都比单任务模型差一点,整体可能并不值得发布。
11. 常见失败模式
模型合并常见失败包含:
失败模式表现可能原因能力互相抵消两个任务都没保住delta 方向冲突输出风格混乱有时像代码模型,有时像客服模型数据风格差异大安全边界变弱更容易回答高风险问题对齐模型被未对齐 delta 冲淡过度拒答正常问题也拒答安全模型权重过高格式错乱chat template、JSON、工具调用不稳定模板或微调目标不一致长文本退化长上下文任务变差合并影响留意力或位置相关能力
出现失败时,不要只调合并系数。要回到前提检查:是否同基座、同 tokenizer、同模板、评估是否覆盖冲突任务、微调强度是否差异太大。
12. 常见误区
误区一:模型合并等于能力相加。 合并可能增强,也可能互相干扰。能力是否保留必须靠评估验证。
误区二:不同基座只要参数形状一样也能合。 权重空间和 tokenizer 语义不对齐时,直接合并通常没有意义。
误区三:SLERP/TIES/DARE 是万能高级方法。 它们只是缓解某些合并问题,无法解决任务本身冲突和数据质量问题。
误区四:合并可以替代训练。 合并适合低成本探索,不等于系统性训练和对齐。
误区五:只看几个 prompt 就能判断成功。 合并后的安全、格式、通用能力和各任务能力都要重新评估。
13. 小结
模型合并是在权重空间里组合能力的工程实验。权重平均简单但粗糙;Task Arithmetic 关注相对 base 的任务变化;SLERP 用球面插值让两个权重方向平滑过渡;TIES-Merge 尝试处理符号冲突;DARE 通过随机丢弃和缩放减少冗余 delta。它们都建立在重要前提上:最好同基座、同架构、同 tokenizer、同模板,并且必须有完整评估。
对开发者来说,最重要的不是记住每个方法名字,而是理解模型合并的边界:它能快速试验,不保证能力叠加;它能降低训练成本,不替代数据、训练和对齐;它能生成候选模型,但能不能上线要靠评估说话。
大模型视角
从大模型技术主线看,模型合并代表了“在权重空间做工程”的思路:不重新训练,而是把已有的能力向量组合复用。开源社区大量合并模型出自这种思路,它便宜、快速,但也提醒我们:没有数据和训练支撑的组合,能力上限是有限的。
下一篇
下一篇进入 SFT。有监督微调是把基础模型调成可用助手的关键步骤,也是后续 LoRA、QLoRA、DPO 和 RLHF 的基础。
这篇笔记就先到这里,后面用到新的思路或者发现有问题再补充。
评论 (0)
暂无评论