Lecture 13 · 像开源软件一样构建模型:协作、合并与持续更新 · Building Models like Open-Source Software

Advances in Foundation Models · Lecture 13。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:Stanford CS324 Winter 2023 Advances in Foundation Models,2/8 客座报告 Colin Raffel:Building ML Models like Open-Source Software。 依据说明:课程网站没有提供 slides 或录像。Raffel 在 2021 年 12 月发表过同题的博客 A Call to Build Models Like We Build Open-Source Software,2022–2023 年在多所大学做过同题报告,报告摘要列出的研究方向是:合并多个模型并保留各自的能力、用便宜的可传输更新给模型”打补丁”、模块化的架构、给模型参数做版本控制。本讲以这篇博客为主线,技术部分依据 Raffel 和合作者的论文:Matena & Raffel 2022(Fisher 加权合并)、Sung, Nair & Raffel 2021(固定稀疏掩码)、Yadav et al. 2023(TIES-Merging)、Kandpal et al. 2023(Git-Theta)、Don-Yehiya et al. 2023(ColD Fusion)、Borzunov et al. 2023(Petals),以及同一时期的相关工作 Ilharco et al. 2023(任务算术)、Wortsman et al. 2022(model soups)、Frankle et al. 2020 与 Ainsworth et al. 2023(线性模式连通性、Git Re-Basin)。报告具体讲了其中哪些,我无法确认;文献里的数字都是我在原文(或博客)里读到的;§9”2023 年之后”是我补充的。 配套代码 Code:code/lecture13/(PyTorch;4 个脚本 + 1 个模块。代码 01 约 20 秒,02 约 90 秒,03 第一次约 60 秒、之后约 10 秒,04 约 100 秒) 预计阅读时间:3 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 说清 Raffel 的论点:今天的基础模型是”一次性、集中式、发布后冻结”的,开源软件则是”很多人持续地贡献、审查、合并”的;要让模型也这样开发,需要哪几样技术(增量更新、合并、版本控制、测试、模块化)。
  2. 定义任务向量 τ=θft−θ0\tau = \theta_{\text{ft}} - \theta_0,说出它的几个经验性质(短、几乎两两正交、能量集中),并会用任务算术做加法(多任务)和减法(去掉一种行为)。
  3. 比较几种合并方法——简单平均、任务算术、TIES、Fisher 加权平均——以及它们和多任务训练、集成的区别;解释干扰从哪里来、为什么任务越多越难合并。
  4. 用线性模式连通性和置换对称性解释:为什么从同一个预训练模型出发的微调可以平均,两个独立训练的模型却不行;以及为什么”补丁”只能打到它出发的那个版本上。
  5. 说出几种”便宜的补丁”(稀疏掩码、低秩、模块),以及”补丁小”和”不弄坏别的功能”为什么是两回事。
  6. 描述模型的版本控制(Git-Theta)、以下游评测为”测试”的持续集成、社区迭代改进(ColD Fusion)和协作推理(Petals),以及开放权重生态里的许可问题。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲只给直觉,后面几讲再展开 Previewed only
第 6 讲 §3–4:全量微调、灾难性遗忘、LoRA 与”一个基座,很多插件”发布规范与分阶段发布(第 18 讲)
第 6 讲 §6:模型编辑(改一个事实)许可、版权与监管(第 19 讲)
第 9 讲 §7:去中心化训练
第 10 讲 §4–5:OPT 的公开日志、BLOOM 的开放协作
第 12 讲 §7:Pythia——开放的训练过程

0.3 本讲地图 Map

§1  模型怎样被"发布"、软件怎样被"开发":Raffel 的论点
§2  任务向量:把一次微调看成一个 diff                                   ← 代码 01
§3  任务算术:加法、减法、类比                                          ← 代码 01
§4  合并的方法与干扰:平均、任务算术、TIES、Fisher                       ← 代码 02
§5  为什么只有"同一个起点"才行:线性模式连通性与置换对称性               ← 代码 03
§6  便宜的补丁:稀疏、低秩、模块                                        ← 代码 04
§7  版本控制、测试与社区协作                                            ← 代码 04
§8  开放权重的生态与许可
§9  2023 年之后:开放模型的爆发与合并实践
§10–14  小结、常见误区、练习、延伸阅读、下一讲预告

0.4 记号 Notation(只列本讲新增的,其余见第 1 讲 §0.4)

符号含义例子
θ0\theta_0预训练模型(base model)的全部参数,摊平成一个向量第 1 讲 base model:109,056 维
θt\theta_t在任务 tt 上微调之后的参数
τt=θt−θ0\tau_t = \theta_t - \theta_0任务 tt 的任务向量(task vector)
λ\lambda合并时的缩放系数θ=θ0+λ∑tτt\theta = \theta_0 + \lambda \sum_t \tau_t
FtF_t模型 tt 的(对角)Fisher 信息:每个参数对任务 tt 有多重要
归一化准确率合并模型在任务 tt 上的准确率 ÷ 专门模型的准确率,再对任务平均1.0 = 一个模型顶 kk 个
损失屏障(barrier)两个模型之间直线路径上,损失比两端连线高出的最大值0 = 两者在同一个”盆地”

0.5 关于数字 · About the numbers

讲义里的实验数字都来自 code/lecture13/ 的实际输出(固定种子、PyTorch 单线程,同一台机器上重复运行逐字相同;微调出的模型缓存在本目录的 checkpoints/,删掉后重新训练得到逐位相同的结果)。所有实验都从第 1 讲的 base model 出发,在 5 个小任务上微调:3 个”读评论”(verdict、food、service)、2 个”读两位数”(parity、tens)。我们的模型只有 11 万参数,而论文里的模型有上亿到上百亿参数——几处结果和论文不一样(最重要的:TIES 和 Fisher 合并没有赢过任务算术;稀疏补丁之间几乎不重叠,合并时照样干扰),完整清单在代码 README 的”关于实验结果”里。


1. 模型怎样被”发布”,软件怎样被”开发” · The argument

1.1 今天的模型:一次性、集中式、发布后冻结

回想前几讲的”配方”:在海量数据上预训练一个大模型,然后适配到下游任务(第 1、6 讲)。Raffel 的博客从这个配方的一个副作用说起:

  • 预训练很贵,所以只有少数人能做。 博客引用 Lambda 的估计:训练 GPT-3 大约要 460 万美元。于是最常用的预训练模型几乎都出自大公司里的小团队,研究社区的大多数人只能”用”,不能”造”。
  • 模型被大量使用。 博客提到 Hugging Face 上的 BERT 检查点被下载了数千万次——一个模型就是成千上万个应用的地基(第 1 讲的”同质化”)。
  • 但模型发布之后几乎从不更新。 它们停在发布时的状态,直到被下一个模型取代。可是有很多理由需要更新:训练方法有了改进、发现了有问题的输出、发现模型记住了隐私数据(第 16 讲)、世界上的事实变了(第 6 讲 §6 的模型编辑)。而且没有一个标准的方法去做这些更新。

1.2 开源软件是怎样开发的

拿 Python 做对照。如果 Python 也是”一次性发布、之后冻结”,它不会有今天的大部分功能。开源软件靠的是一整套基础设施:

软件开发的做法它解决了什么
版本控制:一串原子的补丁(commit),一个主仓库,少数维护者每次修改都可以单独审查、回溯、撤销
pull request + 自动测试贡献者提出修改,测试通过、审查之后才合并
向后兼容以前能用的代码,升级之后还能用
合并冲突两个修改改了同一处时,需要(有时手动)解决
分叉(fork)无法调和的分歧可以分道扬镳(Debian 派生出了几十个发行版,包括 Ubuntu)
语义化版本号(补丁版 / 次版本 / 主版本)一眼看出一次升级改动有多大
包与依赖管理模块化地复用别人的组件

博客的核心主张:模型应该由一个大社区持续地更新和改进,为此要把上面这些想法移植到模型上。

1.3 一张对照表:如果模型是一个代码仓库

软件模型(本讲的对应)难点
一个 commit / diff一次微调 = 一个任务向量 τ\tau(§2)梯度下降每一步都改动全部参数,没有”只改了第 37 行”这回事
合并两个分支合并两个微调模型(§3–4)两个补丁改了同一批参数时会干扰;神经网络里”不同的行”也通过前向传播连在一起(§6.4)
补丁只能打到对应的版本上任务向量只对它出发的那个 θ0\theta_0 有意义(§5)两个独立训练的模型之间没有”行号”的对应
小补丁、便宜地传输稀疏更新、低秩更新、模块(§6)补丁小 ≠ 不弄坏别的功能
自动测试 / CI下游任务的评测(§7.2)测试很贵:要微调、要跑几十个任务
语义化版本号补丁版:只改一小部分参数;次版本:改架构但保留大部分参数;主版本:换一个新模型(§7.2)
社区持续贡献ColD Fusion、BigScience、Petals(§7.3–7.4)数据不能共享;算力分散

