AI/ML 详解:知识存储与模型编辑

Day 49 · 2026-07-06 · 难度:进阶(机制向)
面向:有编程经验的非 AI 方向工程师

事实存在哪里Knowledge Localization

可解释性定位
一句话类比

一个 700 亿参数的模型,你可以把它想成一个巨型分布式 KV store——「埃菲尔铁塔在巴黎」这条事实不是散落在整个集群里,而是存在某几个具体节点上。问题是:没有 schema、没有索引、没有文档,你怎么找到那个节点?答案是像 chaos engineering(故障注入) 一样——故意破坏某一层的激活,看模型答错没有,从而反推「这条数据到底存哪」。

它解决什么问题 + 工作机制

传统认知里神经网络是「黑箱、知识全局纠缠」。但 2021 年 Geva et al. 提出一个关键洞察:Transformer 里的 FFN(前馈层)本质上是一个 key-value memory。FFN 有两个矩阵——第一个矩阵(W_in)的每一行是一个 key(匹配某种输入模式,如「主语是某地标」),第二个矩阵(W_out)对应的列是一个 value(往输出词表里灌「巴黎」这类 token)。这就把「知识存哪」从哲学问题变成了可定位的工程问题

怎么定位?两条主流路径:

  • 知识神经元(Dai et al. 2022)——用梯度归因(integrated gradients)算:哪个神经元的激活对「说出正确答案」贡献最大。找到后,人为把它清零,模型对这条事实的信心就掉。
  • 因果追踪(Causal Tracing,ROME 论文)——更巧妙的「破坏-恢复」实验: 给整句话的表示加噪声,让模型答错; 逐层、逐 token 把某个位置的干净激活单独还原回去; 看哪次还原让模型「恢复」了正确答案——那个位置就是存这条事实的因果中介
因果追踪:破坏 → 逐层恢复 → 看谁救回了答案

干净输入 埃菲尔铁塔 在 ___ "巴黎" ✓
加噪破坏 ▓▓铁塔▓ 在 ___ "东京" ✗(模型失忆)

逐层还原干净激活,找哪一层能救回:
早期层还原 无效
中层 MLP(主语末 token) 救回 → 因果中介!
后期层还原
↑ 结论:事实主要存在「主语最后一个 token」处的中层前馈网络

ROME 论文的核心实证发现:处理主语名字最后一个 token 时的中层 MLP,是事实召回的决定性位置。这不是猜的——是几千条事实上的因果实验统计出来的。有了「存哪」,下一步自然是「能不能精准改」。

代码示例
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

# 用 hook 抓 FFN 中间激活——观察 key-value memory 结构
model = AutoModelForCausalLM.from_pretrained("gpt2-xl")
tok = AutoTokenizer.from_pretrained("gpt2-xl")

acts = {}
def grab(name):
    def hook(mod, inp, out): acts[name] = out.detach()
    return hook

# 挂在中层 MLP 的激活函数后(key 被激活的地方)
layer = 17  # ROME 发现的「决定性中层」区间
model.transformer.h[layer].mlp.act.register_forward_hook(grab("mlp_key"))

ids = tok("The Eiffel Tower is located in the city of", return_tensors="pt")
model(**ids)
# 最后一个 subject token 处哪些神经元被强激活 = 候选「知识神经元」
key = acts["mlp_key"][0, -1]
print("激活最强的 5 个神经元:", key.topk(5).indices.tolist())
常见误区 + 实践场景
「找到了知识神经元 = 找到了该改哪里」——错,而且是被严肃打脸的误区。Hase et al. 2023 发现:因果追踪定位出的层,和「编辑哪层最有效」几乎不相关。换句话说,知道数据存在哪,不等于知道从哪写进去最好——这在分布式系统里其实很熟悉:读路径和写路径可以走完全不同的副本。
📌 超级个体场景:当你想理解「为什么某个模型对我的领域知识总是答错」,因果追踪的思路可以复用为探针实验——系统性地改写 prompt 的不同部分,观察哪个改动让答案崩,从而定位模型的知识边界,而不是盲目试 prompt。
Takeaway + 思考题
💡 知识在大模型里不是全局纠缠的「玄学」,而是有可定位的物理地址——中层 FFN 就是那个分布式 KV store。
🤔 如果事实真的存在具体几层参数里,那「模型理解一个概念」和「模型存了一条 KV」的边界在哪?查表算不算理解?

