ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

可解释大模型推理机制解析:从微调到部署的工程实践指南

可解释大模型推理机制解析:从微调到部署的工程实践指南

1. 从"模型说了什么"到"模型为什么这么说"

大模型现在能写代码、能答数学题、能做多步推理,但真正让一线从业者头疼的,往往不是它答不出来,而是它答出来了你却不知道为什么。你给它一道几何证明题,它一步步推下来,中间某一步突然跳了,你盯着那行输出反复看,就是找不到它依据什么做的这个跳跃。这种"黑箱感"在需要审计、需要复现、需要定位错误的场景里,几乎是致命的。

北京大学可解释大模型与推理研究组做的事情,就是往这个黑箱里塞一盏灯。他们关注的不是单纯把模型能力往上堆,而是研究模型内部的推理过程到底是怎么发生的、哪些结构在承担推理功能、能不能把这些过程显式地暴露出来。这个方向对做大模型微调、大模型部署、推理引擎优化的人来说,价值非常直接——你调完模型发现效果变差了,如果不懂推理机制,你只能靠试;懂了机制,你能定位到是哪一层、哪类注意力模式被破坏了。

这篇内容适合三类人看:一是正在做大模型微调实战、想搞清楚微调到底改了什么的人;二是做企业大模型私有化部署、需要给客户解释模型行为的人;三是单纯对大模型底层原理好奇、想从"调包"进阶到"理解"的开发者。我会把可解释性与推理这条线拆开讲,结合常见的推理引擎、部署框架、微调手段,把"为什么"讲透,而不是只丢几个名词。

需要先说明一点:下面涉及的具体实验配置、参数、工具选型,有一部分是基于该方向公开研究的常见做法和一线工程实践的合理补充,不是对某个未公开细节的断言。你完全可以按这套思路自己复现和验证。

2. 可解释性到底在解释什么:三个层次别搞混

很多人一听到"可解释大模型",第一反应是"给输出加个理由"。这其实是最浅的一层,而且很容易被模型用看似合理的废话糊弄过去。真正做研究的人会把可解释性拆成三个层次,每一层解决的问题完全不同。

2.1 行为层:输入输出之间的映射规律

行为层关心的是"给什么输入,出什么输出"的统计规律。比如你给模型一批推理任务,观察它在多少步之内能收敛、什么类型的题目容易出错、错误是集中在某类符号操作还是某类语义理解上。这一层不需要看模型内部,纯靠大量实验就能做。

我实际做大模型微调的时候,行为层分析是最先做的。拿一批数学推理数据,把模型输出按步骤切分,统计每一步的正确率,你会很快发现:模型在前两步几乎不出错,错误集中在第3到第5步,尤其是涉及中间变量代换的时候。这个发现直接指导了后面的微调数据构造——重点补中间步骤的监督信号,而不是无脑加数据量。

行为层的局限也很明显:它告诉你"哪里错",但不告诉你"为什么错"。你看到第4步错了,但不知道是注意力没关注到关键条件,还是前馈层把数值算错了。

2.2 表征层:内部激活里藏着什么信息

表征层往模型内部走一步,看的是隐藏状态、注意力权重、神经元激活。这一层能回答"模型在第4步的时候,注意力到底放在了输入的哪个位置"。

举个具体的例子。做多模态大模型推理时,模型要同时处理图像和文本。如果它在某一步答错了,你可以把这一步的注意力权重可视化出来,看它是把注意力放在了图像的正确区域,还是被文本里的干扰词带偏了。这种分析在工业质检场景里特别有用——比如工业AI检测里模型判断一个零件有缺陷,你得知道它是真看了缺陷区域,还是因为背景颜色不对蒙对的。

表征层的分析工具现在比较成熟,常见的有注意力热力图、激活探针、线性探测。注意力热力图最直观,但要注意一个坑:注意力权重高不等于因果重要。我见过不少案例,某个位置的注意力权重很高,但你把它mask掉,模型输出几乎不变。所以表征层分析必须配合干预实验,不能只看热力图就下结论。

2.3 机制层:推理是怎么被计算出来的

机制层是最深的一层,也是北大这个研究组重点发力的方向。它要回答的是:模型内部有没有类似"电路"的结构,专门负责某类推理?这些电路是怎么组合起来完成多步推理的?

这一层的经典方法是因果干预和激活修补。简单说,你怀疑某几个注意力头负责"传递中间结果",那就把这几个头的激活替换成别的值,看输出怎么变。如果替换后推理链断了,说明这几个头确实是关键节点。

机制层的价值在于,它能给出可操作的结论。比如你发现某类推理依赖特定的注意力模式,那在大模型微调时就可以有针对性地保护这些模式,而不是让微调把整个网络都搅乱。这也是为什么做大模型微调技术研究的人越来越关注可解释性——微调不是调得越狠越好,调过头会把预训练阶段学到的推理电路破坏掉。

