大模型的“思考过程”是一大堆数字。我想弄明白:模型表现出来的某种行为,能不能在这些数字里找到一个对应的“方向”?如果能,把这个方向加强或去掉,行为会不会跟着变?
这是一个学习性质的实验,全部在自己电脑上完成。不提供代码、数据和修改过的模型下载,原因见后文。
我做了什么
我是可解释性方向的初学者,按照“先读论文、再在小模型上复现、最后检查结论靠不靠谱”的顺序做了两组练习:
- 激活引导(activation steering):先找到代表某种风格的方向,在模型生成文字时把它“加进去”,看输出的风格会不会跟着改变。
- 方向消融(directional ablation):复现 Arditi 等人 2024 年的论文:模型“拒绝回答”这类行为,是不是主要由内部的一个方向控制?把它从计算中去掉会怎样?
- 换更大的模型验证:从 15 亿参数的 Qwen2.5-1.5B 换到 40 亿参数的 Qwen3-4B,检查结论在更大的模型上是否还成立。
- 检查评估方法本身:大量时间其实花在“怎么测才可信”上:设置对照组、换评委、估计误差,推翻过自己的结论。
原理
语言模型由几十层计算组成。一句话进入模型后,每一层都会把每个字(准确说是 token)表示成一串几千维的数字,叫作激活,可以把它想象成高维空间里的一个点。研究者发现,很多“概念”或“行为”在这个空间里大致对应一个固定的方向:沿着这个方向走得越远,那种特征就越明显。
找方向的方法出奇地简单,叫“均值差”(difference of means):
- 准备两组对比的输入一组会引发目标行为,一组不会,其余方面尽量相同。
- 记录模型内部的激活让模型把两组输入各读一遍,记下每一层的激活。
- 两组平均值相减差值就是候选“方向”。在每一层都算一遍,再挑效果最好的那一层。
- 拿这个方向做干预在模型生成回答时,用“钩子”(hook)实时修改激活:加上这个方向就是激活引导;把激活里沿这个方向的分量减掉就是方向消融。
- 评估看目标行为有没有变化,同时检查普通问题的回答有没有被破坏,并和“随机方向”这个对照组比较。
在 Qwen2.5-1.5B 上,去掉找到的方向以后,目标行为几乎消失,普通问题的回答基本不受影响;把方向加回去,又能重新引发这种行为。这说明至少在小模型里,一种看起来很复杂的行为确实可以主要对应到一个方向上。
开关在第几层
后来我在 40 亿参数的 Qwen3-4B 上把这件事看得更细。这个模型有 36 层,每个 token 在每一层都是一串 2560 个数字,沿着一条叫“残差流”的主干道往上传,每经过一层,都会在上面加一点东西。我把方向在每一层各算了一遍,想知道“要不要表现出这种行为”的信号,是在哪一段成形的。
先说消除到底做了什么。2560 维没法画,就用二维来比划:箭头是行为方向,圆点是某一层的激活。消除就是把激活在这个方向上的那一段减掉,剩下的部分和方向垂直,其余 2559 个方向上的信息原样保留。这个操作只在推理时临时挂上,不改模型文件。
方向的长度在前 18 层都很短,从第 20 层开始迅速变长,说明这个信号主要在模型的后半段才形成。不过激活本身也是越往后越大,所以长度只能做个参考,下一张图更能说明问题。
第二张图看的是两组样本在每一层的差距,单位是普通样本的标准差。不做干预时,从第 23 层开始,两组就拉开了 6 到 8 个标准差。把第 24 层的方向从每一层抹掉以后,第 23 到 34 层的差距几乎归零,前面的层却只降了一点。我的理解是,前面几层更像在识别“这是哪一类问题”,后面几层才是在决定“要不要这样回答”,被消除的正是后者;从其他维度看,两组样本依然分得开,模型并没有因此变得“不认识”这些问题。
第三张图对应下面心得里说的“方向不专一”。我用公开的有害请求数据集,按论文原版的方法算出“通用拒绝方向”,再逐层和我找到的方向比较。前 18 层两者几乎无关,从第 20 层起,相似度跳到 0.6 左右,好像模型在后半段把几种不同的“拒绝”汇成了同一个开关。这也解释了为什么消除这个方向时,模型对明显有害请求的拒绝率会从 94% 掉到 14%。
这些结果都只跑了一次,每组大约几十个样本,裁判是另一个模型、没有人工标注,数字要带着误差来看。
学习心得
1. 先看清数据,再算平均值
Qwen 模型第一个 token 的激活数值(范数)大得异常,约为一万,远远超过其他 token,这个现象叫“注意力汇聚点”(attention sink)。一开始我把它也算进平均值,方向就被这个异常值主导了。把它排除以后,结果才正常。
2. “推一下”要分散到多层,力度要按比例
只在一层上加方向,要么没效果,要么模型开始胡言乱语。把较小的力度分散到中间若干层,并且按每层激活本身的大小来定比例,效果才稳定。
3. 我曾经得出过一个错误结论
换到 4B 模型后,我一度认为“这个方法在大一点的模型上失效了”。后来逐项排查才发现,问题出在测量上,而不是方法本身:
- 为了省显存,我用了一种 8 位量化设置。它会把中间计算也一起量化,模型本身就已经被扭曲了。改成只量化权重后,模型的输出和原始精度几乎一致。
- 当时让模型给自己的回答打分,而且只看前 64 个 token,评委本身就不可靠。
修正之后,效果重新出现了。这件事让我记住:下结论之前,先确认测量工具没有坏。
4. 对照组和误差范围必不可少
- 每次都同时测一个随机方向。如果随机方向也能让行为变化,说明只是“扰动了模型”,不是找对了方向。
- 评委换成了另一个独立的模型,并且盲评:打乱顺序,不告诉它哪条来自哪种设置。为了检验评委,又拿 60 条样本请更强的模型独立标注做对比,一致率约 85%。
- 测试集只有几十条时,误差大约有正负十几个百分点。我把训练集和测试集对调重做了一遍,效果比第一次看到的小。原因是“挑最好的那一层”这一步本身就带有偏差。
5. 方向不像看起来那么“专一”
这是我觉得最重要的发现。我找到的方向,和模型拒绝有害请求时用到的方向有不小的重合(余弦相似度约 0.6)。去掉它的时候,模型对明显有害请求的正常拒绝也被大幅削弱了。也就是说,动模型内部的一个“开关”,可能同时关掉了别的东西。这让我更加理解,为什么对齐和安全不能只靠单一机制,也让我决定不公开任何修改过的模型。
6. 这种行为主要是在后期训练中学到的
对比同一系列的“基础模型”(只做过预训练)和“对话模型”(又做过指令微调和对齐)后发现,基础模型没有明显偏好,对话模型则几乎每次都倾向这种回答。可见这类行为主要是在后期训练阶段形成的。
7. 工程上的收获
- 8GB 显存很紧张。批量大小、提示长度、量化方式,三者要一起权衡,超出显存后会溢出到内存,速度骤降。
- 不要一次性对整个词表、整批数据做 log-softmax,中间结果会占掉好几 GB 内存。
- 每次实验都把参数和结果存成文件,回头排查“为什么结论变了”时,全靠这些记录。
为什么不提供代码
方向消融可以用来削弱模型的安全拒绝(见心得 5),修改后的模型和完整代码一旦公开,就可能被用来绕过安全措施。这个项目的目的是学习可解释性的原理,所以只在本地做实验,不发布代码、数据和修改过的模型。
如果你也想入门,建议从下面的公开资料开始。
参考资料
- Arditi, A. et al. Refusal in Language Models Is Mediated by a Single Direction. NeurIPS 2024.
- Turner, A. et al. Steering Language Models With Activation Engineering. 2023.
- Zou, A. et al. Representation Engineering: A Top-Down Approach to AI Transparency. 2023.
- Xiao, G. et al. Efficient Streaming Language Models with Attention Sinks. ICLR 2024.