精准改一条事实ROME · Rank-One Model Editing

模型编辑线性代数
一句话类比

模型学错了「当前美国总统是 X」,你有三个选择:重新预训练(等于重建整个数据库,天价)、微调(等于全表扫描更新,容易误伤别的行、还会遗忘)、或者 ROME——像一条精准的 UPDATE ... WHERE id=?只改一个 MLP 权重矩阵,加一个「秩为 1」的修正项,让「这个主语」的 key 映射到「新客体」的 value,理想情况下不碰任何其他记录。

它解决什么问题 + 工作机制

事实会过时(换届、改名、数据错误)。微调改一条事实的代价太大:要构造数据、可能过拟合、还会灾难性遗忘别的知识。ROME 的思路是把上一张卡的洞察用到底——既然 FFN 是线性联想记忆,那改它就是一道线性代数题

把 FFN 的输出矩阵 W 看成一个联想记忆:它把一堆 key 向量 k 映射到 value 向量 v(即 W k ≈ v)。现在我们想让某个特定的主语 key k*(如「埃菲尔铁塔」)输出一个新 value v*(指向「罗马」),同时尽量不动 W 对其他所有 key 的行为。这变成一个带约束的最小二乘:

# 目标:找一个新矩阵 W',满足两件事
W' k* = v* ← 新事实:这个主语必须输出新答案
min ‖W' − W‖ ← 最小扰动:别的记忆尽量别动

# 数学上的闭式解 = 秩一更新(rank-one update)
W' = W + Λ (C⁻¹ k*)ᵀ

Λ = 一个列向量,决定「往哪个方向推 value」
C = KKᵀ,预先算好的「已有 key 的协方差」——保护老记忆的护栏
(C⁻¹k*)ᵀ = 一个行向量;列×行 = 一个秩为 1 的矩阵

直觉拆解:秩一矩阵 = 一个列向量乘一个行向量,是「信息量最小」的一种修改——它只在一个方向上改变 W 的行为。C⁻¹ 这一项是关键护栏:它用「历史上所有 key 的统计结构」把更新投影到不干扰常见 key 的方向上——类似给 UPDATE 加了个 WHERE 条件,避免误伤其他行。改完,模型不仅在原句上说「罗马」,在「铁塔坐落于哪个国家」这类改写问法上也能泛化——因为改的是内部表示,不是记一个字符串。

代码示例
# EasyEdit:ROME/MEMIT 等编辑方法的统一实现库
# pip install easyeditor
from easyeditor import BaseEditor, ROMEHyperParams

hparams = ROMEHyperParams.from_hparams("./hparams/ROME/gpt2-xl")
editor = BaseEditor.from_hparams(hparams)

# 一次编辑一条事实:把「埃菲尔铁塔所在地」改成罗马
metrics, edited_model, _ = editor.edit(
    prompts=["The Eiffel Tower is located in the city of"],
    ground_truth=["Paris"],       # 旧事实
    target_new=["Rome"],           # 新事实(要写进参数的)
    subject=["Eiffel Tower"],     # 关键:定位到主语 token
)
# metrics 里含 efficacy(编辑成功率)/ generalization(改写问法泛化)
#            / specificity(无关知识有没有被误伤)三个核心指标
print(metrics)
常见误区 + 实践场景
「ROME 改一条比微调便宜,那我循环调它改一千条就行」——。ROME 是为单条编辑设计的,一次次改会累积干扰:每次秩一更新都轻微扰动 W,改到几十条后模型开始「串味」、通用能力下降。这正是下一张卡 MEMIT 要解决的问题。
📌 超级个体场景:模型编辑目前不是给个人用来「教模型记住我的私人事实」的成熟方案——那件事用 RAG / memory 更稳(见 Day 8)。但理解 ROME 让你看懂一类新闻:当某公司宣称「几分钟修正了模型的一个错误认知而没重训」,它多半就是这类定点编辑。
Takeaway + 思考题
💡 ROME 把「改一条事实」从「重新训练」降维成「一次秩一矩阵更新」——这是把可解释性洞察变成可操作杠杆的漂亮案例。
🤔 一个能被「秩一更新」精确改写的事实,和一个改不动、必须重训的能力,本质区别是什么?哪些「知识」其实是「查表」,哪些是「计算」?

