前几天麻省理工的权重空间编程:大模型开始直接读写神经网络权重 刷屏,昨天看到国外的研究者的一个帖子,感觉有点意思,就找来原文,以下是全文翻译。
上周,MIT CSAIL 发布了《Sparks of Weight-Space Programming》。把一个小型网络的原始权重交给 GPT-6 Astra,它就能判断这个网络被训练来玩哪款游戏。只给出游戏规则,它还能从零写出小型网络的权重,击败经过1亿步 PPO 训练的网络。不用梯度,也没有训练循环,只靠推理理解这些权重究竟在计算什么。他们将其称为权重空间编程。
在另一项实验中,他们要求 Astra 直接编辑 Qwen3.5-0.8B 的权重,提高它的数学能力。Astra 只改动了0.016%的权重,MATH-500 准确率就从7.9%跃升至48.1%。〔1〕
研究 Astra 的修改时,我发现,它的大部分搜索其实是在用试错的方式求解一个线性规划问题。为了决定改哪些权重、各改多少,它花了约8小时,做了28次前向计算。几次线性规划求解,每次约一分钟,就能完成同样的工作,而且改动的权重数量只有原来的八分之一。本文会逐步介绍这次修改,解释它为什么是一个线性规划问题,以及这让我们对修复本身有了什么认识。