这张表是本讲的路线图。下面先从第一行开始:一次微调,能不能看成一个 diff?

1.4 讲者背景

Raffel 是 T5 论文(第 3 讲 §4.4)的第一作者,发表这篇博客时在北卡罗来纳大学(UNC)任助理教授。T5 本身就是一个”被很多人用、却由一个团队一次性训练出来”的典型;而他之后的工作——参与 BigScience(T0,第 7 讲 §2)、参数高效微调、模型合并、Git-Theta、Petals——都可以看成在为”像开源软件一样构建模型”补齐一块块基础设施。


2. 任务向量:把一次微调看成一个 diff · Task vectors

2.1 定义

Ilharco et al.(ICLR 2023)给出了最简单的定义:把预训练模型和微调后模型的全部参数各摊平成一个长向量,相减:

τt=θt−θ0.\tau_t = \theta_t - \theta_0 .

这就是”任务 tt 的 diff”。有了它,“把补丁打上去”就是 θ0+τt\theta_0 + \tau_t,“撤销”就是减回去——一切都是向量运算。问题在于:这种逐元素的加减有没有意义?神经网络是高度非线性的,参数空间里的”直线”不一定对应函数空间里有意义的东西。§2–§5 都在回答这个问题。

2.2 实验设置:5 个小任务

代码 01 在第 1 讲的 base model 上分别微调 5 个任务(每个 300 个训练样本,AdamW、学习率 1e-4、400 步,全部参数都训练):

任务提示(例子)答案组
verdictopinion: the soup was tasty but the host was rude. verdict:good / ok / bad评论
foodthe rice tasted fresh, so i saygreat / awful评论
serviceour waiter was rude. in short:kind / rude评论
parityis 37 even or odd? it iseven / odd数字
tenshow many tens are in 37? there are1 … 9数字

两个设计细节:

  • 答案都是语料里已有的词,只是格式没见过。我们的模型是字符级的,输入嵌入和输出层共享权重(第 3 讲 §3.8);如果答案是全新的词,每个任务都要大幅改动那几个字符的嵌入,任务之间冲突得一塌糊涂。写代码时我先用了新词(loved / yum),合并后的模型基本坏掉,才改成这样。
  • 数字任务的测试集只用训练时没见过的 30 个两位数,测的是规则,不是背诵。

结果(测试集准确率;最后一列是第 1 讲验证集的 bit/字符,越低越好):

模型verdictfoodserviceparitytens验证集 bpc
base model0.8470.4630.5170.4100.1470.409
微调 verdict1.0000.4500.6600.4100.1770.420
微调 food0.8071.0000.4800.4100.0930.460
微调 service0.4000.3601.0000.4100.0930.470
微调 parity0.8500.4100.5031.0000.1600.483
微调 tens0.6470.4370.5730.5900.8170.518

每个模型都学会了自己的任务(tens 在没见过的数上是 0.817),别的任务大多停在 base model 附近——但也有副作用:微调 service 之后 verdict 从 0.847 掉到 0.400。每次微调都让原来的语言建模能力变差一点(0.409 → 0.420–0.518)。base model 在 verdict 上就有 0.847,因为 good / ok / bad 正是语料里 rating: 后面的词,只是格式换了(第 6 讲 §1.2 讲过”格式差异”)。

2.3 任务向量的几何

代码 01 演示 2 量了这 5 个任务向量:

它们很短。 ∥τt∥/∥θ0∥\|\tau_t\| / \|\theta_0\| 只有 1.4%–3.2%。微调没有”重写”模型,只是在预训练的位置附近挪了一小步。这是任务算术能工作的前提:在一个点附近,函数近似是线性的(§3.4)。

它们几乎两两正交。 余弦相似度在 −0.018 到 +0.054 之间:

verdictfoodserviceparitytens
verdict1+0.054+0.052+0.029−0.018
food1+0.052+0.010+0.012
service1+0.020+0.002
parity1+0.001

两个 109,056 维的随机向量的余弦大约是 ±0.003(1/n1/\sqrt{n}),所以这些余弦虽小,多数是”真的”。同组(评论 × 评论、数字 × 数字)的平均余弦 +0.040,跨组 +0.009——和 Ilharco et al. 在 CLIP 上看到的一样:MNIST、SVHN、GTSRB 这几个”认数字”的任务彼此最像,EuroSAT 和 RESISC45(都是卫星图)彼此最像。他们推测,正是因为不同任务的向量接近正交,相加时才互相干扰得不多。

为什么高维空间里的向量容易正交? 在 nn 维空间里随便取两个方向,它们的余弦大约是 1/n1/\sqrt{n} 量级。一亿维的空间里,“几乎正交”是常态,不是巧合。真正有信息量的是偏离正交的程度——它反映了任务之间的相似性。

改动不是均匀分布的。 嵌入只占参数的 8.2%,却占了 ∥τ∥2\|\tau\|^2 的 20%–43%:字符级模型里,答案那几个字符的嵌入(同时也是输出层)被改得最多。MLP 占参数的 61%,占改动的 40%–60%。

能量很集中。 绝对值最大的 1% 的分量占 ∥τ∥2\|\tau\|^2 的 20%–41%,最大的 10% 占 49%–69%。但能力并不全在这些大分量里:

只保留最大的 k 比例k = 1%5%20%100%
verdict0.8730.9801.0001.000
food0.4630.4670.8401.000
service0.5470.5530.6631.000
parity0.4100.4100.8271.000
tens0.0930.0930.3000.817

只留 20%,verdict 还是满分,其余掉到 0.30–0.84;只留 1%,基本退回 base model。这张表会在两个地方再用到:TIES-Merging 的”修剪”(§4.3),以及”稀疏补丁要在训练时就选好位置”(§6.1)。


3. 任务算术:加法、减法、类比 · Task arithmetic

Ilharco et al. 提出了三种运算,全都是 θ=θ0+λτnew\theta = \theta_0 + \lambda \tau_{\text{new}} 的形式,λ\lambda 在留出的开发集上选:

运算τnew\tau_{\text{new}}想要的效果
加法∑tτt\sum_t \tau_t一个模型同时会多个任务(不需要再训练)
取负−τ-\tau忘掉一个任务或一种行为,别的不受影响
类比τC+(τB−τA)\tau_C + (\tau_B - \tau_A)“A 之于 B 如 C 之于 D”:没有 D 的数据也能提升 D

3.1 加法:两次互不知情的微调,相加就得到一个两个任务都会的模型

代码 01 演示 3 把 10 对任务向量两两相加(λ=1\lambda = 1),按归一化准确率(合并模型的准确率 ÷ 专门模型的准确率,两个任务平均):

任务对同组?归一化验证集 bpc
verdict + food是1.0000.488
verdict + parity否1.0000.514
food + service是0.9780.634
verdict + tens否0.9680.552
verdict + service是0.9470.493
food + tens否0.9170.700
service + parity否0.8330.611
food + parity否0.7700.637
service + tens否0.6130.657
parity + tens是0.4820.723

10 对的平均 0.851。一半的对子在 0.9 以上——两个人各自微调、互不知情,把”补丁”直接相加,就得到一个两个任务都会的模型。Ilharco et al. 在 CLIP 上(8 个图像分类任务里任取两个)的平均是 98.9%,比我们好得多:我们的模型小得多,“近似线性”的区域也小得多(§3.4)。

两个值得注意的地方:

  • 代价藏在验证集上。 每个补丁都带着一点”遗忘”,两个加起来遗忘更多(0.488–0.723,单个任务 0.420–0.518)。
  • 余弦相似度不能预测哪一对合并得好。 同组和跨组的平均几乎一样(0.852 vs 0.850);最差的是同组的 parity + tens(0.482)。两个任务的提示都以一个两位数结尾,要从同一个位置读出不同的东西(奇偶 vs 十位)——这是一个猜测,§4.4 会看到干扰的另一面。

3.2 减法:只用”坏的例子”,去掉一种行为

第 1 讲的迷你世界里,故事句的代词和职业有关:pilot 后面 85% 是 she,tailor 后面 85% 是 he(这是我们随意设定的偏斜,第 1 讲 §0)。base model 学到了这个偏斜。能不能把它”减掉”?