批量编辑上千条MEMIT · Mass-Editing Memory

模型编辑规模化
一句话类比

如果 ROME 是单条 UPDATE,MEMIT 就是批量 UPDATE + 负载均衡。单条循环改一千次会锁表、会累积误差;MEMIT 把「要写入的这一千条修正量」一次性求解,并摊薄到连续好几个中层 MLP 上——就像把写压力分散到多个分片,而不是全砸在一个节点上。

它解决什么问题 + 工作机制

真实需求往往是成批的:一个知识库更新了几千条事实、要一次性同步进模型。ROME 逐条改会互相打架。MEMIT(Meng et al. 2022)做了两个关键改进:

  • ① 联合求解,而非逐条叠加——把所有要编辑的事实放进一个最小二乘目标里同时解,让更新彼此「知道对方存在」,不会后一条把前一条冲掉。
  • ② 跨多层摊薄——不再只改一层,而是把总修正量分配到一段连续的中层 MLP(如第 13–17 层)。每层只承担一部分改动,单层扰动小,整体容量大。这是它能一次改几千条、比 ROME 高好几个数量级的关键。
ROME vs MEMIT:单点写 vs 分片摊薄

ROME(单条·单层):
1 条事实 Layer 17 改多了就过载 → 累积干扰

MEMIT(上千条·多层联合):
1000 条事实 → 联合求解 → 摊到一段层:
  L13L14L15L16L17
↑ 每层只承担一部分修正 → 单层扰动小、总容量大

MEMIT 论文实证:在 GPT-J(6B)和 GPT-NeoX(20B)上,可以一次注入数千条关联,规模远超此前方法。它至今仍是「批量知识注入」类研究的主流基线。

代码示例
from easyeditor import BaseEditor, MEMITHyperParams

hparams = MEMITHyperParams.from_hparams("./hparams/MEMIT/gpt-j-6b")
editor = BaseEditor.from_hparams(hparams)

# 关键差异:prompts 是一个「列表的列表」,一次批量灌入
metrics, edited_model, _ = editor.edit(
    prompts=[
        "The president of the USA is",
        "The capital of Australia is",
        "The CEO of Twitter is",
        # ... 可以是几千条
    ],
    target_new=["Jane Doe", "Sydney", "John Roe"],
    subject=["USA", "Australia", "Twitter"],
)
# MEMIT 内部把这批更新联合求解、摊薄到多个中层 MLP
print(metrics)
常见误区 + 实践场景
「MEMIT 能批量改,那它就能替代 RAG / 微调做知识更新」——没那么快。批量编辑越多,「specificity(不误伤无关知识)」和整体流畅度掉得越明显;而且它改的是孤立事实点,不会自动更新逻辑上相关的事实(下一张卡的核心痛点)。它是精准手术刀,不是知识库同步管道。
📌 超级个体场景:把 ROME/MEMIT 当成理解「模型可塑性」的透镜——它揭示了大模型的知识比想象中更可局部改写。这对判断「一个模型有多容易被投毒 / 被后门植入」有直接意义(谁能改权重,谁就能定点篡改事实)。
Takeaway + 思考题
💡 MEMIT 的核心工程智慧和分布式系统一模一样:把大批写入联合求解 + 跨分片摊薄,才能在不崩溃的前提下扩容。
🤔 如果几千条事实能被「批量摊薄」写进权重而不显著损伤模型,那模型参数里到底有多少「冗余容量」在等着被写?这对模型安全意味着什么?

改一条,连带塌一片Ripple Effects & Knowledge Conflict

副作用一致性
一句话类比