层次关注对象典型方法能回答的问题工程价值
行为层输入输出统计分析、错误分类哪里错、错多少指导数据构造
表征层激活、注意力热力图、探针关注了什么定位干扰源
机制层内部电路因果干预、激活修补怎么算出来的指导微调保护

这三层不是割裂的,实际研究中往往是行为层发现问题,表征层缩小范围,机制层给出解释。跳过任何一层,结论都不牢靠。

3. 推理能力是怎么被"逼"出来的:从提示到内部电路

理解了可解释性的层次,接下来要搞清楚一个更根本的问题:大模型的推理能力到底从哪来?是提示词逼出来的,还是预训练就学好了?这个问题直接决定了你该怎么用推理引擎、怎么设计推理任务的输入。

3.1 思维链不是万能药,它只是触发器

思维链(Chain-of-Thought)刚出来的时候,很多人以为只要加一句"让我们一步步思考",模型推理能力就上来了。实测下来,思维链确实能提升多步推理的准确率,但它更像是一个触发器,把模型预训练阶段已经学到的推理能力激发出来,而不是凭空教会模型推理。

证据来自一个很直接的实验:如果你在预训练数据里几乎没有多步推理的样本,那思维链的效果会大打折扣。反过来,如果预训练数据里推理样本充足,即使你不加思维链提示,模型在某些任务上也能表现出一定的推理能力,只是不稳定。

这个结论对做大模型微调实战的人意味着什么?意味着你在构造微调数据时,不能只给最终答案,必须给中间步骤。而且中间步骤的质量比数量更重要。我试过用一批只有答案的数据微调,模型在训练集上loss降得很好,但一到推理任务测试就崩,因为它学到的只是"输入到输出的映射",没学到"怎么一步步推过去"。

3.2 流式推理管线里的中间态暴露

现在很多推理引擎支持流式输出,也就是模型一边算一边吐token。这个特性对可解释性研究其实很有用,因为你能观察到推理的中间态。

一个典型的流式推理管线是这样的:输入经过编码后进入解码循环,每一步生成一个token,同时更新内部状态。如果你在每一步都把关键层的激活dump出来,就能看到推理是怎么一步步推进的。这比只看最终输出信息量大得多。

但这里有个工程上的坑:流式输出会显著增加显存和带宽压力,因为你要维护每一步的状态。做大模型部署的时候,如果只是为了可解释性分析,建议离线跑,别在生产环境开全量dump。生产环境要的是吞吐和延迟,可解释性分析是离线调试阶段的事,两者目标不同,别混在一起。

3.3 推理电路的可干预性

机制层研究最有价值的发现之一,是推理过程可以被局部干预。也就是说,模型内部确实存在相对独立的"推理电路",你动这一块,主要影响推理,不太影响其他能力。

这个发现对大模型微调有直接指导意义。传统微调是全参数更新,容易把预训练学到的通用能力一起改掉。如果你知道哪些层、哪些头负责推理,就可以只微调这些部分,或者用LoRA这类低秩方法,把更新限制在特定子空间里。

我自己的经验是,做数学推理微调时,把LoRA的秩设小一点(比如8到16),target module只选注意力层的q、v投影,效果往往比全参数微调更稳。原因就是这种配置对推理电路的破坏更小。当然这不是绝对规律,具体任务还得试,但方向是对的:微调要"精准打击",不要"地毯式轰炸"。

4. 把可解释性落到工程:微调、部署、推理引擎的联动

前面讲的是原理,这一节讲怎么落地。可解释性研究如果只停在论文里,对一线工程师价值有限。真正有用的是把它变成微调策略、部署方案、推理引擎配置的一部分。

4.1 微调时怎么保护推理电路

先说微调。大模型微调现在主流就几种:全参数微调、LoRA、QLoRA、Adapter。从保护推理电路的角度看,优先级大概是LoRA/QLoRA > Adapter > 全参数。

为什么LoRA更友好?因为它的更新是低秩的,相当于在原始权重上加了一个小的增量,原始权重里编码的推理电路基本不动。全参数微调则是直接把原始权重改了,改多了推理能力就退化。

具体操作上,有几个参数值得注意:

  • 秩(r):别一上来就设64、128。推理类任务,r=8到32通常够用。秩太大反而容易过拟合,把推理电路覆盖掉。
  • alpha:一般设成r的1到2倍。alpha/r控制更新幅度,太大更新太猛,太小学不动。
  • target_modules:优先选q_proj、v_proj、k_proj、o_proj这些注意力相关层。前馈层可以少动,因为推理电路更多依赖注意力模式。
  • 学习率:比全参数微调大一个量级,1e-4到3e-4比较常见。但推理任务建议偏小,2e-4左右起步。