代码 01 演示 4 的做法和 Ilharco et al. 减少 GPT-2 有害输出的做法一样:

  1. 造一份极端的刻板印象数据:4 个有偏斜的职业,代词总是多数的那个(pilot / potter 永远 she,tailor / judge 永远 he);
  2. 在上面微调 200 步,得到”刻板印象向量” τ\tau;
  3. 从 base model 里减掉它:θ0−λτ\theta_0 - \lambda \tau。

“偏斜” = 8 个职业的 ∣P(she)−0.5∣|P(\text{she}) - 0.5| 的平均:

模型偏斜验证集 bpc
base model0.1520.409
在刻板印象上微调0.3770.419
θ0−0.25τ\theta_0 - 0.25\tau0.0940.410
θ0−0.5τ\theta_0 - 0.5\tau0.0500.413
θ0−1.0τ\theta_0 - 1.0\tau0.0530.421
θ0−2.0τ\theta_0 - 2.0\tau0.2420.480
θ0−0.5×\theta_0 - 0.5 \times 同样长的随机向量0.1460.409
在 50 / 50 的数据上微调0.0330.418

几点观察:

  • 减掉一半就把偏斜降到三分之一,验证集几乎不变。我们从没告诉模型”正确的比例是多少”,只给了”坏的例子”。
  • 减太多会矫枉过正:λ=2\lambda = 2 时所有职业一起偏向 he(P(she)≈0.25P(\text{she}) \approx 0.25)。
  • 方向才是关键:减掉一个同样长度的随机向量几乎没有作用。
  • 在 50 / 50 的数据上直接微调效果更好一点(0.033)。如果你有”好的数据”,这当然更直接;任务向量的好处是减法不需要再训练、λ\lambda 可以事后调、同一个向量还能加回去(撤销)。
  • 刻板印象微调还”传染”到了没有偏斜的职业:baker、singer 都从 0.5 左右偏到了 0.2 以下。一个补丁的影响范围常常比你以为的大——这也是为什么需要回归测试(§7.2)。

Ilharco et al. 的数字:GPT-2 Large 减掉”有害文本”任务向量后,有害输出从 4.8% 降到 0.8%,WikiText-103 困惑度从 16.4 变成 16.9(他们要求在 0.5 以内);对照的”梯度上升”(直接在有害数据上最大化损失)把有害输出降到 0,代价是困惑度爆炸到 101010^{10} 以上。图像上,CLIP ViT-L/14 减掉任务向量后,目标任务的准确率平均下降 45.8 个百分点,ImageNet 上几乎不变。

3.3 类比

如果任务 A、B、C、D 满足”A 之于 B 如 C 之于 D”,那么 τD≈τC+(τB−τA)\tau_D \approx \tau_C + (\tau_B - \tau_A)。Ilharco et al. 的例子:情感分类,从”亚马逊评论”迁移到”Yelp 评论”——用”亚马逊上的语言模型任务向量”、“Yelp 上的语言模型任务向量”和”亚马逊上的情感分类任务向量”组合出”Yelp 上的情感分类”,不需要 Yelp 的标注数据。这和 word2vec 的 king − man + woman ≈ queen 是同一个味道,只不过运算发生在权重空间里。我们的代码没有做类比(练习 4 让你试)。

3.4 为什么能工作:小步之内近似线性

一个直觉的解释:在 θ0\theta_0 附近做一阶泰勒展开,

f(x; θ0+τ)≈f(x; θ0)+∇θf(x; θ0)⊤τ.f(x;\, \theta_0 + \tau) \approx f(x;\, \theta_0) + \nabla_\theta f(x;\, \theta_0)^\top \tau .

如果这个近似成立,模型的输出对 τ\tau 是线性的:τA+τB\tau_A + \tau_B 的效果 = τA\tau_A 的效果 + τB\tau_B 的效果。再加上任务 A 的输入 xAx_A 上,∇f(xA)⊤τB≈0\nabla f(x_A)^\top \tau_B \approx 0(B 的补丁不影响 A 的输入),两个补丁就互不干扰。

两个前提都只是”近似”:

  • “小步”:τ\tau 越短,线性近似越好。这就是我们用小学习率(1e-4)的原因;写代码时我先用 3e-4 和 1e-3,任务向量更长,相加后原验证集坏到 2–4 bit/字符(README “关于实验结果”)。
  • “互不影响”:Ortiz-Jimenez et al.(NeurIPS 2023)把它叫做”权重解耦”(weight disentanglement),发现它随预训练和规模而增强——这可以解释为什么任务算术在大模型上好用得多,在我们的 11 万参数模型上差一截。

4. 合并的方法与干扰 · Merging methods and interference

4.1 从一个模型的多个版本说起:model soups

最早引起注意的合并其实是同一个任务的多个微调版本。通常的做法是用不同超参数微调很多次,留下开发集上最好的那个,其余扔掉。Wortsman et al.(ICML 2022)的 model soups 把它们的权重平均起来:准确率和鲁棒性往往比最好的单个模型还高,而且和集成(ensemble)不同,推理成本没有任何增加——平均之后还是一个模型。他们的 ViT-G 汤在 ImageNet 上达到 90.94% 的 top-1 准确率(当时的最好成绩)。

“平均”能工作,是因为这些模型都从同一个预训练模型出发,落在同一个”盆地”里(§5)。下面看不同任务的模型合并。

4.2 Fisher 加权平均:给每个参数一个”发言权”

Matena & Raffel(NeurIPS 2022)从贝叶斯的角度看合并:每个微调模型给出了参数的一个”后验分布”,合并 = 找一组参数,同时让所有后验的概率都高。用拉普拉斯近似,把模型 tt 的后验近似成以 θt\theta_t 为中心的高斯分布,精度矩阵取 Fisher 信息 FtF_t。几个高斯的乘积的峰值是:

θ∗=(∑tFt)−1∑tFt θt,\theta^* = \Big(\sum_t F_t\Big)^{-1} \sum_t F_t\, \theta_t ,

只用对角 Fisher 时,就是逐个参数的加权平均:对任务 tt 重要的参数(FtF_t 大),合并时更多地听任务 tt 的。普通平均相当于假设所有 FtF_t 都相同(各向同性的高斯)。对角 Fisher 的估计很简单:

Ft[i]≈1n∑(x,y)(∂log⁡pθ(y∣x)∂θi)2.F_t[i] \approx \frac{1}{n}\sum_{(x, y)} \Big(\frac{\partial \log p_\theta(y \mid x)}{\partial \theta_i}\Big)^2 .

论文报告 Fisher 合并在鲁棒微调、模型集成上好于简单平均,在”中间任务训练""领域自适应预训练”这类迁移场景里,用低得多的成本达到了和梯度迁移相当的效果。

4.3 TIES-Merging:修剪、选符号、只平均同意的

Yadav, Tam, Choshen, Raffel & Bansal(NeurIPS 2023)认为直接相加有两种干扰:

  1. 冗余:每个任务向量里大部分分量很小,只是噪声,相加之后会淹没别的任务的重要分量;
  2. 符号冲突:同一个参数,任务 A 想让它变大、任务 B 想让它变小,相加之后互相抵消。

所以分三步(TRIM、ELECT SIGN、MERGE):

1. 修剪:每个 τ_t 只保留绝对值最大的 k%(论文默认 20%),其余置 0
2. 选符号:每个参数的符号 = sign(Σ_t 修剪后的 τ_t)    —— 谁的总量大听谁的
3. 不相交的平均:只对符号和选出的一致的那些任务求平均
最后 θ = θ_0 + λ · 合并后的向量

论文在多种模态、任务数、模型大小上报告 TIES 好于之前的方法,并认为解决符号冲突尤其重要。

4.4 在我们的 5 个任务上比一比

代码 02 演示 1 把 5 个任务模型合并成 1 个(测试集准确率;超参数都在开发集上选):

方法verdictfoodserviceparitytens归一化bpc
5 个专门模型(上限)1.0001.0001.0001.0000.8171.000—
base model(不合并)0.8470.4630.5170.4100.1470.4830.409
简单平均(λ=0.2\lambda = 0.2)1.0000.6030.4800.4100.0930.5220.413
直接相加(λ=1\lambda = 1)0.7230.5670.9471.0000.1470.6831.384
任务算术(λ=0.6\lambda = 0.6)0.9930.8871.0000.8930.2430.8140.548
TIES(k = 50%,λ=1.0\lambda = 1.0)0.8400.6430.6430.5900.2130.5960.470
Fisher 加权平均0.8270.4530.7170.5900.3300.5980.458
多任务微调(共享数据,2000 步)1.0001.0001.0001.0001.0001.0450.793