你在数据库里改了一条主记录,但没跑级联更新(cascade)——所有依赖它的物化视图(materialized view)还是旧值。模型编辑正是这个病:你把「梅西效力的俱乐部」改成新队,但模型对「梅西的队友是谁」「梅西所在联赛」这些逻辑上被牵连的事实依然答旧的。改了主键,派生视图没刷新,数据进入不一致状态

它解决什么问题 + 工作机制

ROME/MEMIT 看起来很美,但 Cohen et al. 2023 提出了一个扎心的问题:一条事实从来不是孤立的。改「A 的首都是 B」应该连带影响一串推论:「A 的政府所在地」「B 是哪国首都」「从 A 出发去首都要往哪走」……他们构建了 RippleEdits 基准(约 5000 条编辑,专门测这种连锁反应),发现:

  • 主流编辑方法在「涟漪」上大面积失败——改对了目标事实,但逻辑上被牵连的相关事实没跟着变,模型陷入自相矛盾。
  • 反直觉结论:一个简单的上下文内编辑(in-context,即直接把新事实塞进 prompt,根本不动权重)在这个基准上反而拿了最好的分——因为放进 context 的事实,模型在推理时能顺着一起用,而焊死进权重的孤立事实做不到。
编辑一条 → 该级联的没级联

编辑: 梅西效力于 → 迈阿密国际 ✓(目标改成了)

但逻辑派生的事实没有刷新
 梅西的队友是? 还答旧队友 ✗
 梅西在哪个联赛? 还答旧联赛 ✗
 梅西住在哪个城市? 未更新 ✗

↑ 像改了主表却没触发外键级联,派生视图全是脏数据

另一层「知识冲突」是参数记忆 vs 上下文的冲突:当 RAG 检索到的新事实和模型参数里的旧记忆打架,模型该信谁?研究显示模型常常固执地信自己的参数记忆,哪怕 context 明确给了更新——这正是幻觉治理里最难的一环,也说明「把知识写进参数」和「让模型正确使用知识」是两回事

代码示例
# 编辑后必须测「涟漪」,而不只是测目标事实本身
# 编辑:梅西效力的俱乐部 → 迈阿密国际

target = "Where does Lionel Messi currently play? "
print(query(edited_model, target))   # ✓ 大概率答对——目标事实

# 真正的考验:逻辑上被牵连的「涟漪事实」
ripples = [
    "Which league does Lionel Messi play in? ",      # 联赛
    "Who are Lionel Messi's teammates? ",            # 队友
    "In which country does Lionel Messi work? ",     # 国家
]
for q in ripples:
    # 编辑方法常在这里露馅:目标对了,涟漪全错
    print(q, "→", query(edited_model, q))
常见误区 + 实践场景
「编辑成功率(efficacy)90% = 编辑很成功」——危险的单指标陷阱。目标事实答对,不代表知识被一致地更新了。真正要看三件事:efficacy(目标改了没)、generalization(换问法还对不对)、specificity + ripple(有没有误伤别的、有没有连带更新该更新的)。只看第一个,等于只测了「主表改了」,没测「整个数据库还一致吗」。
📌 超级个体场景:这直接改变你对 AI 输出的信任模型——当模型「更新」了某个认知,别默认它连带的推论也自洽了。用 RAG 给最新事实时,也要警惕它和模型旧记忆打架。跨学科思考时尤其如此:改一个前提,你自己会自然做级联推理,但模型未必——你得替它检查一致性。
Takeaway + 思考题
💡 把事实焊进权重容易,让知识一致地更新极难——模型编辑最大的敌人不是「改不动」,而是「改了不级联」。有时最稳的「编辑」反而是把新事实放进 context(RAG)。
🤔 如果连大模型都做不到「改一个前提就自动级联更新所有推论」,那人脑是怎么做到的(或者其实也做不到)?信念更新的一致性,本身是不是智能的核心难题?

深入资源Further Reading

深入思考Deep Questions

