比维度还多的概念
前几章拆出了一些干净的零件:只看上一个 token 的头、照抄的归纳头。可如果你随手挑一个 GPT-2 的神经元,看看它在什么时候亮,多半会失望——它在一些毫不相干的地方都亮。可解释性研究在这里卡了好几年。这一章用一个只有 2 维的玩具模型解释原因:模型要记的概念比它的维度多得多,于是它学会了把好几个概念叠在同一组维度里。
一个玩具模型:有 5 个「特征」(可以想成 5 个概念),要先压进一个 2 维的向量,再从这 2 维里尽量还原出原来的 5 个。5 个特征重要程度不同,第 0 个最重要。
每个特征各自独立,只有 10% 的时候出现(出现时取 0 到 1 之间的随机值,其余时候是 0)。训练完之后,问:这 2 维里一共放下了几个特征?
答案在本章末尾。
一个神经元,一个意思?
可解释性研究最初的梦想是这样的:GPT-2 每层有 3072 个神经元,如果每个神经元负责一个概念——这个管「篮球」,那个管「法语」,另一个管「Python 的缩进」——那拆开模型,就是逐个给神经元贴标签。
现实是:有少数神经元确实很「专一」,但大多数都是多义的(polysemantic)。同一个神经元,可能在学术引用、韩文、扑克牌术语上都会亮,彼此毫无关系。为什么训练会产出这么「乱」的东西?
第 5 章埋了一个伏笔:在 768 维的空间里,随便两个方向有 99.4% 的机会几乎互相垂直。也就是说,这个空间能装下的「几乎互不干扰的方向」,远远多于 768 个。而模型要记的东西——人名、地名、语法规则、代码结构、事实——显然也远远多于 768 个。如果允许一点点互相干扰,它能不能把更多的概念塞进去?
一个玩具:5 个特征,2 个维度
2022 年 Anthropic 的论文《Toy Models of Superposition》用一个小到可以画在纸上的模型回答了这个问题。设定如下:
- 5 个特征 x₀ 到 x₄,重要程度依次是 1、0.9、0.81、0.73、0.66(还原错了,越重要的扣分越多)。
- 每个特征有概率 S 不出现(值为 0),否则取 0 到 1 之间的随机数。S 叫稀疏度。
- 模型先用一个 2 × 5 的矩阵 W 把 5 维压成 2 维:
h = W x;再还原:x' = ReLU(Wᵀ h + b)。W 的每一列,就是一个特征在 2 维平面上的「方向」。
训练 10000 步,然后把 W 的 5 列画成平面上的 5 支箭头——箭头越长,说明这个特征被存得越好;短到接近 0,就是被放弃了。四种稀疏度:
| 稀疏度 S(不出现的概率) | 放下了几个特征 | 哪几个 | 怎么摆的 |
|---|---|---|---|
| 0(每个特征都一直在) | 2 | 0、1 | 互相垂直(相邻夹角 90.1°),其余三个放弃 |
| 0.7 | 4 | 0、1、3、4 | 两根轴,每根轴正反两头各放一个(夹角都在 90° 上下) |
| 0.9 | 5 | 全部 | 五角星,相邻夹角 69.7° 到 74.4° |
| 0.99 | 5 | 全部 | 五角星,相邻夹角 70.6° 到 74.6° |
当特征总是一起出现时(S = 0),模型的选择和你预期的一样:两个维度,放最重要的两个,互相垂直、互不干扰,剩下三个干脆不要(它们的还原值固定成一个平均数,靠偏置给出)。
可是当特征很少同时出现时(S = 0.9),它把 5 个全塞了进去,排成一个几乎正五边形的五角星——理想的正五边形相邻夹角是 72°,它学到的是 69.7° 到 74.4°。5 个概念共用 2 个维度。这就是叠加(superposition)。
为什么稀疏就挤得下
5 支箭头挤在一个平面里,它们不可能互相垂直:相邻两个之间的余弦大约是 0.3,隔一个的大约是 −0.8。于是当特征 0 出现时,还原特征 2 的那一路会读到一个不小的负数——这叫串扰(interference)。
模型的应对办法很巧妙,看它学到的偏置 b:S = 0.9 时,5 个偏置全是负的(−0.21 到 −0.23)。还原时先加上这个负偏置,再过 ReLU(负数一律变成 0)——小的串扰和负的串扰,统统被削成了 0。只有真正出现的那个特征,读数足够大,能越过这道门槛。
这个办法有一个前提:同一时刻只能有一两个特征出现。如果好几个特征同时出现,它们的串扰会叠在一起,越过门槛,还原就乱了。所以特征越稀疏,模型越敢挤;特征一直都在的时候,它宁愿放弃也不挤。
真实世界的概念恰好极其稀疏:一篇文章里,绝大多数 token 和「篮球」无关,和「法语」无关,和「Python 缩进」无关。对一台语言模型来说,叠加不是例外,而是默认状态。一个 token 的向量里,同时叠着它可能的好几种意思,靠上下文把用得上的那一种放大:
The bank of the 后面,␣river(河岸)排第一,可是 ␣United(Bank of the United States)、␣world 也在榜上。␣bank 这一个 token 同时带着「河岸」和「银行」两个方向,这里的上下文还不足以把它们分开。
这对「读懂模型」意味着什么
如果概念是叠在一起存的,那么:
- 一个神经元会对好几个不相干的概念有反应——因为每个概念的方向都在好几个神经元上有分量,每个神经元也都参与了好几个概念的方向。
- 该找的是方向,不是神经元。近两年的主流工具叫稀疏自编码器(SAE):再训练一个「把模型的内部向量展开成很多很多个、每次只亮几个的特征」的小网络,相当于把叠在一起的概念重新拆开。
2024 年 Anthropic 在《Scaling Monosemanticity》里用这种方法,从自家的大模型 Claude 3 Sonnet 里拆出了几百万个特征,其中有一个专门对应「金门大桥」;把这个特征人为调大,模型就开始在每个回答里扯到金门大桥——他们还把这个版本放出来让人玩了一阵,叫 Golden Gate Claude。一个概念是一个方向;沿着这个方向推一把,模型的「想法」就跟着偏。
玩具模型只要 numpy,二十几行,几秒钟跑完:
import numpy as np
def train(S, n=5, m=2, steps=10000, seed=0, lr=1e-2):
"""5 个特征挤进 2 维:x' = ReLU(Wᵀ W x + b)。S = 每个特征「这一次不出现」的概率"""
rng = np.random.default_rng(seed)
I = 0.9 ** np.arange(n) # 重要性:第 0 个最重要
W = rng.normal(0, 1, (m, n)) * np.sqrt(2 / (m + n)); b = np.zeros(n)
st = [np.zeros_like(W), np.zeros_like(W), np.zeros_like(b), np.zeros_like(b)]
for t in range(1, steps + 1):
x = rng.random((1024, n)) * (rng.random((1024, n)) > S)
h = x @ W.T; pre = h @ W + b
d = 2 * (np.maximum(pre, 0) - x) * I * (pre > 0) / 1024
gW, gb = h.T @ d + (d @ W.T).T @ x, d.sum(0)
for P, g, mm, vv in ((W, gW, st[0], st[1]), (b, gb, st[2], st[3])): # Adam
mm *= 0.9; mm += 0.1 * g; vv *= 0.999; vv += 0.001 * g * g
P -= lr * (mm / (1 - 0.9 ** t)) / (np.sqrt(vv / (1 - 0.999 ** t)) + 1e-8)
return W
for S in [0.0, 0.7, 0.9]:
W = train(S)
norm = np.linalg.norm(W, axis=0)
kept = np.where(norm > 0.5)[0]
ang = np.sort(np.degrees(np.arctan2(W[1, kept], W[0, kept])) % 360)
gaps = np.diff(np.r_[ang, ang[0] + 360]).round(0)
print(f"S={S} 2 维里放下了 {len(kept)} 个特征 {kept.tolist()} 相邻夹角 {gaps.tolist()}")
本机输出:
S=0.0 2 维里放下了 2 个特征 [0, 1] 相邻夹角 [90.0, 270.0] S=0.7 2 维里放下了 4 个特征 [0, 1, 3, 4] 相邻夹角 [90.0, 90.0, 91.0, 90.0] S=0.9 2 维里放下了 5 个特征 [0, 1, 2, 3, 4] 相邻夹角 [74.0, 71.0, 70.0, 71.0, 73.0]
改 n 和 m 试试:比如 20 个特征挤 5 维。还可以把重要程度改成全一样,看看五角星会不会更正。
pip install numpy && python 13.py
原论文有大量交互图:transformer-circuits.pub/2022/toy_model。
- 压缩感知(compressed sensing)是同一个数学:一个信号如果是稀疏的,就能用远少于它维度的测量把它还原出来。核磁共振扫描能比过去快好几倍,靠的就是这个——你在医院里躺的时间,部分是被「稀疏就能挤」省下来的。
- 布隆过滤器是工程师熟悉的版本:用很少的比特位记很多元素,允许一点点冲突,换来空间上的巨大节省。冲突率随塞进去的东西增多而上升——和串扰一样。
- 「让模型更诚实 / 更安全」的一类研究,正在用这一章的思路:找到对应某种行为的方向,然后在推理时把它调大或调小。这比重新训练便宜得多,也比改提示词更「深」。
「每个神经元负责一个概念;模型能记住的概念数,不会超过它的神经元数(或维度数)。」
2 维的玩具模型记住了 5 个特征;条件是这些特征很少同时出现。在这种条件下,概念被存成方向,而方向可以比维度多得多,代价是一点点串扰,再用负偏置加 ReLU 把串扰滤掉。
所以「找到代表篮球的那个神经元」往往是一个没有答案的问题:篮球是一个方向,散落在很多神经元上;而每个神经元也同时参与了很多别的方向。要读懂模型,得先把叠在一起的东西拆开。
正确答案是 D:5 个特征全部放进 2 维,排成五角星,相邻夹角 69.7° 到 74.4°(正五边形是 72°),5 个偏置都是负的。
A 「一个维度放一个」——这是特征一直都在(S = 0)时的答案。直觉默认了「每个概念要独占一个维度」,而稀疏性打破了这个前提。 B 「挤一挤,多塞一个」——叠加的几何偏爱对称的排法(正多边形、正反成对),很少出现「多塞一个」的不规则形状。 C 「正负两头各放一个」——这正是 S = 0.7 时的答案:两根轴、四个特征,同一根轴上的两个方向相反,ReLU 让它们互不干扰。稀疏度再高一点,它就敢放下第五个了。《张成》第 11 章讲线性无关和基:d 维空间里最多只有 d 个线性无关的向量——这一章说的是,如果只要求「几乎」无关,你能放下的远不止 d 个。《绕圈》讲频域:很多真实信号在频域里是稀疏的,这正是压缩感知和这一章共用的那个前提。
这一章的一句话
当概念很少同时出现时,模型会把比维度多得多的概念叠在同一组维度里,每个概念是一个方向,靠负偏置和 ReLU 滤掉串扰。所以神经元大多是多义的;要读懂模型,找的应该是方向,而不是神经元。
卷 III 拆完了。下一卷回到出口:那张表出来之后,怎么变成一段话?最直接的办法是每一步都挑第一名。可是让 GPT-2 用这个办法续写「The meaning of life is」,200 个 token 里,不重复的 4 词片段只剩 8%——它写了一句「生命的意义不同于死亡的意义」,然后把这句话原样重复到了最后。