lf.wtf
实验报告 01

AI 研究 · SmolLM2-135M

电路嫁接

与 Claude Opus 5.5 合作撰写和制作

我们把一个小电路嫁接进一个 135M 模型的神经元里,没有用梯度训练。模型是 SmolLM2-135M,它原有的权重一个都没变。在封存的留出题上,它的乘法得分从 21.3% 提高到 83.1%。

嫁接在 SmolLM2-135M 中的位置文本以词元形式进入,经过 30 层神经元,再由输出头选出下一个词元。嫁接是加在这些层旁边的一个小电路。它读取第 20 层,并把它的推动加到最后一层,也就是第 29 层,就在输出头之前。原始神经元保持不变。014202129第 0 层到第 29 层。圆点是每一层的抽样,未按比例绘制。输入词元输出头下一个词元嫁接25,270 到 121,198个新增权重,不到 0.1%132原始神经元,保持不变嫁接,加在各层旁边 嫁接在 SmolLM2-135M 中的位置文本以词元形式进入,经过 30 层神经元,再由输出头选出下一个词元。嫁接是加在这些层旁边的一个小电路。它读取第 20 层,并把它的推动加到最后一层,也就是第 29 层,就在输出头之前。原始神经元保持不变。输入词元014202129输出头下一个词元嫁接25,270 到 121,198个新增权重,不到 0.1%132原始神经元,保持不变嫁接,加在各层旁边圆点是每一层的抽样,未按比例绘制。
嫁接的位置。SmolLM2-135M 有 30 层,每层是一个注意力块(9 个头)后接一个 MLP 块,处理的隐藏状态宽 576 个数,总共约 134.5M 个权重。嫁接是加在这些层旁边的一个小电路,就像一条旁路。原始神经元和权重都没有任何改动。
  1. 读取和两个开关都在第 20 层 MLP 的输入处接入隐藏状态,位置是模型即将写下下一位答案数字的地方。
  2. 嫁接的推动加到第 29 层(最后一层)MLP 的输出上,位置相同,就在输出头选出下一个词元之前。
  3. 对于三位数加法,读取还会合并第 14 层和第 21 层(总共 1,728 个数)。写入仍然在最后一层进行。
乘法,留出题
21.3% → 83.1%
改动的原始权重
0
最大嫁接占模型比例
0.09%
比同规模梯度嫁接高出的百分点
+57.9

缘起

一条规则可以服务许多事实

我想弄清楚,不经过大规模训练,只靠直接编辑权重、嫁接新部件,一个开源小模型能朝前沿水平推进多远。训练是常规做法:把数十亿组问答灌进模型,让梯度下降自己找出规则。但我们也可以打开模型,读出它在解题过程中掌握的信息,并加以修改。那么,其中一些规则能不能手工写进去?

我目前的理论是,很多推理靠的是规则集:一条规则服务许多事实。进位是最简单的例子。没人会把每个加法结果都背下来,因为一条进位规则就能覆盖全部,而且在每一个数位上都适用。我在笔记里是这样写的:

也许从一个事实得出的规则,会与另一个事实的规则相吻合。

对小模型来说,存储事实很昂贵,规则却很便宜。本文的第一个乘法电路是为从 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 层的表格
层六位数字全对
00.0%
14.3%
226.0%
350.1%
452.2%
547.1%
631.4%
720.4%
818.8%
918.2%
1027.1%
1127.9%
1239.1%
1347.2%
1474.8%
1570.1%
1664.0%
1763.0%
1864.2%
1963.5%
2072.9%
2176.6%
2271.5%
2362.9%
2448.8%
2530.5%
2619.0%
2713.9%
2811.7%
2911.8%

所以模型已经读懂了题目。它做不稳的是把这些数字组合起来:进位、借位、乘法表。这一步组合,就是我们补上的部分。

嫁接如何工作

四个部件,都不用梯度下降训练

  1. 读取对两个数的每一位数字各做一次线性读取,取自模型自己的隐藏状态。
  2. 规则答案的每一位数字各有一张小查找表。个位由两个个位数字决定;十位由两个十位数字加上进位决定,依此类推。
  3. 写入在模型最后一层施加一个推动,偏向正确的数字词元。回答仍然由模型自己写,嫁接只是左右下一个出现的是哪个数字。
  4. 开关两个拟合出来的开关决定嫁接何时开启。它们被拟合成只在模型写这类题目的答案时触发,而在其他数学题、应用题、代码和普通文本上保持关闭。

