权重空间编程 是做什么的?
通过直接编辑权重来改进小型开源模型,无需优化器。该方法遵循《Sparks of Weight-Space Programming》中的实验8(Takehi, Teoh, Zhang, Khattab, MIT CSAIL, 2026年11月2日):Qwen3.5-0.8B的思考循环被追溯到由第15层和第19层MLP写入的一个残差方向,并且对每层的64个down_proj列进行一次秩1编辑,使MATH-500得分从7.9%提升至48.1%。
请按以下快速路径的顺序操作。 每个脚本都打包在 scripts/ 目录下并经过测试。除非步骤失败,否则请勿重新设计流程或编写新的工具。
该技能的保证和限制。 没有方法能保证编辑一定有效:有些模型没有单一的、可以关闭的习惯。该技能保证的是过程:
- 不会丢失工作:崩溃后会以更小的批次恢复;
- 任何编辑在通过实测的实践检查之前,不会接触到保留的测试数据;
- 检查失败会通过固定步骤升级,而不是停止;
- 每次运行都以清晰的结论和数字结束,如果无效则恢复原始模型副本。
参考文献,仅作详细参考:
references/goals.md:其他目标;如何编写匹配对;评分器;配方。references/engines.md:哪个操作系统和GPU上使用哪个引擎;安装;规模;陷阱;烟雾测试。references/evaluation.md:数据划分;采样协议;MMLU-Pro细节;配对统计;报告模板。references/method.md:每一步的数学原理和作者的精确配方。
快速路径
设置四个占位符(PowerShell: $env:SKILL = "...", 然后 $env:SKILL):
SKILL=<此技能的文件夹> BASE=<基础模型文件夹> COPY=<基础模型文件夹>-<目标> WORK=<结果文件夹>
在后台运行每个生成任务(Claude Code:使用Bash工具的 run_in_background,而不是shell的 &),并等待其完成通知,而不是轮询。生成过程会占用绝大部分时间,因此请提前告知用户所需时间预算:对于预设任务,消费级GPU需要1.5-3小时;如果使用vLLM在数据中心GPU上,则时间少得多。
0. 仅询问你需要的信息(一个问题)。询问目标。如果是数学或科学任务,则使用预设:无需再问问题。否则,还需询问或帮助编写:
- 展示失败的提示,格式为
{"id", "prompt", "answer"?, "group"?}JSON lines; - 如何评分(
references/goals.md第3节); - 对于非循环行为,需要12+个匹配对(第2节)。
确认“编辑权重,无需训练”。
1. 检查机器(2分钟):python $SKILL/scripts/device.py --model $BASE --jobs 2。如果在新机器上,还需运行references/engines.md第6节中的烟雾测试。
2. 复制模型(1分钟)。每次编辑都写入此副本的文件中;每次评估都加载此副本。
python $SKILL/scripts/copy_model.py --base $BASE --copy $COPY
3. 数据分割(秒级)。
- 科学预设:
python $SKILL/scripts/make_splits.py --task mmlu --source <MMLU-Pro 测试 parquet 文件> --out $WORK/splits。 - 数学预设: 使用
--task math --source <MATH-500 测试.jsonl 文件> --practice 25 --heldout 75。 - 自定义目标: 将你的提示文件按照固定规则分割成
practice.jsonl和heldout.jsonl,再加上一个包含大约30个保留ID的test_ids.txt文件,所有这些选择必须在看到任何答案之前完成。
下文中的 T 代表任务(mmlu、math 或 jsonl),G 代表评分器选项(预设任务不需要;自定义目标请参阅 references/goals.md)。
4. 同时启动两个基线运行:
python $SKILL/scripts/run_eval.py --task $T --model $BASE --label base --data $WORK/splits/practice.jsonl --out $WORK/practice/base.jsonl --max-tokens 16384 --jobs 2
python $SKILL/scripts/run_eval.py --task $T --model $BASE --label base --data $WORK/splits/heldout.jsonl --ids-file $WORK/splits/test_ids.txt --out $WORK/test/base.jsonl --jobs 2
对于其他模型,使用模型卡片的采样参数(--profile 或覆盖标志;references/engines.md)。保留的运行不依赖于编辑。
5. 获得对比。
- 循环(预设任务,或任何答案达到token上限的目标): 循环仅当答案达到token上限时出现。使用
grep -c '"length"' $WORK/practice/base.jsonl计数;如果达到15个或更多,则停止实践运行。 - 其他行为: 你需要匹配对,并且实践运行只需完成以获得基线。
然后评分:
python $SKILL/scripts/grade.py --task $T $G $WORK/practice/base.jsonl --out $WORK/practice/summary.json
进行/停止决策。 估算提升空间:表现出该行为的答案占比,乘以未表现行为答案的分数。如果该值低于大约10%,请告知用户预期提升不大,然后再继续。
6. 读取方向,构建候选,筛选(几分钟,仅前向传播)。
- 循环:
python $SKILL/scripts/probe.py collect --task $T --model $BASE --data $WORK/splits/practice.jsonl --runs $WORK/practice/base.jsonl --out $WORK/probes
- 针对匹配对(需添加
--runs,--data和--task以获得干净的副作用窗口):
python $SKILL/scripts/probe.py collect-pairs --model $BASE --pairs pairs.jsonl --thinking on --out $WORK/probes
然后构建第一轮候选、对照并筛选:
for spec in "15 19:128:6" "15 19:256:6" "15 19:64:6" "11 15 17 19 21:128:6"; do IFS=: read L K A <<< "$spec"; python $SKILL/scripts/probe.py build --probes $WORK/probes --model $BASE --layers $L --k $K --alpha $A --out "$WORK/edits/L${L// /-}_k${K}_a${A}.safetensors"; done
python $SKILL/scripts/probe.py build --probes $WORK/probes --model $BASE --layers 15 19 --k 128 --alpha 6 --control random --out $WORK/edits/random.safetensors
python $SKILL/scripts/probe.py screen --probes $WORK/probes --model $BASE --edits $WORK/edits/*.safetensors
在Windows PowerShell中,每条 build 命令需单独运行。上面的层适用于24层模型。对于其他模型,使用大约深度60%和80%处的层、一个五层的跨度,以及较深的那一层单独使用。
根据 unwanted 上的下降来排列候选者,只保留那些 wanted 和 clean 上的下降不超过其三分之一的部分。随机对照必须保持在0附近;如果它的移动量与最佳候选者一样大,则说明该方向不够特化,因此跳转到停止步骤。
7. 将最佳候选者写入模型副本并验证(1分钟)。切勿评估内存中的编辑。
python $SKILL/scripts/apply_edit.py --base $BASE --copy $COPY --edit $WORK/edits/<candidate>.safetensors
python $SKILL/scripts/diff_edit.py --base $BASE --edited $COPY --out $WORK/edit_report.json
差异必须只显示预期的 down_proj 张量,changed_columns = k,秩1占比约为1,α中位数约为所选α。
8. 实践检查:门控(15–25分钟,与基线保留运行同时进行)。在每一个基线模型失败(截断、错误或标记)的实践问题上运行模型副本,再加上6个它处理得好的问题:
python -c "import json; g=json.load(open('$WORK/practice/summary.json'))['graded']['base']; bad=[x['id'] for x in g if x['cut'] or x['correct'] is False or x['flagged']]; ok=[x['id'] for x in g if x['id'] not in bad]; open('$WORK/check_ids.txt','w').write('\n'.join(bad+ok[::max(1,len(ok)//6)][:6])+'\n')"
python $SKILL/scripts/run_eval.py --task $T --model $COPY --label copy --data $WORK/splits/practice.jsonl --ids-file $WORK/check_ids.txt --out $WORK/practice/copy.jsonl --max-tokens 16384 --jobs 2
python $SKILL/scripts/grade.py --task $T $G $WORK/practice/base.jsonl $WORK/practice/copy.jsonl
门控通过当且仅当同时满足以下三个条件时:
- 修复的数量 ≥ max(3, 检查到的基线失败数量的20%),
- 破坏的数量 ≤ 修复的数量 ÷ 4,
- 并且,对于循环目标,截断比例下降至少四分之一。
通过:跳转到步骤10。失败:步骤9。
9. 按以下顺序升级;每一级都是一次额外的实践检查。
- 下一个排名的候选者。 恢复副本(
copy_model.py --base $BASE --copy $COPY --restore),应用它,并重复步骤7-8。最多再尝试两个候选者。 - 第二轮,当行为转移而不是消失时。 例如,思考现在关闭了,但循环在
response之后重新开始,或者副本的答案中出现了新的不希望的模式。将编辑后的副本视为新的基础:- 使用
copy_model.py --base $COPY --copy $COPY2创建COPY2; - 使用
--model $COPY --runs $WORK/practice/copy.jsonl运行probe.py collect(或collect-pairs); - 使用
--model $COPY构建和筛选; - 使用
--base $COPY --copy $COPY2应用; - 检查
COPY2,如同步骤8。
- 使用
- 停止。 三次检查后(或用户设定的时间预算到期后)仍无可靠提升。恢复模型副本,并报告结论“无可靠提升”,附上每个候选者的数据。对于Qwen3.5-0.8B,提供已发布的数学编辑作为对比(根据作者日志,它已将MMLU-Pro的截断比例从28.9%降至0.5%;请参见本文件末尾)。仅在用户同意时使用它。
切勿使用保留结果来选择;仅凭实践检查决定。
10. 在模型副本上执行保留测试(30–60分钟)。在基线保留运行超过其最长答案后启动:
python $SKILL/scripts/run_eval.py --task $T --model $COPY --label copy --data $WORK/splits/heldout.jsonl --ids-file $WORK/splits/test_ids.txt --out $WORK/test/copy.jsonl --jobs 2
python $SKILL/scripts/grade.py --task $T $G $WORK/test/base.jsonl $WORK/test/copy.jsonl --out $WORK/test/summary.json
根据配对结果给出结论:
- “显著提升”:当 fixed > broke 且符号检验 p < 0.05。
- “与提升一致,但未证明”:当 fixed > broke 但 p ≥ 0.05;建议进行更大规模的测试。
- “无提升”:当 fixed ≤ broke;建议恢复模型副本。
11. 报告,使用 references/evaluation.md 中的模板:
- 保留测试前后的分数,以及配对的 fixed/broke 计数;
- 行为发生率(截断、标记、思考关闭);
- 每个候选者及其筛选和检查数据,以及随机对照;
- 更改的权重;
- 注意事项:关闭一个习惯并非获得新能力。
之前耗费数小时的错误(请勿重复)
- 批次太大,或同时运行两个长任务达到峰值。 内存不足崩溃会使正在运行中的答案丢失,而这些答案正是循环。请使用
--batch auto --jobs 2,并且切勿在第一个32k任务处理其最长答案时启动第二个。监督者会以一半的批次重启崩溃的运行。 - 等待整个运行完成。 大约15个循环就足够了。
- 在内存中进行编辑并用于评估。 只有
probe.py screen会在内存中应用编辑,并且仅用于教师强制读取。 - 仅信任筛选结果。 在循环任务上,-0.10的筛选下降在生成中仅破坏了26个循环中的3个,因此始终要运行门控检查。
- 对部分运行进行评分,或根据完成顺序选取测试问题。
- 使用
pkill -f <text>停止任务, 这也会杀死命令行中包含该文本的观察者进程。
规则(如原文所述)
请遵守这些规则,并在报告中说明您已遵守。
- 无训练。 不允许反向传播、自动求导、优化器或迭代地将权重拟合到损失。调整 α 或权重直到指标改善属于拟合。在上述固定候选列表中进行选择则不算。
- 允许用于测量的前向传播: 生成、教师强制对数概率、隐藏状态和MLP激活,以及这些量或权重的统计量(均值、差异、SVD)。
- 不在范围内:
- 嵌入层、(绑定的)输出头和最终层归一化(
weights.py会拒绝它们); - 将整个张量乘以一个常数;
- 全局锐化或平滑;
- 强制思考提前关闭,无论问题如何;
- 移除安全行为:拒绝回答、安全措施、对有害请求的顺从。
- 嵌入层、(绑定的)输出头和最终层归一化(
- 仅权重值改变。 保持名称、形状、数据类型、配置、分词器、聊天模板和解码设置不变。
- 数据分割纪律。 使用不相交的实践集和保留集,且在编辑之前固定。在编辑冻结后,只看一次保留结果。
- 公开的选择过程。 报告列出尝试过的每个候选者以及选择获胜者的指标。
每个步骤如此设计的原因
- 对比: 方向是模型每一层输出上,不想要的文本和与之匹配的想要文本之间均值的差异。
- 对于循环,
loops.py取循环开始前的1,024个token和循环内部的1,024个token:相同问题,相同答案,几乎相同的位置。循环起始点是第一个使得接下来的512个token中有90%重复了更早的16-gram的点。 - 对于其他行为,匹配对提供了匹配。
- 对于循环,
- 写入者列: score_j = (神经元j的不想要激活均值 − 想要的激活均值) × (u · W_down[:, j]);取前k个。仅凭静态写入 u·W 是不够的:每层已发布的64列中只有21–22列位于其前64中。
- 编辑: W'[:, S] = W[:, S] − α u (uᵀ W[:, S]),使用被编辑模型的float32类型计算。α = 1会移除该成分,这对循环无效;α = 6会将其翻转为 −5c,因此这些神经元会写入与行为相反的内容。
- 筛选: 应用编辑后平均对数概率的变化。不想要的文本应变得不太可能;想要的和干净的文本应保持不变。在相同列上的范数匹配的随机方向是对照。
- 门控: 筛选分数只能松散预测生成(参见下面的结果),因此在保留测试之前,由实践问题上的简短生成检查来决定。
- 保存:
weights.py仅从原始文件重写被触及的 safetensors 分片。推理引擎会在加载时重写或丢弃张量,因此切勿从推理引擎保存权重。 - 协议: thinking 开启,temperature 0.6,top_p 0.95,top_k 20,仅对生成的token施加 presence penalty 1.5,最多32,768个新token(
--profile thinking)。所有引擎都匹配 vLLM 的语义。 - 配对: 引擎和批次大小会改变随机数,因此在同一比较中按问题配对。切勿比较在不同问题或使用不同引擎测量得到的分数。
R
…
[已截断为翻译版本]