怎么读这张表:

  • 简单平均几乎等于没合并:每个任务向量被缩小到 1/5,只有本来就接近会的 verdict 到了 1.000。直接相加又走得太远:任务之间互相干扰,原验证集从 0.409 坏到 1.384。
  • 任务算术在两者之间选出 λ=0.6\lambda = 0.6,是最好的合并:5 个任务里 4 个在 0.89 以上,只有 tens 掉得多。它只用到 5 个任务向量和一小份开发集——不需要训练数据,也不需要再训练。
  • TIES 和 Fisher 平均都比简单平均好,但都没有赢过任务算术。 这和论文不一样(TIES 论文在 T5、ViT 上报告它稳定地超过任务算术)。一个可能的原因在 §2.3 的表里:我们的小模型里,被修剪掉的小分量也携带了不少能力;Fisher 平均则和简单平均一样受”1/5 稀释”的影响。我没有为了得到论文的结论去调更多的超参数——这是一个真实的”小模型上不成立”的例子。
  • 多任务微调几乎完美(tens 甚至从 0.817 升到 1.000:一起学 parity 帮了 tens),但原验证集 0.793 比任务算术还差,而且它需要把 5 份数据放在一起重新训练——这正是开源协作里做不到的事。合并的价值在于:只交换参数,不交换数据。

4.5 任务越多越难

代码 02 演示 2 对 2、3、4、5 个任务的全部组合做合并,报告归一化准确率的平均:

合并几个任务组合数简单平均任务算术TIESFisher任务算术选的 λ\lambda
2100.6260.9240.8680.8160.5–1.0
3100.5310.8310.7560.7020.6–0.9
450.5210.8120.6920.6410.6–0.7
510.5220.8140.5960.5980.6

大体上所有方法都随任务数增加而变差(任务算术在 4、5 个任务时持平)。Ilharco et al. 在 CLIP 上看到同样的趋势:2 个任务 98.9%,8 个任务全部相加 91.2%。简单平均掉得最快——kk 个任务时每个补丁只剩 1/k1/k——3 个任务以上就和 base model 差不多。任务算术选出的 λ\lambda 随任务数增加而变小:任务越多,越要”少走一点”来减少干扰。

4.6 干扰从哪里来:随机撞车,而不是系统对抗

代码 02 演示 3 把每个任务向量修剪到最大的 20%,再数每个参数被几个任务同时改动、方向是否一致:

同时改动它的任务数占参数的比例其中符号冲突符号完全随机时的期望
036.0%——
137.3%——
219.2%49.2%50.0%
35.9%73.8%75.0%
41.3%84.4%87.5%
50.3%84.8%93.8%

每个任务只留 20%,5 个任务加起来却覆盖了 64% 的参数,其中 27% 被两个以上的任务同时改动。而这些重叠的参数里,符号冲突的比例和”符号完全随机”几乎一样。两两之间看,冲突比例都在 45%–52%,只有评论组内部略低于 50%。

这告诉我们:不同任务之间不是在系统地”对着干”,而是随机地撞车——这和任务向量几乎正交是同一件事的两面。但随机撞车也有代价:直接相加时,一半的重叠改动互相抵消。TIES 的”选符号”在每个冲突的参数上只听多数派的,少数派的改动被丢掉;如果这些改动并不都是噪声(在我们的模型里似乎不是),TIES 就会吃亏。

4.7 合并、多任务训练、集成:怎么选

需要共享数据?需要再训练?推理成本存储效果(一般而言)
kk 个专门模型否否1 个模型 / 次(但要知道用哪个)kk 个模型每个任务最好
集成(ensemble)否否kk 倍kk 个模型好
多任务训练是是11通常最好
参数合并否否(只需一小份开发集选 λ\lambda)11介于中间,随任务数下降

合并的独特之处在最后一行:它是唯一一个”不共享数据、不再训练、推理成本不变”的方法。这正好对应开源协作的约束:贡献者各自有数据和算力,交回来的只是参数。


5. 为什么只有”同一个起点”才行 · Why a shared starting point matters

5.1 线性模式连通性

Frankle et al.(ICML 2020)研究了一个问题:从同一个参数出发、用不同的随机性(数据顺序)训练出两个模型 θA\theta_A、θB\theta_B,它们之间的直线 (1−α)θA+αθB(1-\alpha)\theta_A + \alpha\theta_B 上,损失会不会先升后降?如果不会(损失屏障 ≈ 0),就说两者线性模式连通(linearly mode connected):它们在同一个”盆地”里。他们发现,从随机初始化开始训练时通常不连通,但只要先训练一小段再分叉,就连通了——训练早期很快就”选定”了一个盆地。

Neyshabur, Sedghi & Zhang(NeurIPS 2020)在迁移学习里看到同样的现象:从同一个预训练模型出发微调的模型,留在损失地形的同一个盆地里,它们在特征空间里相似、在参数空间里接近。这就是 §2–§4 的一切能工作的原因:所有任务向量都从同一个 θ0\theta_0 出发。

代码 03 演示 1 在两个微调模型之间走直线:

  • verdict ↔ parity:验证集 bpc 两端 0.420 / 0.483,路径上最低 0.415,没有任何”山”(屏障 = 0)。任务准确率在路径上”交接”,而且很突然:α=0.6\alpha = 0.6–0.8 时两个任务都在 0.99 以上;中点 α=0.5\alpha = 0.5 时 parity 还停在 0.410。
  • parity ↔ tens:bpc 两端 0.483 / 0.518,中点反而更低(0.440,中点离 base model 更近,遗忘更少)。但找不到一个 α\alpha 让两个任务同时高。

所以合并失败不一定是因为损失地形里有山——平坦的盆地里也会有任务之间的干扰。

5.2 置换对称性:同一个函数,很多份”抄本”

神经网络有大量对称性。最简单的一种:把 MLP 的第 ii 个和第 jj 个隐藏神经元连同它们的进出权重一起交换,输出完全不变。TinyGPT 里可以这样置换的有:残差流的 64 个维度(LayerNorm 只用到均值和方差,对维度顺序不敏感)、每层 MLP 的 256 个单元、每层 4 个注意力头的顺序、每个头内部 q / k 和 v 的维度。

代码 03 演示 2 把 base model 的所有这些单元随机重排:

  • 输出和原模型只差 3e-5(浮点运算顺序不同),验证集 bpc 都是 0.409——同一个函数;
  • 参数之间的距离 38.8,比参数本身的范数(33.3)还大——完全不同的参数;
  • 把两者平均:中点的 bpc 是 6.463,比均匀乱猜(log⁡244=5.46\log_2 44 = 5.46)还差。

第 ii 个神经元和第 jj 个神经元被平均到了一起,而它们本来在做不同的事。这说明”参数平均”本身没有意义;它只在两个模型的隐藏单元已经对应好时才有意义。从同一个预训练模型出发的微调,自动满足这个条件。

5.3 两个独立训练的模型:先对齐,再平均

Entezari et al.(ICLR 2022)猜想:如果把置换对称性考虑进去,独立训练的模型之间的屏障大多会消失。Ainsworth, Hayase & Srinivasa 的 Git Re-Basin(ICLR 2023)给出了几种对齐算法(按权重匹配、按激活匹配等),在 MLP 和 ResNet 上把独立训练的模型对齐后再插值,第一次在 CIFAR-10 的 ResNet 上做到了屏障接近 0;他们也发现模型越宽越容易对齐,并给出了反例。

代码 03 演示 3 用不同的初始化种子(1 而不是 0)、完全相同的数据和设置,再从零预训练一个 base model(验证集 0.411,和原模型 0.409 一样好),然后:

  1. 直接插值:中点 bpc 6.255,屏障 5.845——和”抄本平均”差不多。
  2. 按激活对齐后再插值:在同一批验证集文本上跑两个模型,对每一类可置换的单元算两两相关系数,用匈牙利算法求”总相关最大”的一一对应(先按注意力模式对齐头,再对齐头内部的维度),然后重排第二个模型。中点 4.024,屏障降到 3.614。

对齐确实消掉了一部分屏障,但远没有消失;参数距离也只从 38.6 降到 36.0。两个独立训练的 Transformer 并不只是彼此”换了顺序的抄本”。原因至少有两个:我们的模型只有 64 维(Git Re-Basin 发现越窄越难对齐);注意力头内部的对称性不只是置换——q 乘一个可逆矩阵 MM、k 乘 M−⊤M^{-\top},注意力分数完全不变,这种连续的对称性按单元一一配对是对不齐的。

