手动修补大语言模型权重
我仅使用 torch 和 transformers,从头实现了 ROME——秩-1 模型编辑。目标是:通过一次矩阵加法,在 GPT-2 Medium 内部改写一个事实,然后观察模型所知道的其他一切会发生什么。
我进行了四次编辑。三次精准无误。第四次暴露出一个问题:ROME 的"精准性"对那些你事先未必会想到的实现细节极为敏感。
四次编辑
我挑选了 GPT-2 Medium 确实以较高置信度(正确答案的概率 P > 0.5)知晓的事实,涵盖四个不同领域:
| 主体 | 原始事实 | 修改为 |
|---|---|---|
| 哈佛大学 | 位于马萨诸塞州 | 加利福尼亚州 |
| 谷歌 | 位于加利福尼亚州 | 德克萨斯州 |
| 墨西哥卷饼 | 来自墨西哥 | 日本 |
| 自由女神像 | 位于纽约 | 拉斯维加斯 |
四次编辑全部命中目标。更新后,模型对精确编辑提示的新答案预测概率均达到 ≥ 0.98。所以 ROME 本身是有效的。变化在于其他的一切。
三次干净的编辑
四次编辑中,有三次的表现大致符合 ROME 论文的预测。以下是每次编辑后,不相关事实的变化情况:
墨西哥卷饼 → 日本(更新范数:权重范数的 9%)
| 对照项 | 编辑后 |
|---|---|
| 寿司来自日本 | ✓ 未变 |
| 披萨来自意大利 | ✓ 未变 |
| 拉面来自日本 | ✓ 未变 |
| 墨西哥卷 来自墨西哥 | → 日本 |
只有玉米卷(Burritos)——在墨西哥食物语义空间中最近的邻居——被带偏了。
哈佛 → 加利福尼亚州(更新范数:13%)
| 对照项 | 编辑后 |
|---|---|
| MIT 位于马萨诸塞州 | ✓ 未变(仍然模糊) |
| 马萨诸塞州首府 = 波士顿 | ✓ 未变 |
| 波士顿位于马萨诸塞州 | ✓ 未变 |
| 耶鲁 位于康涅狄格州 | → 加利福尼亚州 |
耶鲁——哈佛在常春藤联盟语义空间中最近的邻居——被带偏了。其他一切没有变动。
自由女神像 → 拉斯维加斯(更新范数:16%)
| 对照项 | 编辑后 |
|---|---|
| 时代广场位于纽约 | ✓ 未变 |
| 纽约市位于纽约 | ✓ 未变 |
| 帝国大厦 | 有争议(纽约 0.49 vs 拉斯维加斯 0.26) |
| 自由钟 位于费城 | → 拉斯维加斯 |
两个地标出现了摇摆。自由钟(Liberty Bell)特别有趣——它并不在纽约,但它和"Liberty"这个词共享,GPT-2 将两者混淆了。
总结:直接编辑干净,附带损伤范围窄,更新幅度合理(9–16%)。
模型还欣然编造了与之匹配的替代历史。我最喜欢的一个:将哈佛迁至加利福尼亚州后,当被问及哈佛的创建年份时,模型回答道:“1776 年由法国耶稣会神父查尔斯·德·孟德斯鸠创建。” 完整的句子,内部自洽,完全错误。这就是模型的先验知识(“哈佛是名校,著名的地方有著名的创始人”)在填补原有事实消失后留下的空洞。
混乱的那次:谷歌
谷歌的编辑很糟糕。我的第一次运行报告的更新范数为 117%——秩-1 变化比权重矩阵的范数本身还要大——整个加利福尼亚科技集群就此崩溃:
- 苹果 → 德克萨斯州(P = 1.00)
- 微软 → 德克萨斯州(P = 1.00)
- 硅谷 → 德克萨斯州(P = 1.00)
- 斯坦福大学 → 德克萨斯州(P = 0.75)
- 以及:“谷歌于 2000 年由史蒂夫·乔布斯创立。”
我原本打算以这个发现作为博客文章的头条结论。后来我多想了一会儿,意识到 117% 这个数字很可疑。秩-1 编辑的幅度不应该比被编辑对象本身还大。
调试谷歌
有两个地方出了问题。
问题 1:我的协方差估计不足
ROME 的更新公式依赖于 C——目标层中间(post-GELU)向量的协方差:
u = torch.linalg.solve(C + lambda * I, h_star)
delta_W = (u / (h_star @ u)).unsqueeze(1) @ (v_star - W @ h_star).unsqueeze(0)
方向 C⁻¹ @ h* 使编辑具有选择性——它与 h* 对齐,同时与典型的键向量正交。如果 C 的条件数很差,C⁻¹ @ h* 就会在低特征值方向上爆炸,导致更新变得巨大。
我当时用 200 个 WikiText 样本估计 C——大约 10,600 个 token。对于一个 4096×4096 的协方差矩阵,这意味着每个维度只有约 2.5 个样本。矩阵严重秩亏。我设置了 1e-4 * I 的正则化,完全不够。
修复方案:使用 2000 个样本(约 118,000 个 token,每个维度约 29 个),以及基于迹的缩放正则化(1e-2 × mean(diag(C)))。
同一次编辑的结果:更新范数从 117% 降至 50.5%。 条件数改善,更新幅度减半。
但 50% 仍然很大,对照项仍然被破坏:
| 对照项 | 修复协方差后 |
|---|---|
| 苹果总部 | 德克萨斯州(1.00) |
| 微软总部 | 德克萨斯州(0.99) |
| 硅谷 | 德克萨斯州(0.92) |
| 斯坦福 | 德克萨斯州(0.97) |
因此,部分"灾难"是 bug 造成的——但并非全部。集群泄漏是真实存在的。
问题 2:位置影响极大
“Google” 是一个单独的 BPE token。在我的提示词——“Google is a company headquartered in the state of”——中,它位于位置 0。这意味着 h*(用于编辑的中间向量)是从一个没有见过任何前文语境的 token 计算得来的。这是一个裸表示。
如果我把 Google 放在位置 0 以外的地方呢?我把提示词改为 “The technology company known as Google is headquartered in the state of”——这样 Google 位于位置 5,有 “The technology company known as” 作为前文语境。
相同的编辑目标,相同的新协方差。结果:
- 更新范数:9.1%(从 50.5% 下降,从 117% 下降)
- 苹果 → 德克萨斯州(0.97)——仍被带偏
- 微软 → 德克萨斯州(0.57)——部分被带偏
- 硅谷 → 加利福尼亚州(0.81) ✓ 保留
- 斯坦福 → 加利福尼亚州(0.92) ✓ 保留
仅仅通过在计算 h* 之前给主体 token 一些语境,编辑就变得足够精准,硅谷和斯坦福得以保全。苹果和微软仍然被波及,说明真实存在一些与谷歌相邻的泄漏——但与最初的大灾难相去甚远。
这究竟告诉了我们什么
我原本希望这篇文章的结论是"看,ROME 无法编辑枢纽概念——看谷歌是怎么把一切都炸掉的。“这个框架是错的。真相更有趣,也没那么戏剧化:
ROME 对论文中看不到的实现细节十分敏感。 协方差的样本数量。正则化强度。主体 token 在提示词中的位置。这些地方任何一个出错,你所谓的"灾难性附带损伤"可能就是你自己的代码造成的。
位于位置 0 的单 token 主体是最差情况。 它们的
h*判别性最弱,C中的任何数值误差都会在求逆时放大为过大的更新。如果你想要干净的编辑,请用前文语境填充主体。枢纽概念泄漏是真实的,但程度有限。 即使使用了正确的协方差和前文语境,编辑谷歌仍然会轻微影响苹果和微软。“Google” 处于一个语义密集的邻域,秩-1 编辑会触及这个邻域。你可以通过 MEMIT 式的多层分布将其再减少 2–4 倍,但无法完全消除。
更新范数是可靠的诊断指标。 低于权重范数的 15%:可能没问题。高于 50%:可能有问题,要么是因为 bug,要么是因为你在编辑一个枢纽概念。在信任编辑结果之前,先检查这个值。
幻觉是真实存在的
在每一次成功的编辑中,模型都编造了自洽的替代事实来配合:
- 哈佛(现在在加利福尼亚州)由一位法国耶稣会士于 1776 年创立。
- 墨西哥卷饼(现在来自日本)配米饭食用,与墨西哥卷饼关联最密切的语言是西班牙语。
- 自由女神像的渡轮服务现在从"拉斯维加斯国际机场"出发。
- 谷歌(现在在德克萨斯州)“由史蒂夫·乔布斯于 2000 年创立”。
这些不是噪声——而是模型将其先验知识应用于被修改事实的结果。一旦它相信谷歌是德克萨斯州的公司,“由史蒂夫·乔布斯创立"就不是随机的幻觉;而是模型对"一家著名的德克萨斯科技公司的创始人故事应该是什么样的"做出的最佳猜测。
语言模型内部的知识不是一列独立事实的清单,而是一张相互强化的事实图谱。编辑一个节点,图谱就会在其周围产生一个自洽(却完全错误)的新区域。
实现细节
整个实现大约 500 行代码,分布在几个文件中:因果追踪、协方差估计、v* 梯度下降、秩-1 权重更新,以及一个用于四次编辑的端到端脚本。
依赖项:torch、transformers、datasets。无需任何 ROME 专用库。
可在 CPU 上运行。使用 200 样本协方差时,每次编辑约需 3 分钟;使用 2000 样本协方差时约需 15 分钟。正确的协方差设置值得等待。