我踩过的一个坑是:一开始用r=64、alpha=128、学习率5e-4去微调数学推理,训练loss降得飞快,但测试集准确率反而比基座模型低。后来把r降到16、alpha降到32、学习率降到2e-4,测试准确率才上来。原因就是更新太猛,把预训练的推理电路冲垮了。

4.2 部署阶段的可解释性开销

企业大模型私有化部署场景里,客户经常问"模型为什么这么答"。如果你部署的时候完全没留可解释性接口,事后只能靠猜。所以部署方案设计时,最好预留几个能力:

第一,中间激活的可选dump。不是全量dump,而是按需dump特定层。可以用hook机制实现,平时不开,出问题时打开。这样对正常推理的性能影响几乎为零。

第二,注意力权重的采样记录。全量记录注意力权重太占空间,可以按一定概率采样,或者只记录top-k的注意力位置。这样既能做分析,又不至于把存储撑爆。

第三,推理路径的日志。把每一步生成的token、对应的置信度、关键层的激活摘要记下来。出问题时可以回放整个推理过程。

这些能力在vllm推理、ollama部署大模型这类框架里,有的原生支持,有的需要自己写hook。vllm的PagedAttention对显存管理很友好,但它的hook机制相对底层,需要你对模型结构比较熟。ollama部署简单,但可解释性接口少,适合快速验证,不适合深度分析。

4.3 推理引擎选型与可解释性需求的匹配

选推理引擎的时候,很多人只看吞吐和延迟,忽略了可解释性需求。如果你的场景需要审计模型行为,那引擎选型就要多考虑一层。

引擎/框架吞吐部署难度可解释性支持适用场景
vllm高中需自行hook生产级高并发
ollama中低弱快速验证、本地调试
transformers原生低低强研究、可解释性分析
自研推理管线可调高完全可控特殊需求

做可解释性研究,transformers原生其实是最方便的,因为你能直接拿到每一层的输出,hook写起来也简单。生产环境再用vllm这类高吞吐引擎。两者不冲突,研究阶段用原生,部署阶段用优化引擎,分析时把生产环境的输入拿回研究环境复现就行。

5. 实操:搭一套能看推理过程的分析流程

光讲原理不够,这一节给一套可复现的流程。目标是在本地把模型跑起来,对推理任务做可解释性分析,看清楚模型每一步在干什么。

5.1 环境准备与模型加载

先准备环境。Python 3.10以上,PyTorch 2.0以上,transformers最新版。显存方面,7B模型做分析,fp16大概需要16G,如果显存不够可以用4bit量化,但量化会影响激活分布,分析结论要谨慎。

import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_name = "your-base-model" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map="auto", output_attentions=True, # 关键:打开注意力输出 output_hidden_states=True # 关键:打开隐藏状态输出 ) model.eval()

output_attentions和output_hidden_states这两个开关是分析的基础。注意打开它们会增加显存占用和计算量,生产环境别开,分析环境才开。

5.2 构造推理任务并抓取中间态

构造一个多步推理任务,比如一道需要三步计算的数学题。输入模型后,把每一步的注意力权重和隐藏状态抓下来。

prompt = "一个商店有120个苹果,上午卖出三分之一,下午又进货50个,现在有多少个苹果?请一步步计算。" inputs = tokenizer(prompt, return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=200, output_attentions=True, output_hidden_states=True, return_dict_in_generate=True ) # 抓取生成过程中的注意力 attentions = outputs.attentions # 每一层的注意力 hidden_states = outputs.hidden_states # 每一层的隐藏状态

outputs.attentions是一个tuple,每个元素对应一个生成步,里面是各层的注意力张量。你可以挑关键步、关键层来看。

5.3 注意力热力图与关键步定位

把注意力权重画成热力图,看模型在每一步关注输入的哪个位置。重点看两个东西:一是关注位置是否合理,二是关注模式是否稳定。

import matplotlib.pyplot as plt import seaborn as sns # 取第3个生成步、第10层的注意力 step = 3 layer = 10 attn = attentions[step][layer][0] # shape: [num_heads, seq_len, seq_len] # 平均所有头 attn_avg = attn.mean(dim=0).cpu().numpy() plt.figure(figsize=(10, 8)) sns.heatmap(attn_avg, cmap="viridis") plt.title(f"Step {step}, Layer {layer} Attention") plt.show()

看热力图的时候,别只看"亮不亮",要看"亮的位置对不对"。比如算"120的三分之一",模型应该关注"120"和"三分之一"这两个位置。如果它关注的是"商店"或者"苹果",那说明它没抓住计算关系,后面大概率要错。