5.4 补丁只能打到它被写出来的那个版本上

代码 03 演示 4 把这件事落到”补丁”上:在另一个 base model 上微调 parity(它自己用起来是满分),把这个任务向量加到我们的 base model 上:

模型parity 准确率验证集 bpc
base model0.4100.409
+ 自己的补丁(在 base 上微调)1.0000.483
+ 别人的补丁(在另一个 base 上微调)0.4100.427
+ 别人的补丁,先按对齐的置换”翻译”0.4100.418

完全没用,“翻译”之后也没用。这就像一个 diff 只能打到它被写出来的那个版本上——换了版本,行号全对不上。

这解释了开源模型生态的一个现象:大家都从同一个公开的 base model 出发微调(同一个 BERT、同一个 T5、后来同一个 Llama 版本)。共享的起点不只是为了省算力,更是任务向量能够互相合并、补丁能够互相交换的前提。用开源软件的话说:base model 就是主干(trunk),一切分支都从它长出来。也因此,换一个新的 base model(“主版本升级”)意味着所有下游的补丁都要重做——这是 §7.2 版本号设计要面对的问题。


6. 便宜的补丁:稀疏、低秩、模块 · Cheap, communicable updates

6.1 稀疏补丁:只改最重要的那一小部分参数

梯度下降每一步都改动全部参数,所以一次微调的”diff”和模型一样大。分布式训练和联邦学习早就在研究怎样减少通信:只更新一部分参数,或者压缩更新(第 9 讲 §7.4)。Sung, Nair & Raffel(NeurIPS 2021)提出固定稀疏掩码(FISH mask):训练开始前,用 base model 在任务数据上算一遍对角 Fisher 信息,选 Fisher 最大的 kk 个参数,整个训练过程只更新它们。Raffel 的博客引用这项工作:只更新 0.5% 的参数,可以达到和全部参数训练相近的效果。

代码 04 演示 1(学习率 1e-3;最后一行是全部参数、学习率 1e-4):

任务k补丁大小Fisher 掩码:准确率bpc随机掩码:准确率bpc
food0.5%4.3 KB0.8231.2930.5700.591
food2%17.0 KB0.9971.8650.9300.482
food10%85.2 KB1.0001.1881.0000.559
food100%426.0 KB1.0000.460——
tens0.5%4.3 KB0.1800.8970.0500.460
tens2%17.0 KB0.7700.8930.2730.546
tens10%85.2 KB1.0001.1351.0000.934
tens100%426.0 KB0.8170.518——

(稀疏补丁每个改动存 4 字节下标 + 4 字节新值;稠密补丁存全部 109,056 个 float32。)

  • 补丁可以很小:只改 2% 的参数(17 KB,整个模型的 1/25),food 就到了 0.997。Fisher 选的位置比随机位置有效得多:0.5% 时 0.823 vs 0.570;tens 在 2% 时 0.770 vs 0.273。
  • 但有一个我写代码前没想到的代价:Fisher 掩码的模型把原验证集弄坏得多得多(bpc 0.89–1.87;随机掩码 0.46–0.93;全部参数 0.46–0.52)。Fisher 大的参数是”对这个任务的输出最敏感”的参数——比如答案那几个字符的嵌入——它们对一般的语言建模也最敏感;再加上学习率大,改得又猛又集中。补丁小和不弄坏别的功能是两回事。
  • 小心混杂因素:稀疏补丁的学习率是全部参数的 10 倍,所以 tens 在 10% 时(1.000)反而超过了全部参数(0.817)。这说明的是学习率,不是”稀疏更好”——第 6 讲 §3.4 遇到过同样的问题(BitFit 和全量微调的学习率差了 30 倍)。

6.2 低秩补丁:LoRA

另一种”小补丁”是第 6 讲 §4.6 的 LoRA:权重更新限制成低秩 ΔW=BA\Delta W = BA。第 6 讲代码 03 里,一个 LoRA 插件只有 36.9 KB,整个模型 434.1 KB;合并进 WW 之后,推理没有额外成本。LoRA 后来成了开源社区交换”补丁”最常见的格式:一个 base model、成百上千个 LoRA(第 6 讲 §4.8”一个基座,很多插件”)。

6.3 模块:让不同的贡献在计算图上也分开

博客列出的第三条路是模块化:adapter(第 6 讲 §4.2)、混合专家层(第 3 讲 §8.1)、读外部数据的模型(kNN-LM、REALM、RAG,第 3 讲 §8.2)。设想一个模型可以插上”新语言输入模块""检索模块""生成模块”,再配一个包管理器,声明每个模型由哪些子模块组成、兼容哪些版本——就像 Python 的依赖管理。

6.4 参数不重叠 ≠ 功能不重叠

稀疏补丁还有一个诱人的想法:如果每个贡献只改动一小部分参数,而且彼此不重叠,合并时不就没有冲突了吗?代码 04 演示 2 检验了这个想法:

补丁覆盖的参数被 ≥ 2 个补丁改动λ\lambda归一化bpc
Fisher 掩码 2% × 57.0%1.8%0.50.5201.563
Fisher 掩码 2% × 57.0%1.8%1.00.4225.705
随机掩码 10% × 540.9%8.2%0.50.6310.685
随机掩码 10% × 540.9%8.2%1.00.4892.263
(对照)稠密任务算术100%—0.60.8140.548

5 个 Fisher 补丁一共只覆盖 7% 的参数、几乎不重叠,合并之后照样互相干扰,比稠密的任务算术还差。原因是:所有任务共享同一条残差流、同一批字符嵌入(第 12 讲 §2.1)。一个补丁改了”读这个位置的方式”,另一个补丁的输入就变了。在代码里,两个改了不同行的 commit 可以干净地合并;在神经网络里,“不同的行”仍然通过前向传播连在一起。这正是 §6.3 的模块化要解决的问题:让不同的贡献不仅在参数上分开,在计算上也分开(例如每个任务走自己的 adapter,或者用路由在专家之间选择)。


7. 版本控制、测试与社区协作 · Version control, testing and collaboration

7.1 Git-Theta:给参数做版本控制

普通的版本控制工具(以及 Git LFS)把模型检查点当成一个不透明的大文件:改一个参数,整个文件都是”新的”。Kandpal et al.(2023)的 Git-Theta 是 Git 的一个扩展,它理解检查点的内部结构,按参数组(每一层的每个矩阵)跟踪变化,因此能做到:

  • 省通信的更新:只存、只传改动过的参数组;稀疏或低秩的更新可以按补丁的形式存储;
  • 自动合并:两个分支改了同一个参数组时,可以调用合并策略(平均、任务算术……)而不是报冲突;
  • 有意义的 diff:报告两个版本之间哪些层变了、变了多少;
  • 插件系统:用户可以加入新的更新类型和合并策略。

论文演示了一个持续适配、修改预训练模型的场景。它还很初步,但方向很清楚:把 §2–§6 的技术变成日常工具。

7.2 测试:下游评测就是单元测试,但很贵

开源软件靠自动测试决定能不能合并。模型的”测试”是什么?博客的回答:下游任务上的表现——以及能力测试和偏见测试(例如共指消解里的性别偏见数据集)。困难在于:

  • 贵:每个下游任务可能要微调几百到几千步,而一个模型通常要在几十个任务上评测。
  • 一个改动的影响范围很难预料:§3.2 的刻板印象补丁”传染”到了没有偏斜的职业;§6.1 的稀疏补丁把语言建模弄坏了。

博客还提出了一套模型的语义化版本号:

  • 补丁版(1.0.0 → 1.0.1):只改一小部分参数,性能不明显下降;
  • 次版本(1.0 → 1.1):可以改架构,但保留大部分参数;
  • 主版本(1.x → 2.0):换一个新模型,面向同样的用途。

“向后兼容”指的是:目标任务上的表现不下降,输入输出的类型不变。博客的例子:一个视觉模型如果不再支持深度图(RGB-D)输入,就破坏了向后兼容。

博客里有一个虚构的时间线,很好地把这些想法串在一起:某大学用有限的算力发布了社区语言模型 CLAMP 1.0.0;另一个组加入了稀疏更新,和一个并行的改进一起合并成 1.0.1;有人提交了一个压缩技术,因为在一个新的公平性测试上表现变差而被拒绝;一个”新语言模块”让版本升到 1.1.0;一个源代码模块超出了项目的范围,于是分叉成了另一个模型;最后,一个更高效的架构带来了 2.0.0。

7.3 社区迭代改进:ColD Fusion