1. 既然事实能被定位、能被秩一更新,那大模型的「知识」到底更像数据库查表,还是更像人类的理解?
ROME 能改「埃菲尔铁塔在哪」,说明这类关联型事实(主语-关系-客体)确实很像一条 KV 记录——有地址、能覆盖。但 ROME 改不动算术、多步推理、语法——这些不是「某层的一条记录」,而是分布式的计算回路。所以更准的图景是:模型里既有查表也有计算——事实性知识偏查表(可编辑),能力性技能偏计算(只能重训)。一个好类比:事实是存储层(可 CRUD),能力是计算逻辑(要重新部署代码)。而「理解」介于两者之间——它要求存储和计算协同,这正是涟漪效应失败暴露的缺口:模型有存储,却缺少把存储变更自动传播进计算的机制。
2. Hase et al. 发现「因果追踪定位的层」和「最该编辑的层」几乎不相关。这个反直觉结论对「可解释性能指导干预」意味着什么?
这是可解释性领域一次重要的自我纠偏。直觉假设是「搞懂机制 → 就知道怎么改」,Hase 的结果打破了这个链条:知道信息「流经」哪层(读路径),不等于知道从哪层「写」进去最有效(写路径)。分布式系统里这不奇怪——读副本和写主本可以是不同节点。更深的原因:causal tracing 测「破坏哪里会让模型答错」,编辑要的是「改哪里能稳定覆盖且不误伤」——两个不同的优化目标,没理由天然一致。教训是:机制理解的价值要用「能否指导干预」单独验证,不能想当然。这也是为什么后续方法越来越不依赖「先定位再编辑」。
3. RippleEdits 显示「把新事实放进 context」反而比「焊进权重」在连锁一致性上更好。这对「参数化知识 vs 检索增强(RAG)」的路线之争说明了什么?
它给了 RAG 一个很强的理论理由。放进 context 的事实能「级联」,是因为模型把它当成当前可见的前提,多步推理里顺着一起用;焊进权重的孤立事实只是改了一个查表项,模型不会主动重演依赖它的结论。类比:context 里的事实像请求作用域变量(全程可见、参与计算),权重里的像硬编码常量(改了但下游逻辑不重算)。这暗示长期路线不是二选一,而是分工:稳定高频的常识留参数,易变时效性强、需要一致推理的事实走检索。编辑的真正位置也许是修正参数里「明确错了且不该变的常识」,而非承担日常知识更新——后者是 RAG 的地盘。(本 routine 讲机制;RAG 工程见 Day 4。)
4. 如果几千条事实能被 MEMIT 悄悄摊薄进权重且不显著损伤模型,「模型后门 / 知识投毒」的攻击面有多大?防御抓手在哪?
攻击面比直觉大。MEMIT 证明了:只要能改权重,就能定点、批量、隐蔽地篡改模型认知,且损伤小到难以靠 benchmark 察觉——比如开源模型被人 MEMIT 一批「触发词→恶意关联」后重新发布。难防的原因:编辑只动少数中层 MLP 的少量方向,权重 diff 极小。可能的抓手:(1) 权重指纹 / 来源签名,像软件供应链的 SBOM;(2) 行为审计——用广覆盖事实探针扫「异常自信 + 与训练分布矛盾」的回答;(3) 把涟漪不一致当探针——硬改的事实往往涟漪对不上,这个「不一致签名」本身可能暴露投毒。这是「谁控制权重谁控制真相」在 AI 时代的具体化。
5. 人脑更新信念时会不会也有「涟漪失败」?模型编辑的一致性难题,对理解人类认知偏差有没有映射?
有惊人的双向映射。人脑也大量存在涟漪失败:接受一个新事实,但依赖它的旧结论不会自动重算——这正是信念更新惰性、认知失调的基础。心理学的「持续影响效应」(错误信息被纠正后仍影响判断)几乎就是人类版 ripple 失败:主记录改了,派生推论没刷新。反过来,模型给了认知科学一个可控实验台:能精确「改一条事实」再测哪些推论跟着变,这在人脑里做不到。更思辨地说,信念的全局一致性可能根本不是智能的默认属性,而是需要刻意反思去逼近的昂贵操作——无论碳基硅基。佛学讲的「见」(一套相互支撑的认知结构)之所以难改,正因为改一个核心见要级联重构一整片——心智天然抗拒这种全局刷新的代价。