缘起
一条规则可以服务许多事实
我想弄清楚,不经过大规模训练,只靠直接编辑权重、嫁接新部件,一个开源小模型能朝前沿水平推进多远。训练是常规做法:把数十亿组问答灌进模型,让梯度下降自己找出规则。但我们也可以打开模型,读出它在解题过程中掌握的信息,并加以修改。那么,其中一些规则能不能手工写进去?
我目前的理论是,很多推理靠的是规则集:一条规则服务许多事实。进位是最简单的例子。没人会把每个加法结果都背下来,因为一条进位规则就能覆盖全部,而且在每一个数位上都适用。我在笔记里是这样写的:
也许从一个事实得出的规则,会与另一个事实的规则相吻合。
对小模型来说,存储事实很昂贵,规则却很便宜。本文的第一个乘法电路是为从 2 × 10 到 9 × 99 的所有题目构建的,共 25,270 个权重。
同一批对话还引出了第二个想法,正好和第一个配对:一套用于学会新东西的学习规则集,加上一套用于运用已有知识的逻辑规则。本文讲的是逻辑这一边。
模型已经有什么
数字就在里面
动手之前,我们先看了看 SmolLM2 在开始写答案的那一刻掌握着什么。在答案位置对隐藏状态做线性读取,用最小二乘拟合,几乎可以完美地读出两个数的各位数字。三位数加法只用一层不够,但把第 14、20、21 层合起来,在新题目上每一位数字的读取准确率都达到 99%。
三位数加法。在新题目上,线性读取把两个数的全部六位数字都读对的比例:先逐层读取,再把第 14、20、21 层合起来读取。每个读取都用 1,500 道题拟合。
76.6% 最佳单层,第 21 层
11.8% 最后一层,第 29 层
97.9% 第 14、20、21 层合并;若该读取用 3,000 道题拟合,则为 99.2%
全部 30 层的表格
| 层 | 六位数字全对 |
|---|---|
| 0 | 0.0% |
| 1 | 4.3% |
| 2 | 26.0% |
| 3 | 50.1% |
| 4 | 52.2% |
| 5 | 47.1% |
| 6 | 31.4% |
| 7 | 20.4% |
| 8 | 18.8% |
| 9 | 18.2% |
| 10 | 27.1% |
| 11 | 27.9% |
| 12 | 39.1% |
| 13 | 47.2% |
| 14 | 74.8% |
| 15 | 70.1% |
| 16 | 64.0% |
| 17 | 63.0% |
| 18 | 64.2% |
| 19 | 63.5% |
| 20 | 72.9% |
| 21 | 76.6% |
| 22 | 71.5% |
| 23 | 62.9% |
| 24 | 48.8% |
| 25 | 30.5% |
| 26 | 19.0% |
| 27 | 13.9% |
| 28 | 11.7% |
| 29 | 11.8% |
所以模型已经读懂了题目。它做不稳的是把这些数字组合起来:进位、借位、乘法表。这一步组合,就是我们补上的部分。
嫁接如何工作
四个部件,都不用梯度下降训练
- 读取对两个数的每一位数字各做一次线性读取,取自模型自己的隐藏状态。
- 规则答案的每一位数字各有一张小查找表。个位由两个个位数字决定;十位由两个十位数字加上进位决定,依此类推。
- 写入在模型最后一层施加一个推动,偏向正确的数字词元。回答仍然由模型自己写,嫁接只是左右下一个出现的是哪个数字。
- 开关两个拟合出来的开关决定嫁接何时开启。它们被拟合成只在模型写这类题目的答案时触发,而在其他数学题、应用题、代码和普通文本上保持关闭。
读取和开关都用闭式最小二乘拟合,规则表由程序写出(第一个乘法嫁接是手写的)。基础模型文件从不改动,每个嫁接都是附加在它旁边的。
发展过程
从一个嫁接到一条流水线
- 神经元嫁接第一次尝试是在最后一层加了 24 个新神经元,为两位数加法提供进位。它在封存题上站住了,+19.7 个百分点。它的作用方式是轻推模型自己的算术,所以下一步是干脆直接做算术。
- 手工接线电路乘法这边,我们手写规则表,并把模型自己的数字读取喂给它们。第一版在封存的留出题上得分 83.5%,原本是 21.3%。第二版改进了开关,得分 83.1%,它在其他数学题上最差的变化是 −0.75 个百分点。
- 程序找出的规则一个规则搜索程序拿到了 340 道已解出的题目,形式是数字和答案。对答案的每一位,它寻找能决定这一位的最小输入组合。它自己找出了加法的进位、减法的借位和乘法的进位链。它还发现,十位就是把个位规则用在十位数字上,再加一步进位:一条规则服务多个数位,这正是这一切的出发点。它的乘法电路在留出题上得分 82.7%,与手工接线的版本相差不到半个百分点。
- 同一条流水线,新技能减法和两位数加法走的是同一个脚本,设置照搬乘法,没有调参。三位数加法需要三处修正,每一处都在打开其留出集之前测试过:从三层合并的读取、第二个开关,以及按数位分别设定的推动强度。
为什么行得通
程序为什么能找到规则
规则不是模型想出来的。是一个独立的程序在模型之外,从 340 个已解出的例子中找到的。模型负责提供数字,嫁接从它的隐藏状态里读取这些数字。有四件事让这成为可能。
- 把题目拆成一位一位的数字作为整数看,7 × 48 = 336 只是 720 道可能题目中的一个事实。拆成数位后,规律就出现了:答案的最后一位只取决于 7 和 8。这是我们做出的最重要的选择,也正好适合 SmolLM2,因为它把每个数字存成单独的词元。
- 寻找最简单的解释对答案的每一位,程序会寻找总能决定它的最小一组输入数位,并且只有当规则在每个例子上都成立时才保留。基于两位数字的规则大约有 100 个可能的条目,340 个例子几乎能把它们全部填满,所以它在重复使用同样数对的新题目上也能用。死记硬背则要用上每一个输入数位,组合数量远远超出 340 个例子能覆盖的范围。
- 复用已经找到的规则对更高的一位,它再次尝试个位规则,并检查是否有一个额外信号能解释剩下的部分。这个信号原来就是进位。没有人告诉程序这个词;它找到这个变量,是因为数据需要一个变量。对百位重复同样的步骤,就得到了进位链。
- 在没见过的题目上检验这些规则先在它们没见过的题目上测试,然后编译成嫁接内部的查找表。
梯度训练没有内置的、偏向最简单解释的倾向,例子一少它就死记硬背。在乘法上,同规模的梯度嫁接在自己的训练题上得分 72.7%,在 140 道开发集题目上只有 25.1%。电路在用来构建它的题目上得分 94.7%,在同样的开发集题目上得分 88.7%。
按数位拆分的框架是我们定的,程序在这个框架里找规则。迈向算术以外技能的下一步,是让程序自己也找到拆分问题的正确方式。
结果
五项技能,在没人调过的题目上
每项技能的留出题都在动手构建之前就分出来了,没有在上面调过任何东西。每个分数都是两个随机种子、每题四次运行,模型单独运行和加上嫁接时使用相同的采样设置。乘法题集为第一个手工接线版本打开过一次,又为最后两个版本打开过一次。
完全答对的题目比例。乘法 140 题,其余各 500 题。
- 仅 SmolLM2
- 加上嫁接
- 同一嫁接,接线打乱
| 技能 | 题数 | 仅 SmolLM2 | 加上嫁接 | 提升,百分点(95% 下界) | 打乱接线 | 新增权重 |
|---|---|---|---|---|---|---|
| 乘法,规则手工接线7 × 48 | 140 | 21.3% | 83.1% | +61.8 (+56.1) | 2.4% | 25,2700.019% |
| 乘法,规则由程序找出7 × 48 | 140 | 21.3% | 82.7% | +61.3 (+55.5) | 6.3% | 32,5190.024% |
| 两位数减法83 − 27 | 500 | 11.3% | 68.3% | +57.1 (+54.0) | 7.8% | 32,4460.024% |
| 两位数加法38 + 47 | 500 | 49.7% | 86.5% | +36.8 (+33.8) | 16.7% | 32,4760.024% |
| 三位数加法347 + 285 | 500 | 26.1% | 83.7% | +57.6 (+54.6) | 21.4% | 121,1980.090% |
- 没有改动任何原始权重,也没有梯度训练。每个嫁接新增 25,270 到 121,198 个权重,占模型的 0.019% 到 0.090%。我们给自己定的上限是 0.1%。
- 打乱对照是同一个嫁接,只是接线被打乱。它在每项技能上的得分都不高于模型单独运行,在 2.4% 到 21.4% 之间,所以没有正确的规则,多出来的权重什么也做不了。
- 三个两位数嫁接(乘法、减法和加法)也能在同一个模型里同时运行,总共 92,496 个权重。每一个在开发集题目上都保住了提升。在其他数学题上最差的变化是 −0.25 个百分点,200 条无关提示中有 200 条给出的答案和之前相同。
提升按题目配对计算,使用单侧 95% bootstrap 下界。副作用检查覆盖另外八个算术和应用题题集,以及 GSM8K 和代码。无关提示是 200 条 IFEval 提示,用贪心解码作答。每次运行的规则和预测都在出结果之前写下。
对比梯度训练
三场正面比较
梯度训练是添加技能的标准方式,所以我们把它当作对手。每个梯度嫁接在测试前都在一组设置网格上调过参。
140 题,2 个种子,每题 4 次运行。第一个手工接线电路对比两个调过参的梯度训练嫁接。
电路分别领先 57.9 和 59.8 个百分点。
140 道开发集题目,2 个种子,每题 4 次运行。LoRA 用的是网格搜索中最好的设置。
领先 49.3 个百分点,95% 下界为 +42.8,用的权重不到四分之一。
500 道开发集题目,2 个种子,每题 4 次运行。电路的设置照搬乘法,没有调参。
与一个大四倍的梯度嫁接打平,比 14 个神经元的那个高出约 17 个百分点。
电路的减法规则来自 340 道题。梯度嫁接用 1,500 道题训练,正是电路拟合数字读取时用的同样 1,500 道。
仍在进行
从更大的模型移植
我们也在尝试把技能从更大的模型迁移过来。在我们的检查中,Qwen3.5-4B 在这批乘法题上 300 道答对了 300 道,所以我们尝试把它的一组神经元转换到 SmolLM2 里。另外,我们在小学应用题上把 Qwen3.5-4B 的一个修正信号映射到了 Qwen3.5-0.8B。两者都还没成功,也都还没有结论。到目前为止,大模型最有用的地方是作为向导,指出小模型在哪里出错。
嫁接是什么
由模型自己的读取来供数的计算器
算术由嫁接自己完成,用的是模型本来就掌握的数字。SmolLM2 并没有学会乘法。嫁接处理的是问题的简单形式(“347 + 285 等于多少?”),而开关会有意让它在应用题上保持关闭。到目前为止,这一切都只在一个模型上做过。
这个方法可以迁移到新技能上。规则由程序从 340 个例子中找出,编译成表,每项技能都用相同的设置放入。下一步是学习这一侧:让 SmolLM2 用上提示中给它的事实。