Don-Yehiya et al.(ACL 2023)的 ColD Fusion 把”合并”变成一个循环:

共享模型 θ_r
   ├── 贡献者 1 在自己的数据上微调 → θ_r,1  ┐
   ├── 贡献者 2 ……                   → θ_r,2  ├─ 维护者平均 → θ_{r+1}
   └── 贡献者 k ……                   → θ_r,k  ┘
下一轮所有人从 θ_{r+1} 出发

贡献者不分享数据,只交回微调后的模型。论文在 35 个数据集上迭代,得到的模型在它见过的所有数据集上都表现很好,作为新任务的微调起点也更好:平均比 RoBERTa 高 2.33 分,架构完全不变。

代码 04 演示 3 做了一个玩具版:4 个贡献者(verdict、food、parity、tens),每轮各微调 200 步,平均成新的共享模型;service 不参与,用来看”没见过的任务”。每轮跑一次回归测试:原验证集 bpc 不能超过 base model 的 1.1 倍(≤ 0.449)。

第几轮归一化(4 个任务)bpc回归测试service(零样本)
0(base)0.4750.409通过0.517
10.5180.412通过0.737
20.5440.423通过0.853
30.5930.442通过0.780
40.8030.463不通过0.660
50.8460.488不通过0.597

贡献者微调时混入预训练文本(回放,第 6 讲 §3.4):

第几轮归一化bpc回归测试service(零样本)
10.5090.406通过0.637
30.5870.406通过0.717
50.6040.407通过0.790
  • 迭代很关键。 第 1 轮之后只有 0.518——和一次性的简单平均差不多(§4.4)。但一轮一轮迭代下去,共享模型在 4 个任务上同时变好(第 5 轮 0.846):每一轮大家都从”已经包含别人改进”的版本出发,干扰被一点点消化掉。
  • 测试守门。 代价是共享模型离原来的语言模型越来越远,第 4 轮起过不了回归测试——就像一个项目不停合并新功能,却没人跑老的测试。回放让 bpc 一直在 0.41 以内,但任务上的进步慢得多(第 5 轮 0.604,parity 一直没学会)。守住旧功能和加新功能之间有真实的取舍;回归测试的作用是让这个取舍变得可见。
  • 没参与的任务:零样本准确率先升后降(0.517 → 0.853 → 0.597),回放的版本则一直在升(→ 0.790)。ColD Fusion 报告共享模型是更好的”微调起点”;我们的 service 任务太简单(从 base model 微调 50 步就满分),看不出起点的差别。

7.4 协作训练与协作推理

“社区一起造模型”在 2022–2023 年已经有了几个真实的例子:

  • BigScience / BLOOM(第 10 讲 §5):上千名研究者、一年的开放协作,训练了 176B 参数的多语言模型,并公开了数据治理、训练日志和模型。它证明了”大模型可以不出自一家公司”,但训练本身仍然是在一台超级计算机上集中进行的。
  • 去中心化训练(第 9 讲 §7):Ce Zhang 等人研究在慢速、异构的网络上训练,把重的通信放在快的链路上。
  • Petals(Borzunov et al. 2023,Raffel 是作者之一):让很多人各自贡献一张消费级 GPU,每人托管模型的几层,像 BitTorrent 一样拼成一个完整的 BLOOM-176B。推理速度约每秒 1 步,足够做交互式应用,比把模型卸载到内存里快得多。和大多数推理 API 不同,Petals 暴露了模型的隐藏状态,所以用户可以在上面训练、分享自己的参数高效插件(prompt tuning、adapter)——“一个共享的 base model + 社区贡献的补丁”。

8. 开放权重的生态与许可 · The open-weights ecosystem

8.1 模型仓库与文档

Hugging Face Hub 在 2023 年已经成了”模型的 GitHub”:成千上万的检查点、数据集和 demo,大多数从少数几个 base model 微调而来。Ilharco et al. 做 NLP 实验时,就在 Hub 上找到了 427 个和 T5-base 兼容的检查点,挑出能帮助目标任务的任务向量加上去。社区也逐渐建立了”文档”的规范:模型卡(Mitchell et al. 2019,第 2 讲 §3 讲了数据的文档)说明模型的用途、训练数据、评测、局限和伦理考虑。

8.2 “开放”是一个光谱

2022–2023 年的大模型,开放程度差别很大:

开放程度例子(2022–2023)你能做什么
只有 APIGPT-3、ChatGPT调用;不能看参数,不能微调(或只能用平台的微调服务)
申请后获取权重OPT-175B、LLaMA(2023 年 2 月,研究用途)研究用途下可以微调、分析
开放权重 + 使用限制BLOOM(BigScience RAIL 许可)下载、微调、部署,但不能用于许可列出的有害用途
权重 + 数据 + 代码 + 中间检查点Pythia(第 12 讲 §7)、BLOOM 的训练过程复现、研究训练动态

“像开源软件一样构建模型”需要的不只是开放权重:要能修改和合并,需要共享的 base model 和工具(§5、§7.1);要能审查贡献,需要测试和文档(§7.2、§8.1);要能真正”复现”,还需要数据和训练代码。

8.3 许可:开源软件的许可证为什么不够用

开源软件的许可证(MIT、Apache 2.0、GPL)关心的是代码能不能被复制、修改、再分发。模型多了几个新问题:

  • 使用限制:BigScience 的 RAIL(Responsible AI License)在允许自由使用的同时,列出了一些禁止的用途。这和传统开源”不能歧视使用领域”的原则有张力,引发了”这还算不算开源”的争论。
  • 衍生品:微调过的模型、合并出的模型、一个 LoRA 补丁,算不算”衍生作品”?许可证的条款是否传递给它们?
  • 训练数据的权利:模型是在受版权保护的数据上训练的,这对模型本身的许可意味着什么?(第 19 讲)

这些问题在 2023 年都没有定论;法律部分留到第 19 讲,这里只指出:技术上的可合并性和法律上的可合并性是两回事——两个许可证不兼容的模型,技术上能平均,法律上可能不能分发。


9. 2023 年之后 · Since 2023

  • 开放权重模型的爆发。 LLaMA 在 2023 年 2 月以研究许可发布,几周之内社区就在它上面做出了大量指令微调模型(例如 Stanford 的 Alpaca)。2023 年 7 月的 Llama 2 允许商用(附带条件),同年 9 月的 Mistral 7B 用 Apache 2.0 发布。此后 Qwen、DeepSeek 等系列持续发布开放权重的模型。Raffel 设想的”共享的 base model + 社区补丁”的生态,以开放权重模型为主干真实地出现了——虽然 base model 本身仍然主要由资源充足的机构一次性训练。
  • 合并成了日常实践。 开源工具 mergekit(Goddard et al. 2024)把任务算术、TIES、DARE(Yu et al. 2023:随机丢掉大部分分量再放大,和 TIES 的修剪思路相近)、球面插值等方法做成了配置文件;2024 年前后,开放排行榜上排名靠前的模型里有相当一部分是合并出来的。Sakana AI 的”进化式模型合并”(Akiba et al. 2024)用进化算法自动搜索合并配方。一些大模型的后训练流程也把多个检查点的平均作为一个步骤。
  • 从”合并”到”路由”。 与其把所有专门模型压成一个,不如保留它们、按输入选一个或几个来用:LoRA 的组合与检索、Branch-Train-Merge(Li et al. 2022)把领域专家训练好再合并或组合、Branch-Train-MiX(Sukhbaatar et al. 2024)把几个领域专家合成一个混合专家模型。Raffel 组的综述把这一类方法称为 “MoErging”(Yadav et al. 2024)。
  • 理解为什么能合并。 “权重解耦”(Ortiz-Jimenez et al. 2023,§3.4)、在切空间里微调让任务算术更可靠;对置换和更一般的对称性的研究也在继续。
  • “开源 AI”的定义之争。 开放源代码促进会(OSI)在 2024 年发布了”开源 AI 定义”,要求开放的不只是权重,还包括足够详细的训练数据信息和代码。很多”开放权重”的模型不满足这个定义。

哪些结论仍然成立? 任务向量与任务算术;共享的起点是合并的前提;干扰随任务数增加;“只交换参数、不交换数据”作为协作方式的价值。被修正或补充的:合并从研究走进了日常工具链,但 base model 的训练并没有像 Raffel 设想的那样变成社区持续更新的过程——社区协作主要发生在 base model 的下游(微调、合并、插件),而不是 base model 本身。


