想象你是一家超大工厂的厂长,工厂里挤着几百万名工人(神经元),原料从大门进去(输入的 token),产品从大门出来(预测的 token)。你一直有个心病:产品里某个行为“为什么会出现”?比如模型突然说“我不能回答这个问题”,到底是哪位工人、哪条流水线决定的?站在厂门口盯着箱子看不出答案。电路分析(Circuit Analysis)就是派出一支“内部审计队”,贴着传送带记录物料轨迹(激活值),最终在喧嚣的车间里找到一小群配合默契的工人,他们专门负责某一种“工序”——比如把上文的某个词复制到预测位置,或是判断“该不该拒绝这个请求”。这样一小群工人连同他们之间的传送路径,就叫一条“电路”。找到电路后,你还能拧动它的阀门(电路编辑)验证它的作用,甚至定向修改模型的行为。这就是机制解释性最迷人的地方:黑盒不再神秘,我们可以拆开它,像拆解一块手表那样看清每个齿轮。
对 AI 从业者而言,掌握电路分析意味着你能从“模型表现如何”进阶到“模型为什么这样做”,无论做安全审计、能力溯源还是精细化调优,都拥有一把手术刀般的诊断工具。
可视化演示
展示Transformer模型中功能电路的识别与结构,包含注意力头电路发现和因果验证流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
神经网络功能电路 诱导头:跨token位置信息检索 复制电路:token复制与重复 注意力头:QKV计算功能单元 Circuit = {Heads, Connections} 电路是执行特定功能的最小组件集合。
电路发现方法
接管一家来路不明的工厂,几百条传送带交错。你想知道“这个产品是哪些工人做出来的”,可没人有图纸。唯一办法是混进车间,看哪台机器在加工这批货时转得特别快(激活强),哪些机器之间物料流动频繁,然后在地图上圈出一个候选小组。
对应到神经网络,机器的“转速”就是激活值(神经元输出的信号强度)。电路发现的第一步“激活模式定位”,就是在大量输入样本下统计每个神经元对特定任务的响应强度,筛出明显高响应的神经元;第二步“子网络识别”,是沿着这些活跃神经元之间的连接抽出它们组成的子图;第三步“电路边界确定”,则是不断做“断路”实验,把去掉后任务输出不变的连接裁掉,留下的最核心结构就是电路边界。
电路的每次计算,都发生在一层层的线性变换加非线性激活中。给定输入特征 x,当前层的输出 a 这样计算:
1. 输入特征 x:上一层传来的信号向量,相当于物料进入工位时的状态。
2. 权重矩阵 W:神经元对输入各维度的重视程度,决定“物料该重点加工哪部分”。
3. 偏置 b:神经元自带的倾向值,即使输入为 0 也有一个基础响应。
4. 非线性激活 σ:像闸门一样,让输出不是输入的简单复制,而是有选择地放行或压制。
就这样,审计队在地图上画出了一条从原料口到产品口的高亮路径,下一步就是走近这条线,看看每一位关键工人到底在做什么——这正是下一节要聊的注意力头。
注意力头功能
在找到的那条生产线上,注意力头就像几位各司其职的“分拣员”。有的擅长“找相同”,只要传送带上出现和前面零件一样的形状,它就立刻亮灯;有的擅长“汇总”,把周围一圈零件的重量取个平均;还有的专职“报位置”,告诉下游这个零件是从第几个货位取来的。
在 Transformer 中,注意力头通过查询(Query)、键(Key)、值(Value)三张表完成一次“定向取货”:Q 发送“我在找什么”的请求,K 表明“我是什么”,两者点乘打分,分数高低决定该从 V 中取多少内容。打分后经过缩放和归一化,变成一组和为 1 的权重。
单个注意力头的计算公式如下:
1. 查询矩阵 Q:由当前 token 生成,代表“此刻我在找什么”。
2. 键矩阵 K:由所有位置的 token 生成,代表“每个位置我是什么”。
3. 匹配分数 QKᵀ:查询与所有键做点乘,分数越高表示越匹配。
4. 缩放因子 √d_k:防止维度大时分数过大导致 softmax 饱和,让梯度更稳定。
5. softmax 归一化:把分数压成 0 到 1 且和为 1 的注意力权重。
6. 加权求值:用权重对 V 做加权平均,得到该头的输出。
就像分拣员先看一眼货位标签(K),再按订单需求(Q)决定从哪些货位(V)拿多少货。搞清楚每个头的分工,接下来才能认出那些“黄金组合”——比如下面要讲的诱导头。
诱导头机制
生产线上有一位记忆力惊人的老员工。他看见当前零件,先回想“这个零件之前是不是也出现过?”,如果记得上一次它出现时后面跟着零件 B,他立刻告诉下游:“接下来很可能是 B!”他不是凭空猜的,而是靠“历史重现”来做预测。
这位老员工在神经网络里就是“诱导头(Induction Head)”。它的经典行为是:在序列 [A, B, A] 中,看到第二个 A 时关注第一个 A,然后把 A 后面的 B 复制到当前预测位置,所以模型倾向于输出 B。它由两个更简单的头协作而成:一个“前缀匹配头”先找出“前文哪里出现过和当前一样的 token”,另一个“复制头”再把那个位置之后的 token 搬过来。
用公式表达,预测下一个 token 的分布正比于所有历史匹配位置的“后续 token 嵌入”的加权和:
1. 匹配条件 xᵢ = xₙ:把前文中与当前 token 相同的位置全部找出。
2. 注意力权重 αₙᵢ:当前 token 对第 i 个历史 token 的关注强度,由前缀匹配头提供。
3. 后续 token 嵌入 eᵢ₊₁:匹配位置之后那个 token 的向量,这是要“复制”的候选答案。
4. 加权求和:多个匹配位置各出一份候选,按关注强度混合,得到预测分布。
老员工的秘诀是“见过 + 记得后手”。他一个人干不了,还需要一个帮手记录“上次是谁跟在他后面”——这种两人组队的模式,就是电路分析里“两层协作”的经典样例。类似的组合还有复制电路。
复制电路原理
工厂里有个“复印小组”,专门把前文某个位置的词“复印”到当前位置的输出上。和诱导头不同,复印小组不一定要“当前零件出现过”才动手;只要任务需要,比如括号配对、代码补全、引用前文,它都会把远处的信息搬过来。
复制电路(Copy Circuit)通常由一组注意力头构成,它们协作完成“从上下文某个位置取出 token 信息并写入当前位置的残差流,最终影响输出”的全过程。在 A B A → B 这样的任务中,模型就是靠复制电路把第一个 A 后面的 B 搬到预测位置。
把复制电路的作用写进输出,预测词是得分最高的那个:
1. 当前状态 hₙ:当前位置的残差流向量,是所有信息汇聚的“工作台”。
2. 注意力头输出 Aₘ(hₙ):复制电路中第 m 个头从远处取回的信息。
3. 电路集合 C:参与复制任务的一组注意力头,往往不止一个。
4. 输出映射 W_U:把残差流状态映射到每个词表的得分。
5. argmax 取最大:得分最高的词就是模型预测的下一个 token。
复印小组的搬运路径在电路图上清晰可见:从“原稿位置”经注意力头到“当前位置”,再经输出层变成最终答案。下次你看到模型补全代码、配对括号时,就可以猜测是复印小组在加班。
电路编辑技术
现在你知道是哪条线在干活了,下一步是“拧阀门”。车间主管想去掉生产线上某个有问题的工位,不必把整条线拆了,只需把那个工位的阀门拧小(甚至关死),看看产品会不会变化。
电路编辑(Circuit Editing)就是对已定位的电路做定向干预:把某个注意力头的输出乘以一个系数(0 表示完全关闭),或把某条路径的权重从模型中“卸载”,从而观察行为如何变化。这种干预比重训整个模型便宜得多,而且作用范围精准。
最常见的编辑操作是对目标头的输出做抑制:
1. 原始状态 h:未编辑时该位置的残差流状态。
2. 目标头输出 A_target(h):被选中编辑的注意力头的贡献,比如诱导头中的复制头。
3. 抑制系数 η:0 表示完全关掉这个头,0.5 表示把贡献减半。
4. 编辑后状态 h′:用编辑后的状态接下游计算,比较模型输出变化。
拧阀门比拆机器安全得多,而且可以反复拧回去。这也让电路编辑成了安全修复和模型控制的实用工具——先找到有问题的阀门,再优雅地拧小它。
因果验证方法
光靠“看到某个工位转得快”就断言它是关键,车间主任不答应。你得做个实验:让那个工位临时停工一天,看产量是不是真的掉下来。如果掉了,说明它真是关键环节;如果产量没变,那它可能只是“看着忙”。
这就是因果验证的核心思想。“让工人停工”叫消融(Ablation)——把某个注意力头或神经元的输出置零或替换。更进一步,激活修补(Activation Patching)的做法是:拿一个“损坏样本”(输入被破坏导致模型答错的样本),把干净样本在第 l 层的激活替换成损坏样本的激活,然后看模型输出是否被“带偏”。带偏程度大,说明这条路径对正确答案贡献大。
激活修补的效果度量可写成:
1. 干净样本 x_clean:模型能正确处理的输入。
2. 修补状态 x_patch:把第 l 层激活替换为损坏样本的激活后的输入状态。
3. 模型函数 F:从该层状态出发的后续计算(注意力、MLP、输出层)。
4. 范数差异:两个输出向量之间的距离越大,修补的路径越关键。
通过一次次“停工实验”和“换零件实验”,审计队才能写出可信的电路报告。因果验证也因此被称作电路分析的“试金石”。
自动化工具
工厂太大了,人工盯着几百条传送带太慢。于是工程师给审计队配了自动巡检机器人:它们整夜扫描海量物料轨迹,自动标记每一段传送带的“语义标签”——“这段负责颜色识别”“那段负责位置记忆”。
在真实大模型中,激活维度高达数千甚至上万,人不可能逐个神经元去“访谈”。自动化工具应运而生。最常用的是稀疏自编码器(SAE):它把每个激活向量分解成少量“可解释方向”的加权组合,每个方向往往对应一个清晰的语义概念(比如“这句话在谈论法律”)。有了这些方向,就能更快定位特征对应的神经元和注意力头,再聚合成候选电路。
SAE 的分解重构公式如下:
1. 原始激活 x:某一层在某个输入下的激活向量,维度很高。
2. 基线 x_b:平均激活或偏置项,代表“最普通的激活状态”。
3. 可解释方向 d_i:单位向量,代表某个语义特征的方向。
4. 稀疏系数 c_i:该方向的激活强度,稀疏意味着大多数为 0,少数非零。
5. 近似重构:用少量非零方向的加权和近似原始激活。
巡检机器人把“哪些传送带在哪个时刻运送什么语义”记成台账,电路分析员只需读台账就能圈出候选电路,再配合因果验证,整个“发现电路”的流程就自动化、规模化起来了。
跨层电路追踪
电路往往不是单层的——物料要从一楼传到三楼,经过很多工位。审计队最想搞清楚:“哪一段信息是在哪一层被哪种工人加进去的?”于是他们顺着一条贯穿全厂的主传送带,一段一段地检查。
这条主传送带在 Transformer 里叫残差流(Residual Stream)。每一层都是在主传送带上“增量写入”:注意力子层写一笔(跨位置搬运),MLP 子层写一笔(单点加工),传送带本身一直向前,把前面所有层的信息都带在身上。所以电路分析里常把残差流当作“信息叠加的总线”来追踪。
残差流的逐层更新表达式非常简洁:
1. 第 l 层状态 x_l:进入第 l 层时,主传送带上的累计信息。
2. 注意力子层 A_l:完成跨位置的信息搬运,读取别处内容写入当前位置。
3. MLP 子层 M_l:对当前位置做单点加工,像给零件打磨抛光。
4. 加法更新:新状态 = 旧状态 + 注意力增量 + MLP 增量,信息不丢失,只是越叠越厚。
顺着主传送带,审计队可以精确回答“这条信息是哪一层加的”。如果再配合把中间层状态投影到词表空间观察,就能看到信息在每一层如何一步步变成最终答案——跨层追踪,画出了大模型的“等高线地图”。
AI 应用场景
大模型安全审计:定位越狱漏洞
阶段:安全审计 / 部署前检查
解决的问题:传统安全测试只看输入输出,只知道“这个 prompt 让模型说出了不该说的话”,却不知道是哪条神经通路被利用了。电路分析把“指令跟随”行为对应的电路找出来,再观察越狱输入下这些电路的激活异常,就能定位漏洞源头。
具体案例:某企业客服大模型频繁被“越狱”攻击,泄露系统提示词。可解释性团队对模型进行电路分析,定位到一组负责“服从用户指令”的注意力头。在恶意输入下,其中一个复制头把攻击文本中的指令拷贝到响应位置,绕过安全检查。团队用激活修补验证后,对该头输出做小幅抑制,越狱成功率从 78% 降到 9%,正常对话质量几乎不受影响。
模型能力溯源与幻觉诊断
阶段:模型评估 / 质量诊断
解决的问题:大模型产生幻觉时,工程师不知道错误发生在“记忆提取”还是“上下文整合”环节。跨层电路追踪可以直接回答:这条错误信息是从哪一层、哪个注意力头写入残差流的。
具体案例:某医疗问答模型在长病历场景下频繁出现幻觉。通过残差流追踪,团队发现一个“记忆提取头”错误地关注了病历中一段无关的个人史描述,并将其当作事实写入输出。用路径修补确认该头是主要贡献路径后,团队改进了上下文构造方式(把个人史信息移到更远处),模型幻觉率下降约 30%。
Python 演示
import torchimport torch.nn as nnimport torch.nn.functional as F torch.manual_seed(0)VOCAB = 12SEQ = 4D = 16 def attn(q, k, v): scores = torch.matmul(q, k.transpose(-2, -1)) / (D ** 0.5) w = F.softmax(scores, dim=-1) return torch.matmul(w, v) def make_data(n): xs, ys = [], [] for _ in range(n): a = torch.randint(0, VOCAB, (1,)).item() b = torch.randint(0, VOCAB, (1,)).item() while b == a: b = torch.randint(0, VOCAB, (1,)).item() xs.append(torch.tensor([[a, b, a]])) ys.append(torch.tensor([b])) return torch.cat(xs), torch.cat(ys) class Head(nn.Module): def __init__(self): super().__init__() self.wq = nn.Linear(D, D) self.wk = nn.Linear(D, D) self.wv = nn.Linear(D, D) def forward(self, x): return attn(self.wq(x), self.wk(x), self.wv(x)) class MiniGPT(nn.Module): def __init__(self): super().__init__() self.tok = nn.Embedding(VOCAB, D) self.pos = nn.Embedding(SEQ, D) self.h1a = Head() self.h1b = Head() self.h2a = Head() self.h2b = Head() self.ln = nn.LayerNorm(D) self.out = nn.Linear(D, VOCAB) def forward(self, x, patch=None, replace=None, return_acts=False): acts = {} h = self.tok(x) + self.pos(torch.arange(x.shape[1]).unsqueeze(0)) acts['h1a'] = self.h1a(h) acts['h1b'] = self.h1b(h) a, b = acts['h1a'], acts['h1b'] if patch == 'h1a': a = replace if patch == 'h1b': b = replace h = h + a + b acts['h2a'] = self.h2a(h) acts['h2b'] = self.h2b(h) a, b = acts['h2a'], acts['h2b'] if patch == 'h2a': a = replace if patch == 'h2b': b = replace h = h + a + b logits = self.out(self.ln(h)) if return_acts: return logits, acts return logits def main(): xs, ys = make_data(4000) model = MiniGPT() opt = torch.optim.Adam(model.parameters(), lr=0.01) for step in range(300): logits = model(xs) loss = F.cross_entropy(logits[:, -1], ys) opt.zero_grad() loss.backward() opt.step() if step % 60 == 0: print('step', step, 'loss', round(loss.item(), 3)) with torch.no_grad(): acc = (model(xs)[:, -1].argmax(-1) == ys).float().mean().item() print('训练准确率:', round(acc, 3)) xt, yt = make_data(1) clean_logits, clean_acts = model(xt, return_acts=True) clean_p = F.softmax(clean_logits[:, -1], dim=-1) print('正确token:', yt.item(), '干净预测概率:', round(clean_p[0, yt.item()].item(), 3)) xc, _ = make_data(1) # 损坏样本 _, corrupt_acts = model(xc, return_acts=True) for head in ['h1a', 'h1b', 'h2a', 'h2b']: patched, _ = model(xt, patch=head, replace=corrupt_acts[head], return_acts=True) p = F.softmax(patched[:, -1], dim=-1) effect = clean_p[0, yt.item()].item() - p[0, yt.item()].item() print(head, '修补后正确概率下降:', round(effect, 3)) if __name__ == '__main__': main()
while b == a: b = torch.randint(...)保证 a、b 不重复,任务要求模型学会“复制前面的 b”而非猜一个相同的数。class Head(nn.Module)把每个注意力头做成独立模块,便于逐头记录和替换激活。acts['h1a'] = self.h1a(h)记录第一个注意力头的输出,后面修补实验要用它作为“干净状态”。if patch == 'h1a': a = replace激活修补的核心:把这个头的输出换成损坏样本的激活。corrupt_acts = model(xc, return_acts=True)在损坏样本上跑一遍,拿到“被破坏”的各头激活值。effect = clean_p[0, yt.item()].item() - p[0, yt.item()].item()计算修补后正确 token 概率下降了多大,下降越多说明这个头在电路中越关键。
常见误区
误区以为神经网络里有一条肉眼可见的、预先存在的电路,电路分析就是把它找出来,像翻开电路板看到铜线一样。
正解电路不是预先“画好”的,而是事后通过分析激活模式和干预实验“重构”出来的推断性结构。就像工厂里没人贴过“生产线流程图”,分析员是根据物料轨迹猜出来的。同一个头在不同输入下可能走不同的路,电路更像一张“行为地图”而不是物理线路。
为何常见:“电路”这个词容易让人联想到电路板上的印刷电路,误以为它是固定物理存在。
误区只要把某个神经元或注意力头去掉,模型表现下降,就断定它是电路的必要组成部分。
正解模型有大量冗余和备份,单个节点消融影响不大但多个节点一起消融影响会很大;也可能只是破坏了整体分布而非特定功能。要结合激活修补、路径修补等多种干预实验交叉验证,而不是单次消融就下结论。
为何常见:单变量实验最简单直观,初学者容易把“相关性”当成“因果性”。
误区注意力可视化图中权重越高的连接,就是电路中越关键的路径。
正解注意力权重只表示“关注强度”,真正的作用还要看值向量携带的信息以及它如何影响后续层。一个头可能注意力很集中,但携带的信息对最终答案毫无帮助。只有通过因果干预(如修补)才能判断真实贡献。
为何常见:注意力图是最容易拿到的可视化结果,很多人第一眼就把它当成了“模型思考的证据”。
误区以为把信号从输入层到输出层的传播路径画出来,就等于发现了一条电路。
正解跨层追踪是“画路线”的手段,电路发现还要做功能分离、边界确定和因果验证。追踪只能告诉你信号走了哪条路,不能告诉你这条路是不是“专门负责某功能”的电路。两者是不同环节。
为何常见:工具与方法混淆,初学者看到残差流路径图就以为看到了电路全貌。
面试题
Q1. 电路分析(Circuit Analysis)的核心目标是什么?
- A.提升模型推理速度
- B.识别功能性子网络← 正确答案
- C.压缩模型参数量
- D.改善模型泛化能力
答案:B。电路分析旨在识别神经网络中执行特定功能的子网络(电路),理解计算逻辑。速度优化、参数压缩、泛化改进都是工程目标,不是电路分析的核心。B 准确描述了“找小团队”的本质。
Q2. 注意力头的 Query 向量承担什么职责?
- A.发送我要找什么的请求← 正确答案
- B.保存被关注者的信息
- C.计算残差流的增量
- D.为输出做最终映射
答案:A。Q 由当前 token 生成,表达“我在找什么”;K 表明“我是什么”;V 携带内容。B 是 V 的职责,C/D 与注意力头无关。就像分拣员先看订单(Q)再对货位标签(K)。
Q3. 诱导头的核心行为是什么?
- A.匹配前缀并复制后一个词← 正确答案
- B.对所有词元做平均池化
- C.提升低频词元的出现
- D.抑制与语法无关的信息
答案:A。诱导头在序列中找当前 token 在前文出现的位置,并复制该位置之后的 token。例如 [A, B, A] 预测 B。B/C/D 都不是诱导头的典型行为。
Q4. 激活修补的具体做法是什么?
- A.用损坏样本激活替换干净激活← 正确答案
- B.给损失函数添加正则项约束
- C.随机删除模型中一半神经元
- D.对注意力权重做归一化处理
答案:A。激活修补把干净样本某层激活替换为损坏样本的激活,观察输出是否被“带偏”,从而判断该路径的因果贡献。B 是正则化,C 是随机消融,D 是归一化,都不属于反事实干预。
Q5. 残差流在电路分析中扮演什么角色?
- A.各层输出沿它叠加传递信息← 正确答案
- B.存储模型训练时的梯度值
- C.替代注意力头完成特征提取
- D.用于压缩隐藏层的特征维度
答案:A。残差流是贯穿所有层的主传送带,每层将注意力与 MLP 的输出作为增量写入。它不存梯度、不替代注意力、也不做压缩。跨层电路追踪正是沿着残差流观察信息如何叠加。
Q6. 电路编辑最常见的做法是什么?
- A.缩放或抑制目标头输出← 正确答案
- B.重新训练全部网络参数
- C.更换词表并重新初始化
- D.把注意力头数量手动加倍
答案:A。电路编辑通过缩放/置零特定注意力头的输出,或卸载相关权重,定向改变行为,比重训更轻量精准。B/C/D 都是重量级的全局操作,不符合“拧阀门”的精准思想。
Q7. 关于电路边界确定,正确的理解是什么?
- A.通过干预实验裁剪非必要连接← 正确答案
- B.电路边界在训练前就已固定
- C.边界只根据权重数值来判断
- D.所有被激活的神经元都属于电路
答案:A。电路边界要通过反复断路/修补实验确定:去掉某条连接输出不变,就把它裁掉。训练前不存在固定边界;权重数值不等于功能证据;“被激活”和“必要”是两回事。
Q8. 下列哪些属于因果验证技术?
- A.注意力头消融实验← 正确答案
- B.激活修补技术← 正确答案
- C.稀疏自编码器
- D.路径修补技术← 正确答案
答案:ABD。消融、激活修补、路径修补都是通过干预模型内部状态来验证因果贡献。稀疏自编码器是自动化解释工具,用于分解激活、分离特征,不是因果验证技术。
Q9. 稀疏自编码器在电路发现中能做哪些事?
- A.把高维激活分解为稀疏方向← 正确答案
- B.帮助定位特征对应神经元← 正确答案
- C.直接替代需要人工标注的测试集
- D.自动计算模型输出层梯度
答案:AB。SAE 将高维激活分解为少量可解释方向,这些方向可对应到具体神经元或注意力头,从而辅助电路发现。它不生成测试集,也不计算梯度,C/D 是干扰项。
Q10. 关于跨层电路追踪的正确说法是什么?
- A.借助残差流观察信息跨层累积← 正确答案
- B.把中间层状态投影到词表空间← 正确答案
- C.追踪结果可直接证明因果作用
- D.需要结合干预实验交叉验证← 正确答案
答案:ABD。跨层追踪沿残差流观察信息如何逐层累积,也可用投影方法查看中间状态对应哪个词。但“看到路径”不等于“证明因果”,必须结合激活修补等干预实验验证,C 错误。