读取和开关都用闭式最小二乘拟合,规则表由程序写出(第一个乘法嫁接是手写的)。基础模型文件从不改动,每个嫁接都是附加在它旁边的。

嫁接内部:以 38 + 47 为例读取数字,套用规则,推动正确的数字,再由两个开关决定推动何时开启。推动加在第 29 层,模型写出 85。1读取第一个数:3, 8第二个数:4, 7剩余答案位数:22规则个位:8 + 7 = 15写 5,进位 1十位:3 + 4 + 1 = 83写入答案第一位:推动 8答案下一位:推动 54开关推动 × 开关 × 开关开:写这个答案时关:其他文本加在第 29 层。模型写出 85。 嫁接内部:以 38 + 47 为例读取数字,套用规则,推动正确的数字,再由两个开关决定推动何时开启。推动加在第 29 层,模型写出 85。1读取第一个数:3, 8第二个数:4, 7剩余答案位数:22规则个位:8 + 7 = 15写 5,进位 1十位:3 + 4 + 1 = 83写入答案第一位:推动 8答案下一位:推动 54开关推动 × 开关 × 开关开:写这个答案时关:其他文本加在第 29 层。模型写出 85。
嫁接内部,以 38 + 47 为例说明。读取找到 3、8、4 和 7,以及还剩两位答案数字。规则给出 8 + 7 = 15,所以个位是 5,进位 1;十位是 3 + 4 + 1 = 8。在第一个答案位置,嫁接推动 8;在下一个位置,推动 5。推动按每个数位分别缩放。两个开关与推动相乘,所以只有在模型写这类题目的答案时,推动才会开启。回答仍然由模型自己写。

发展过程

从一个嫁接到一条流水线

  1. 神经元嫁接第一次尝试是在最后一层加了 24 个新神经元,为两位数加法提供进位。它在封存题上站住了,+19.7 个百分点。它的作用方式是轻推模型自己的算术,所以下一步是干脆直接做算术。
  2. 手工接线电路乘法这边,我们手写规则表,并把模型自己的数字读取喂给它们。第一版在封存的留出题上得分 83.5%,原本是 21.3%。第二版改进了开关,得分 83.1%,它在其他数学题上最差的变化是 −0.75 个百分点。
  3. 程序找出的规则一个规则搜索程序拿到了 340 道已解出的题目,形式是数字和答案。对答案的每一位,它寻找能决定这一位的最小输入组合。它自己找出了加法的进位、减法的借位和乘法的进位链。它还发现,十位就是把个位规则用在十位数字上,再加一步进位:一条规则服务多个数位,这正是这一切的出发点。它的乘法电路在留出题上得分 82.7%,与手工接线的版本相差不到半个百分点。
  4. 同一条流水线,新技能减法和两位数加法走的是同一个脚本,设置照搬乘法,没有调参。三位数加法需要三处修正,每一处都在打开其留出集之前测试过:从三层合并的读取、第二个开关,以及按数位分别设定的推动强度。
规则搜索程序如何工作输入 340 道已解出的题目。对答案的每一位,程序找出能决定这一位的最小输入组合,发现十位沿用个位规则再加一个进位,并把这些规则编译成查找表。340 道已解出的题目38 + 47 = 8516 + 29 = 4552 + 31 = 83...答案每一位的最小输入个位 ← 两个个位数字十位 ← 两个十位数字+ 个位的进位个位规则,在高一位上复用查找表8, 7 → 5, 进位 16, 9 → 5, 进位 12, 1 → 3, 进位 0... 规则搜索程序如何工作输入 340 道已解出的题目。对答案的每一位,程序找出能决定这一位的最小输入组合,发现十位沿用个位规则再加一个进位,并把这些规则编译成查找表。340 道已解出的题目38 + 47 = 8516 + 29 = 4552 + 31 = 83...答案每一位的最小输入个位 ← 两个个位数字十位 ← 两个十位数字+ 个位的进位个位规则,在高一位上复用查找表8, 7 → 5, 进位 16, 9 → 5, 进位 12, 1 → 3, 进位 0...
规则搜索程序。它拿到 340 道已解出的题目,形式是数字和答案。对答案的每一位,它找出能决定这一位的最小输入组合:个位只取决于两个个位数字。它发现十位沿用个位规则再加一个进位,并把这些规则编译成查找表。示例行仅作说明。

为什么行得通

程序为什么能找到规则