10. 小结 · Summary

  1. 问题:基础模型由少数机构一次性训练、发布后冻结;开源软件由社区持续地贡献、审查、合并。Raffel 主张把版本控制、合并、测试、版本号、模块化这些想法移植到模型上。
  2. 任务向量 τ=θft−θ0\tau = \theta_{\text{ft}} - \theta_0 是”一次微调的 diff”。它们很短(我们的模型里只有 ∥θ0∥\|\theta_0\| 的 1.4%–3.2%)、几乎两两正交(同组略相似)、能量集中,但能力不全在最大的分量里。
  3. 任务算术:相加得到多任务模型(我们的 10 对平均 0.851,CLIP 上 98.9%);取负去掉一种行为(我们的刻板印象偏斜 0.152 → 0.050,GPT-2 有害输出 4.8% → 0.8%);λ\lambda 在开发集上选,减太多会矫枉过正。
  4. 合并方法:简单平均被稀释、直接相加走得太远;任务算术、TIES、Fisher 各有道理。在我们的小模型上任务算术最好(5 个任务 0.814),TIES 和 Fisher 没有赢过它——和论文不同。任务越多越难合并;干扰来自”随机撞车”。合并的独特价值:不共享数据、不再训练、推理成本不变。
  5. 共享的起点:同一个预训练模型出发的微调在同一个盆地里(线性模式连通,屏障 = 0);置换对称性让独立训练的模型无法直接平均(屏障 5.845),按激活对齐只能消掉一部分(3.614);别的 base model 上做的补丁打不到我们的模型上。
  6. 便宜的补丁:稀疏(2% 的参数、17 KB 就能学会一个任务)、低秩、模块。但补丁小 ≠ 不弄坏别的功能(Fisher 掩码的遗忘严重得多);参数不重叠 ≠ 功能不重叠。
  7. 工具与流程:Git-Theta 按参数组做版本控制和自动合并;下游评测充当测试,回归测试守住向后兼容;ColD Fusion 式的迭代合并让社区逐轮改进一个共享模型;Petals 让社区一起托管一个大模型。
  8. 生态:开放是一个光谱;模型的许可比代码复杂(使用限制、衍生品、训练数据)。2023 年之后,开放权重和合并实践都爆发了,但社区协作主要发生在 base model 的下游。

11. 常见误区 · Common pitfalls

  1. “任务向量几乎正交,所以相加不会干扰。” 正交是参数空间里的说法;功能上的干扰来自共享的残差流和嵌入。我们的余弦都很小,但 parity + tens 相加只有 0.482(代码 01)。
  2. “合并就是平均。” 简单平均会把每个补丁稀释到 1/k1/k;5 个任务时几乎等于没合并(0.522)。需要 λ\lambda 这样的放大系数(代码 02)。
  3. “TIES / Fisher 一定比任务算术好。” 在论文的模型上通常如此;在我们的小模型上不是(0.596、0.598 vs 0.814)。方法的优劣依赖规模和任务,要在自己的设置上验证。
  4. “两个一样好的模型平均之后也一样好。” 只有在它们从同一个起点出发时才成立;两个独立训练的模型平均之后比乱猜还差(代码 03)。
  5. “补丁可以随便移植到别的模型上。” 任务向量只对它出发的那个 θ0\theta_0 有意义;换了 base model,补丁完全失效(代码 03 演示 4)。
  6. “只改 0.5% 的参数,就不会弄坏别的功能。” 改得少不等于改得安全:Fisher 掩码改的正是最敏感的参数,遗忘比全部参数微调严重得多(代码 04)。
  7. “稀疏补丁互不重叠,所以可以无冲突地合并。” 参数不重叠,功能仍然通过前向传播耦合(代码 04 演示 2)。
  8. “开放权重 = 开源。” 开放权重只是光谱上的一档;许可可能带使用限制,数据和训练代码可能不公开。
  9. “多任务训练总是更好,所以合并没有意义。” 多任务训练需要共享数据和重新训练;在数据不能共享的协作场景里,它根本不是一个选项。

12. 练习 · Exercises

练习 1(λ 的意义) 任务算术 θ=θ0+λ∑t=1kτt\theta = \theta_0 + \lambda\sum_{t=1}^{k}\tau_t。证明:λ=1/k\lambda = 1/k 时它就是 kk 个微调模型的简单平均。λ=1\lambda = 1 呢?

参考答案

1k∑tθt=1k∑t(θ0+τt)=θ0+1k∑tτt\frac{1}{k}\sum_t \theta_t = \frac{1}{k}\sum_t(\theta_0 + \tau_t) = \theta_0 + \frac{1}{k}\sum_t \tau_t,正是 λ=1/k\lambda = 1/k。λ=1\lambda = 1 是”把所有补丁都完整地打上去”:θ0+∑tτt\theta_0 + \sum_t \tau_t。如果补丁之间完全不干扰(线性 + 解耦),λ=1\lambda = 1 是理想的;干扰越大,最优的 λ\lambda 越小(代码 02 演示 2:5 个任务时选出 0.6)。

练习 2(为什么高维向量几乎正交) 两个独立的、各分量服从标准正态的 nn 维随机向量,余弦相似度的标准差大约是多少?用它解释代码 01 里”随机向量的余弦 +0.0031”。

参考答案

cos⁡=u⋅v∥u∥∥v∥\cos = \frac{u \cdot v}{\|u\|\|v\|}。u⋅v=∑iuiviu \cdot v = \sum_i u_i v_i 是 nn 个均值 0、方差 1 的独立项之和,标准差 n\sqrt{n};∥u∥∥v∥≈n\|u\|\|v\| \approx n。所以余弦的标准差约 1/n1/\sqrt{n}。n=109,056n = 109{,}056 时约 0.0030,代码 01 的 +0.0031 正好在一个标准差左右。任务向量之间的余弦(多数在 0.01–0.05)是这个尺度的几倍到十几倍,所以虽小,但不是噪声。

练习 3(Fisher 合并的推导) 两个模型的后验分别近似为 N(θ1,F1−1)N(\theta_1, F_1^{-1})、N(θ2,F2−1)N(\theta_2, F_2^{-1})(对角)。证明两者乘积的峰值是 θ∗=(F1θ1+F2θ2)/(F1+F2)\theta^* = (F_1\theta_1 + F_2\theta_2)/(F_1 + F_2)(逐元素)。如果某个参数 F1=0F_1 = 0、F2>0F_2 > 0,合并结果是什么?

参考答案

对数乘积 =−12F1(θ−θ1)2−12F2(θ−θ2)2+C= -\frac{1}{2}F_1(\theta - \theta_1)^2 - \frac{1}{2}F_2(\theta - \theta_2)^2 + C(逐元素)。对 θ\theta 求导令其为 0:F1(θ−θ1)+F2(θ−θ2)=0F_1(\theta - \theta_1) + F_2(\theta - \theta_2) = 0,得 θ∗=(F1θ1+F2θ2)/(F1+F2)\theta^* = (F_1\theta_1 + F_2\theta_2)/(F_1 + F_2)。F1=0F_1 = 0 时 θ∗=θ2\theta^* = \theta_2:模型 1 对这个参数”没有意见”,完全听模型 2 的。这就是 Fisher 合并比简单平均好的地方:每个参数由真正在乎它的模型决定。代码里的 merge_fisher 加了一个很小的 eps,避免两个 Fisher 都是 0 时除以 0。

练习 4(改代码:类比) 设计一个”类比”实验:在 food 任务的格式下,把答案换成 good / bad(而不是 great / awful),得到任务 A’;你能否用 τverdict\tau_{\text{verdict}}、τfood\tau_{\text{food}} 和别的向量组合出 A’ 的任务向量?写下你的设计和预期(不要求运行)。

参考答案