思考循环
小型推理模型有一种常见故障:陷入思考循环,一遍遍重复相同的几行文字,始终无法完成推理。Qwen3.5-0.8B 尤其容易出现这个问题,模型卡也明确提到了这一点。在 MATH-500 上,基础模型有93%的回答一直生成到32,768个 token 的上限,仍然没有给出答案。
这次修改修复了循环,因此只要任务中会出现循环,它就能提供帮助。这也包括指令遵循:IFEval 从21%提高到了62%。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
对于很少陷入循环的 Gemma 3 270M,Astra 没能显著改善它的数学能力。正如论文作者所说,编辑权重可以移除遮蔽已有能力的障碍,却无法增添模型本来没有的能力。
Astra 怎样修复循环
Transformer 使用残差连接:每一层的输出会加到输入上,而不是替换输入。沿着一个 token 穿过整个网络,就会得到一个由各层不断累加的向量,这就是残差流,在这个模型中是1,024维。模型陷入循环时,残差流会朝一个稳定的方向偏移。Astra 的修复办法是修改其中两层,让它们加到残差流中的内容朝相反方向移动。
找到循环方向
Astra 写了四组短文本,每组包括一段正常推进的推理,以及同一段推理陷入循环后的版本。它把这八段文本输入模型,记录某一层每个 token 的残差流向量。对循环文本取平均,得到一个向量;对正常推理文本取平均,得到另一个向量。将两者相减并归一化,就得到了循环方向 u,也就是模型陷入循环时残差流移动的方向。
编辑权重
每个 MLP 的末尾都有一个矩阵 W,叫作 down_proj。对于每个 token,它将 MLP 的3,584维隐藏激活 h 变换成1,024维的输出 Wh,再加到残差流 x 上:
x ← x + Wh
Wh = Σⱼ hⱼ wⱼ
因此,MLP 的输出始终是同一组3,584个向量的加权和,这些向量就是 W 的列向量 wⱼ。W 决定这些向量是什么,激活值 hⱼ 则决定在每个 token 处,各个向量要加上多少。
每个列向量都有一部分指向 u。这个分量 uᵀwⱼ 表示:只要 hⱼ 处于激活状态,第 j 列会在多大程度上将模型推向循环。Astra 的修改是将这个分量减去 α 倍:
wⱼ → wⱼ − α (uᵀwⱼ) u
当 α = 1 时,沿 u 的分量被完全移除,这一列也就不再推动模型进入循环。在第15层和第19层的所有列上做这件事,GSM8K 和 MATH 训练题上的准确率从9%提高到了33%。当 α = 6 时,这个分量会反向:原先沿 u 写入 +a 的列,现在写入 −5a,每次被使用时都会将模型推离循环。
每个被编辑的列,变化量都是同一个向量 u 的倍数。因此,整次修改只需要一个方向 u,以及每列对应的一个数值 vⱼ = −α(uᵀwⱼ);未修改的列对应数值为零。这是一个秩为1的改动:
ΔW = u vᵀ
对于每个 token,它会向残差流加入 (v · hₜ)u,也就是沿 u 方向的一定改变量,大小由被编辑列的激活程度决定。其他方向都不变。
剩下要选择的就是 v:改哪些列,α 应该取多大。Astra 通过试错确定,每次修改后都换一批训练题进行测试:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
比 α = 1 更强的推动带来了更大改善,随机方向则没有效果。最终修改在每层只选择64列,也就是在循环文本上沿 u 写入最多的那些列,并使用 α = 6。
时间花在了哪里
论文公开了 Astra 的完整日志,包括每次工具调用、每次前向计算,以及它写给自己的每条笔记。Astra 有一个已经加载模型权重的 Python 内核,以及50次前向计算的预算。每次前向计算,都是将它自行选择的一批训练题或文本输入模型。
修改方案在约10小时后冻结,最后17次前向计算只是重复测试,没有再作任何改动。〔2〕在这10小时中,找到 u 花了15分钟,找到 v 则花了约8小时。
选择 v 就是一个线性规划问题
Astra 要寻找一个 v,让循环中的 token 大幅偏离 u,同时尽量不影响正常推理的 token。由于在 token t 处的推动量是 v · hₜ,它对 v 是线性的,因此这两个要求都能写成线性不等式。
我从基础模型自己的生成轨迹中标注 token:已经深陷循环的 token 记为 R,远在循环开始之前、仍在正常推进推理的 token 记为 P,〔3〕并记录它们在第15层和第19层的激活 hₜ。每个 token 都成为一个约束,线性规划会选择 L1 范数 Σⱼ |vⱼ| 最小的 v:
最小化 Σⱼ |vⱼ|
满足以下约束:
v · hₜ ≤ −δ,t ∈ R(至少沿远离 u 的方向推动 δ)
∣v · hₜ∣ ≤ ε,t ∈ P(改变量至多为 ε)
实际中,没有一个 v 能满足全部约束,因此我为每个约束都加入了带惩罚项的松弛变量。
最小化 L1 往往会让 v 变得稀疏。L1 球是菱形,将它从原点不断放大,直到接触可行域时,通常会在一个角点相接,而那里大多数坐标恰好为零。因此,线性规划既选择了要修改的列,也选择了修改量。
为了让每层恰好只改8列,我保留影响最大的8列,将其余列固定为零,再求解一次。〔4〕
这样,Astra 花了8小时进行的搜索,包括选列和选择 α,就被几次线性规划求解替代了。现成的求解器 HiGHS 处理这些约束,每次需要20多分钟,于是我写了一个 GPU 求解器,使用 PDHG,也就是 PDLP 背后的算法。它大约一分钟就能得到距最优值不超过0.2%的结果。〔5〕
结果
我测试了两种线性规划修改方案,都使用 Astra 选出的层。其中一种沿用 Astra 的方向 u;另一种使用启发式方向:通过一个简单的重复检测器,将模型自己生成轨迹中的 token 标记为循环或正常推理,再计算两类 token 平均残差流的差。它采用了与 Astra 相同的思路,但不再需要手写文本,因此这一步没有 LLM 参与。我在一个独立的128题开发集上调整线性规划的设置,图表中的所有结果都来自从未用于拟合或调参的数据。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
使用 Astra 的方向时,线性规划在每项基准上都与 Astra 的修改效果相当,差别处于噪声范围内,而改动的权重数量只有它的八分之一。换成启发式方向,也能达到约93%的效果。
我还观察到了几件事:
-
搜索确实有用,而线性规划替代了它。在开发集上,如果直接将 Astra 的做法,也就是前64列、α = 6,套用到启发式方向,而不重新搜索,得分为44.5。线性规划的得分则是54.7。 -
列不同,修复效果相同。线性规划选出的8列,与 Astra 的64列相比,第15层只有3列重合,第19层只有1列重合。很多不同的稀疏解都能奏效,关键是沿 u 方向的总推动量。 -
随机方向没有效果。在开发集上,使用同等规模的随机 u,得分为10.2,而基础模型为10.9。
一个引导向量也能完成同样的修复
权重修改只能通过激活值来区分循环 token 和正常推理 token,而循环往往已经持续了一段时间,才能从激活值中识别出来。〔6〕因此,在开发集上,要求线性规划只推动看起来处于循环中的 token,并不比推动所有 token 更好。
线性规划在第15层选出的8列,在89%的 token 上都会激活;它们加入的推动量,在不同 token 之间也几乎不变。因此,这次修改接近于沿 u 方向施加一个恒定推动。恒定推动不需要修改权重,可以直接通过引导向量实现,也就是在每个 token 处,向残差流加入同一个固定向量。
下表在未经修改的模型上,向第15层和第19层加入线性规划修改沿 u 方向产生的平均推动量,并测试了三种强度:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
作为对照,在同一批运行中:线性规划修改在开发集上得分62.5,MATH-500 上为53.2;Astra 修改版分别为59.4和50.0;基础模型分别为10.9和7.0。这里的 MATH-500 每道题只采样一次。
在1×强度下,引导向量基本恢复了整次修复的效果。有效区间很窄:降到0.5×,循环又回来了;提高到2×,循环消失了,推理却也崩掉了。
两者唯一的区别在交付形式。线性规划修改得到的是一个普通 checkpoint,而引导向量需要在推理时加入额外代码。
局限与下一步
目前只测试了一个模型。线性规划仍然依赖 Astra 选择的层,最好的结果也使用了 Astra 的方向。启发式方向不依赖 LLM,就能取得大部分效果,但还不是全部。
事实也表明,一个恒定推动就能修复循环,因此线性规划为每个 token 设置的约束,最终并没有起到很大作用。更适合检验这些约束的,是某种只应改变部分 token、又应保留其他 token 的行为。另一个尚未解决的问题,是如何不借助 Agent 来选择层。
最后,目前只测试了消除循环。还有很多行为,例如拒绝或谄媚,可以通过在残差流中增加或移除单个方向来引导。〔7〕如果一种行为可以用单个方向引导,那么选择如何将它写入权重,应该也能归结为同样的线性规划问题,但这一点仍需验证。
代码、修改后的模型,以及完整技术说明都已放在 GitHub 上。
引用
如需引用这项工作,请使用:
Barry, Nathan. "Can a Linear Program Do Weight-Space Programming?"
nathan.rs (Nov 2026). https://nathan.rs/posts/weight-space-lp.
或者使用 BibTeX:
@article{barry2026wslp,
title = "Can a Linear Program Do Weight-Space Programming?",
author = "Barry, Nathan",
journal = "nathan.rs",
year = "2026",
month = "Nov",
url = "https://nathan.rs/posts/weight-space-lp"
}
注释
〔1〕Rikiya Takehi、Jayden Teoh、Alex Zhang 和 Omar Khattab,《Sparks of Weight-Space Programming》,MIT CSAIL,2026。日志以 CC BY 4.0 许可发布,本文关于 Astra 运行过程的全部信息都来自这些日志。
〔2〕Astra 的完整运行共158轮,耗时20.3小时,其中4.7小时是故障中断。19次前向计算失败,没有计入预算。完整明细见技术说明。
〔3〕如果以某个 token 结尾的连续16个 token,之前已经在同一回答中出现过,就把这个 token 算作重复。循环的起点,是从该位置开始,余下 token 中至少80%都在重复的最早位置。循环 token 是进入循环至少64个 token 之后出现的重复 token。正常推理 token 则位于循环开始前至少256个 token 的位置,或者位于一条已正常完成的回答中的任意位置。
〔4〕每列按 |vⱼ| 乘以 hⱼ 的标准差排序,大致对应它在一个典型 token 处沿 u 方向加入多少。
〔5〕PDHG,即原始—对偶混合梯度法,只需要矩阵—向量乘法,因此所有 token 约束可以同时放进 GPU,并且它返回的 v 具有精确的稀疏性。
〔6〕在第15层的激活上训练一个线性探针,当循环已经发生时,能较好地区分循环 token 与正常推理 token,AUC 为0.93;但在循环刚开始时,识别能力明显较弱,AUC 为0.73。
〔7〕或者,能否让它说话像她一样。
参考链接
-
原文:https://nathan.rs/posts/weight-space-lp/ -
Sparks of Weight-Space Programming:https://rikiyat.github.io/blog/weight-space-programming/ -
Astra 的完整运行日志:https://github.com/RikiyaT/lm_understand-logs -
完整技术说明:https://github.com/nathanrs/weight-space-lp/blob/main/docs/WRITEUP.md -
代码与修改后的模型:https://github.com/nathanrs/weight-space-lp