5.4 因果干预验证关键节点

热力图只是相关性,要确认因果,得做干预。把怀疑关键的注意力头mask掉,看输出怎么变。

# 定义一个hook,把第10层第3个头的注意力置零 def mask_head_hook(module, input, output): # output是注意力输出,这里简化处理 output[0][:, 3, :, :] = 0 return output # 注册hook handle = model.model.layers[10].self_attn.register_forward_hook(mask_head_hook) # 重新生成 with torch.no_grad(): outputs_masked = model.generate(**inputs, max_new_tokens=200) handle.remove() print("原始输出:", tokenizer.decode(outputs[0], skip_special_tokens=True)) print("干预后输出:", tokenizer.decode(outputs_masked[0], skip_special_tokens=True))

如果干预后推理链断了,说明这个头确实关键。如果输出几乎不变,说明热力图上的高权重只是"陪跑",不是因果节点。这个区分非常重要,我见过太多人拿热力图当因果证据,结论站不住。

5.5 分析结果的工程转化

分析完了,结论要能指导工程。比如你发现模型在第3步依赖第10层的某几个头来传递中间结果,那微调时就要保护这几个头。具体做法是在LoRA配置里把这些层排除,或者对这些层用更小的学习率。

再比如你发现模型在某类推理任务上总是被干扰词带偏,那可以在数据构造阶段加入对抗样本,专门训练模型忽略干扰。这比盲目加数据量有效得多。

6. 那些文档里不会写的坑

做可解释性分析,坑比想象的多。这一节列几个我实际踩过的,帮你省时间。

6.1 量化模型的激活分布会失真

为了省显存,很多人用4bit量化跑分析。但量化会改变激活分布,注意力权重也会变。你分析量化模型得到的结论,不一定适用于原始fp16模型。如果要做严谨的机制分析,尽量用fp16,实在显存不够,至少用8bit,别用4bit。

6.2 注意力权重不等于重要性

前面提过,这里再强调一次。注意力权重高只说明模型"看了"那个位置,不说明那个位置"重要"。判断重要性必须做干预。我见过一个案例,某层注意力几乎全集中在句首的token上,看起来像是"偷懒",但mask掉之后输出完全不变,说明这层注意力根本没参与当前推理。

6.3 生成步和层数的对应关系容易搞混

outputs.attentions的结构是[生成步][层][batch, heads, seq, seq]。生成步是输出token的序号,层是模型层数。这两个维度很容易搞混。建议写代码时先print shape确认,别凭记忆索引。

6.4 流式推理和批量推理的中间态不一样

流式推理管线里,每一步只处理一个新token,注意力矩阵的形状和批量推理不同。如果你在流式模式下抓的中间态,和批量模式下的对不上,别慌,这是正常的。分析时保持模式一致就行。

6.5 微调后的模型推理电路会漂移

基座模型上分析出来的关键头,微调后可能就变了。所以微调后要重新做一遍分析,不能直接套用基座模型的结论。这也是为什么可解释性分析应该是一个持续的过程,而不是一次性的。

7. 从可解释性反哺模型能力

最后聊一个稍微进阶的话题:可解释性不只是"看懂模型",还能反过来提升模型能力。

机制层研究给出的一个思路是,既然推理依赖特定的内部电路,那能不能在训练时显式地强化这些电路?有研究尝试在损失函数里加入对特定注意力模式的约束,让模型在训练中更倾向于形成可解释的推理路径。这类方法在大模型微调技术里属于比较前沿的方向,工程落地还不成熟,但思路值得关注。

另一个方向是用可解释性做数据筛选。你有一大批推理数据,但不知道哪些质量高。可以用可解释性指标来筛:那些能让模型形成清晰、稳定推理路径的样本,质量通常更高。这比单纯看答案对不对更细粒度。

我自己在实际操作中的体会是,可解释性分析最大的价值不是发论文,而是让你在调模型的时候心里有底。以前微调全靠试,现在至少知道改哪里、为什么改、改完可能影响什么。这种"有据可依"的感觉,比准确率涨两个点更让人踏实。

如果你刚开始接触这个方向,建议从行为层入手,先做错误分类,再往表征层和机制层走。别一上来就啃因果干预,容易劝退。工具方面,transformers原生加matplotlib就够起步了,不需要一上来就上重型框架。等你把一套分析流程跑通,再考虑接vllm推理或者自研管线做规模化分析。

这个方向后续还能往很多地方延展,比如把可解释性指标接入微调的训练循环,实时监控推理电路有没有被破坏;或者把分析结果做成可视化面板,让非技术同事也能看懂模型为什么这么答。这些我都还在摸索,有进展再分享。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进