规则不是模型想出来的。是一个独立的程序在模型之外,从 340 个已解出的例子中找到的。模型负责提供数字,嫁接从它的隐藏状态里读取这些数字。有四件事让这成为可能。

  1. 把题目拆成一位一位的数字作为整数看,7 × 48 = 336 只是 720 道可能题目中的一个事实。拆成数位后,规律就出现了:答案的最后一位只取决于 7 和 8。这是我们做出的最重要的选择,也正好适合 SmolLM2,因为它把每个数字存成单独的词元。
  2. 寻找最简单的解释对答案的每一位,程序会寻找总能决定它的最小一组输入数位,并且只有当规则在每个例子上都成立时才保留。基于两位数字的规则大约有 100 个可能的条目,340 个例子几乎能把它们全部填满,所以它在重复使用同样数对的新题目上也能用。死记硬背则要用上每一个输入数位,组合数量远远超出 340 个例子能覆盖的范围。
  3. 复用已经找到的规则对更高的一位,它再次尝试个位规则,并检查是否有一个额外信号能解释剩下的部分。这个信号原来就是进位。没有人告诉程序这个词;它找到这个变量,是因为数据需要一个变量。对百位重复同样的步骤,就得到了进位链。
  4. 在没见过的题目上检验这些规则先在它们没见过的题目上测试,然后编译成嫁接内部的查找表。

梯度训练没有内置的、偏向最简单解释的倾向,例子一少它就死记硬背。在乘法上,同规模的梯度嫁接在自己的训练题上得分 72.7%,在 140 道开发集题目上只有 25.1%。电路在用来构建它的题目上得分 94.7%,在同样的开发集题目上得分 88.7%。

按数位拆分的框架是我们定的,程序在这个框架里找规则。迈向算术以外技能的下一步,是让程序自己也找到拆分问题的正确方式。

结果

五项技能,在没人调过的题目上

每项技能的留出题都在动手构建之前就分出来了,没有在上面调过任何东西。每个分数都是两个随机种子、每题四次运行,模型单独运行和加上嫁接时使用相同的采样设置。乘法题集为第一个手工接线版本打开过一次,又为最后两个版本打开过一次。

留出题准确率,SmolLM2-135M

完全答对的题目比例。乘法 140 题,其余各 500 题。

  • 仅 SmolLM2
  • 加上嫁接
  • 同一嫁接,接线打乱
乘法7 × 48,规则手工接线
21.3% → 83.1%+61.8
乘法7 × 48,规则由程序找出
21.3% → 82.7%+61.3
两位数减法83 − 27,规则由程序找出
11.3% → 68.3%+57.1
两位数加法38 + 47,规则由程序找出
49.7% → 86.5%+36.8
三位数加法347 + 285,规则由程序找出
26.1% → 83.7%+57.6
同一结果的表格形式
技能题数仅 SmolLM2加上嫁接提升,百分点(95% 下界)打乱接线新增权重
乘法,规则手工接线7 × 4814021.3%83.1%+61.8 (+56.1)2.4%25,2700.019%
乘法,规则由程序找出7 × 4814021.3%82.7%+61.3 (+55.5)6.3%32,5190.024%
两位数减法83 − 2750011.3%68.3%+57.1 (+54.0)7.8%32,4460.024%
两位数加法38 + 4750049.7%86.5%+36.8 (+33.8)16.7%32,4760.024%
三位数加法347 + 28550026.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 次运行。第一个手工接线电路对比两个调过参的梯度训练嫁接。

仅 SmolLM2
21.3%
梯度嫁接,同规模14 个神经元
25.6%
0.1% 上限的梯度嫁接77 个神经元
23.7%
电路嫁接25,270 个权重,无梯度
83.5%

电路分别领先 57.9 和 59.8 个百分点。

乘法:对比 LoRA 微调

140 道开发集题目,2 个种子,每题 4 次运行。LoRA 用的是网格搜索中最好的设置。

仅 SmolLM2
28.5%
LoRA 微调115,200 个训练权重
39.4%
电路嫁接25,270 个权重,无梯度
88.7%

领先 49.3 个百分点,95% 下界为 +42.8,用的权重不到四分之一。

两位数减法

500 道开发集题目,2 个种子,每题 4 次运行。电路的设置照搬乘法,没有调参。

仅 SmolLM2
11.35%
梯度嫁接,14 个神经元
48.15%
梯度嫁接,77 个神经元133,056 个权重,已调参
65.0%
电路嫁接32,446 个权重,未调参
64.98%

与一个大四倍的梯度嫁接打平,比 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 用上提示中给它的事实。