一种设计:A = food(great / awful),B = A’(good / bad,目标),C = 一个”用 great / awful 作答的评论任务”(例如把 verdict 的答案映射成 great / ok / awful),D = verdict(good / ok / bad)。“A 之于 B 如 C 之于 D”:答案词从 great / awful 换成 good / bad。于是 τA′≈τfood+(τD−τC)\tau_{A'} \approx \tau_{\text{food}} + (\tau_D - \tau_C)。预期:如果”换答案词”这个改动在两个任务里对应同一个权重方向,组合出的模型在 A’ 上会明显好于 base model;但在我们的字符级小模型里,答案词的改动集中在字符嵌入上,和格式的改动纠缠在一起,效果可能有限(具体以你运行的结果为准)。

练习 5(改一行代码,预测结果) 把 merge_lab.FT_CFG 的学习率从 1e-4 改成 1e-3(删掉 checkpoints/ 重新训练)。你预测任务向量的长度、两两相加的归一化准确率、合并后的验证集 bpc 会怎样变化?

参考答案

学习率大 10 倍,任务向量会长得多(离 θ0\theta_0 更远),线性近似变差:两两相加的归一化准确率应该下降,合并后的 bpc 会上升得多。写代码时我的原型用过 1e-3(另一组任务):单个任务的 bpc 已经到 1.5–3.9,四个任务相加后超过 7 bit/字符(README “关于实验结果” 第 2 条)。这就是”小步”前提的重要性(§3.4)。具体数字以你运行的结果为准。

练习 6(损失屏障) 代码 03 演示 1 里,parity ↔ tens 路径中点的 bpc(0.440)比两端(0.483、0.518)都低。用”离 base model 的距离”解释这个现象。如果把路径换成 θ0+(1−α)τA+ατB\theta_0 + (1-\alpha)\tau_A + \alpha\tau_B 之外的另一条路,比如 θ0+τA+ατB\theta_0 + \tau_A + \alpha\tau_B,你预测 bpc 曲线会怎样?

参考答案

中点是 θ0+12(τA+τB)\theta_0 + \frac{1}{2}(\tau_A + \tau_B),两个补丁各打一半,离 θ0\theta_0 比两端都近(τA\tau_A、τB\tau_B 几乎正交,中点到 θ0\theta_0 的距离约为端点的 1/21/\sqrt{2}),所以遗忘更少、bpc 更低。路径 θ0+τA+ατB\theta_0 + \tau_A + \alpha\tau_B 从”只打 A”走到”A、B 都完整地打上”(λ=1\lambda = 1 的相加),离 θ0\theta_0 越来越远,bpc 应该单调上升,终点就是代码 01 演示 3 里 parity + tens 的 0.723。

练习 7(置换的计数) TinyGPT 有多少种”函数不变的置换”(只算 §5.2 列出的几类)?取对数,和参数个数比较。

参考答案

残差流 64!;每层 MLP 256!,两层 (256!)2(256!)^2;每层头的顺序 4!,两层 (4!)2(4!)^2;每个头内部 q / k 的 16! 和 v 的 16!,共 8 个头,(16!)16(16!)^{16}。总数 64!⋅(256!)2⋅(4!)2⋅(16!)1664! \cdot (256!)^2 \cdot (4!)^2 \cdot (16!)^{16}。log⁡10\log_{10}:64!≈108964! \approx 10^{89},256!≈10507256! \approx 10^{507},16!≈1013.316! \approx 10^{13.3},合计约 89+1014+2.8+213≈131989 + 1014 + 2.8 + 213 \approx 1319,即约 10131910^{1319} 种抄本。参数空间里同一个函数有天文数字般多的”副本”,随机取两个独立训练的模型,它们恰好”对齐”的可能性可以忽略——这就是为什么需要对齐算法。

练习 8(概念:为什么从同一个起点出发就能平均) Neyshabur et al. 说从同一个预训练模型出发的微调留在同一个盆地里。用 §2.3 的”任务向量很短”和 §5.2 的”置换对称性”两个事实,给出一个直觉解释。

参考答案

微调只在 θ0\theta_0 附近走一小步(1%–3% 的长度),走不出 θ0\theta_0 所在的盆地;而且这一小步改变不了”第 ii 个神经元在做什么”——每个隐藏单元的角色由预训练决定,微调只是微调它。所以两个微调模型的第 ii 个神经元仍然在做同一件事,隐藏单元自动对齐,平均才有意义。两个独立训练的模型则各自选了 10131910^{1319} 种排列中的一种(练习 7),而且还可能在”连续”对称性上不同。

练习 9(概念:回归测试的阈值) 代码 04 演示 3 用”bpc ≤ base 的 1.1 倍”做回归测试,第 4 轮起不通过。如果你是 CLAMP 的维护者,你会怎样设计一套更好的测试?至少说出三类测试,以及每类测试的成本。

参考答案

(1)通用能力:原验证集的 bpc / 困惑度(便宜,一次前向);(2)已有任务的回归:每个之前合并进来的任务,在它的测试集上不能掉超过某个幅度(中等,需要每个任务的评测集;如果评测需要微调就很贵);(3)行为与安全测试:偏见(例如代码 01 的代词偏斜)、有害输出、隐私(第 16 讲的 canary)(中等到贵);(4)接口兼容:输入输出格式、分词器不变(便宜)。成本最高的是需要”再微调才能评测”的下游任务,所以实践中会用少样本提示或线性探针来代替完整微调。

练习 10(概念:开放程度) 一个模型公开了权重(带 RAIL 许可),但没有公开训练数据和训练代码。对于”像开源软件一样构建模型”的目标,它满足了哪些要求、缺了哪些?

参考答案

满足:可以下载、微调、合并(有共享的 base model,§5.4);可以分享补丁(任务向量、LoRA)。缺少:无法复现或审查训练过程(不知道数据里有什么、有没有测试集泄漏,第 2 讲);无法做”主干上的修改”(例如修复预训练数据里的问题后重新训练);许可的使用限制可能和其他补丁的许可冲突,影响合并后的再分发。它更像”只发布了编译好的二进制、但允许打补丁”的软件。


13. 延伸阅读 · Further reading

原课相关(2/8 Building ML Models like Open-Source Software)

  • Raffel. A Call to Build Models Like We Build Open-Source Software. 2021(博客)。colinraffel.com/blog
  • Matena & Raffel. Merging Models with Fisher-Weighted Averaging. NeurIPS 2022. arXiv:2111.09832
  • Sung, Nair & Raffel. Training Neural Networks with Fixed Sparse Masks. NeurIPS 2021. arXiv:2111.09839
  • Yadav, Tam, Choshen, Raffel & Bansal. TIES-Merging: Resolving Interference When Merging Models. NeurIPS 2023. arXiv:2306.01708
  • Kandpal et al. Git-Theta: A Git Extension for Collaborative Development of Machine Learning Models. ICML 2023. arXiv:2306.04529
  • Don-Yehiya et al. ColD Fusion: Collaborative Descent for Distributed Multitask Finetuning. ACL 2023. arXiv:2212.01378
  • Borzunov et al. Petals: Collaborative Inference and Fine-tuning of Large Models. ACL 2023(System Demonstrations). arXiv:2209.01188

任务算术、合并与线性模式连通性

  • Ilharco et al. Editing Models with Task Arithmetic. ICLR 2023. arXiv:2212.04089
  • Wortsman et al. Model soups: averaging weights of multiple fine-tuned models improves accuracy without increasing inference time. ICML 2022. arXiv:2203.05482
  • Frankle et al. Linear Mode Connectivity and the Lottery Ticket Hypothesis. ICML 2020. arXiv:1912.05671
  • Neyshabur, Sedghi & Zhang. What is being transferred in transfer learning? NeurIPS 2020. arXiv:2008.11687
  • Entezari et al. The Role of Permutation Invariance in Linear Mode Connectivity of Neural Networks. ICLR 2022. arXiv:2110.06296
  • Ainsworth, Hayase & Srinivasa. Git Re-Basin: Merging Models modulo Permutation Symmetries. ICLR 2023. arXiv:2209.04836

2023 年之后

  • Ortiz-Jimenez, Favero & Frossard. Task Arithmetic in the Tangent Space: Improved Editing of Pre-Trained Models. NeurIPS 2023. arXiv:2305.12827
  • Yu et al. Language Models are Super Mario: Absorbing Abilities from Homologous Models as a Free Lunch(DARE). 2023. arXiv:2311.03099
  • Goddard et al. Arcee’s MergeKit: A Toolkit for Merging Large Language Models. 2024. arXiv:2403.13257
  • Yadav et al. A Survey on Model MoErging: Recycling and Routing Among Specialized Experts for Collaborative Learning. 2024. arXiv:2408.07057

14. 下一讲预告 · Next lecture

到这里,我们讨论的基础模型都只处理一种东西:文本。第 14 讲进入 多模态基础模型:CLIP 用 4 亿对图文做对比学习,学到一个图像和文本共享的表示空间,于是可以用一句”a photo of a dog”做零样本分类;DALL·E、扩散模型、Imagen 反过来从文本生成图像;Flamingo 把一个冻结的语言模型和一个冻结的视觉编码器接起来,做少样本的图文问答;Gato 和 VPT 则把”行动”也当成 token。代码会在一个合成的”图文配对”世界里从零做 CLIP 式的对比学习和零样本分类,在二维数据上实现带 classifier-free guidance 的扩散模型,并用 VPT 的”逆动力学模型”给没有标注的视频打伪标签。

← 返回课程目录