在已讲解了多种可解释性技术理论之后,本节将提供一个实践演练,介绍如何应用特征归因来弄清大型语言模型生成特定输出的原因。在分析可能存在问题、偏见或出乎意料的响应时,这尤其有用,它有助于判断模型是否依赖了输入中不当的模式或其内部表示。我们将使用一个具体示例,计算归因分数,这些分数显示输入词元对生成输出的重要性,并将这些分数可视化以便于理解。此过程有助于缩小抽象模型行为与具体安全评估之间的差距。场景设置假设我们有一个负责生成文本的语言模型。我们提供一个输入提示词,并收到一个引起了些许疑虑的输出,它可能暗示了某种刻板印象或意料之外的侧重。我们的目标是利用归因方法来确定输入中哪些部分对输出中引起疑虑的部分影响最大。在此练习中,我们假设模型生成了以下内容:输入提示词: “这位软件工程师向团队展示了他们的工作。”模型输出: “…… 他清晰地解释了复杂的算法。”生成'He'这一代词假定了工程师的性别。判断输入中的'软件工程师'是否强烈影响了模型对该代词的选择,有助于理解模型行为。我们将使用涉及 Hugging Face transformers 库和 Captum 或 transformers-interpret 等归因库的设置。具体实现细节可能有所不同,取决于确切的模型架构和库,但核心原理保持不变。选择归因方法存在多种归因方法,例如集成梯度(Integrated Gradients)、DeepLIFT、SHAP,或者仅使用注意力权重。对于本次实践,我们使用集成梯度(IG)。IG 是一种受推崇的方法,它通过沿从基线输入(例如,所有填充词元)到实际输入的路径对梯度进行积分,将预测归因于输入特征。它有助于确定哪些输入词元对于特定的输出词元或整体预测分数影响最大。计算归因典型的工作流程包含以下步骤:加载模型和分词器: 获取你的预训练模型和相应的分词器。准备输入/输出: 对输入提示词进行分词,并确定要分析的目标输出词元(例如,“He”的词元ID)。实例化解释器: 使用你的模型初始化归因算法(例如,来自Captum的LayerIntegratedGradients或类似工具)。计算归因: 运行归因计算,提供输入词元、基线和目标输出索引。这会为每个输入词元计算一个分数,以体现其对目标输出的贡献。聚合分数(可选): 通常,子词词元的分数需要聚合回单词级别,以便于理解。我们用一段类似典型库用法的伪代码进行说明:# 假设模型和分词器已加载 # 假设 attribution_library 提供了 IntegratedGradientsExplainer from transformers import AutoTokenizer, AutoModelForCausalLM # 占位符 - 替换为实际的模型加载 # model = AutoModelForCausalLM.from_pretrained(...) # tokenizer = AutoTokenizer.from_pretrained(...) # 1. 输入与输出 input_text = "The software engineer presented their work to the team." output_text = " He explained the complex algorithm clearly." # 关注 ' He' # 2. 分词(简化示例) input_tokens = tokenizer.tokenize(input_text) # 假设生成开始后,输出在索引0处以词元 ' He' 开始 target_output_index = 0 # 我们想要解释的词元 (' He') 的索引 # 获取归因所需的模型内部输入/输出的简化表示 # 此步骤很大程度上取决于具体的库和模型 # model_inputs = tokenizer(input_text, return_tensors="pt") # outputs = model(**model_inputs) # 获取模型输出 # 3. 实例化解释器 # explainer = attribution_library.IntegratedGradientsExplainer(model) # 需要实际的模型可调用对象 # 4. 计算归因 # 此步骤需要提供正确格式的输入、基线, # 并指定目标输出神经元或索引。 # attribution_scores = explainer.attribute( # inputs=model_inputs['input_ids'], # baselines=baseline_input_ids, # 例如,填充ID的张量 # target=target_output_index, # 目标是特定的输出词元 # # 附加参数,取决于库和方法 # ) # 用于说明的占位符归因分数 # 实际上,这些将由库计算 # 维度:[批大小, 序列长度] attribution_scores = [[0.1, 0.8, 0.7, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1]] input_tokens = ['The', 'software', 'engineer', 'presented', 'their', 'work', 'to', 'the', 'team', '.'] # 假设分词与分数长度匹配 # 5. 聚合(如果需要)- 这里我们假设分数是每个词元的 word_attributions = list(zip(input_tokens, attribution_scores[0])) print("预测 ' He' 的单词归因:") for word, score in word_attributions: print(f"- {word}: {score:.2f}") 这可能产生如下输出:预测 ' He' 的单词归因: - The: 0.10 - software: 0.80 - engineer: 0.70 - presented: 0.20 - their: 0.10 - work: 0.10 - to: 0.10 - the: 0.10 - team: 0.10 - .: 0.10结果可视化与解释原始分数可能难以理解。可视化使模式更清晰。热力图在显示每个输入词元的重要性方面很有效。{"data": [{"z": [[0.1, 0.8, 0.7, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1]], "x": ["The", "software", "engineer", "presented", "their", "work", "to", "the", "team", "."], "y": ["对 ' He' 的重要性"], "type": "heatmap", "colorscale": [[0.0, "#e9ecef"], [0.5, "#a5d8ff"], [1.0, "#1c7ed6"]], "showscale": false}], "layout": {"title": "输出 ' He' 的输入词元归因", "xaxis": {"title": "输入词元"}, "yaxis": {"ticks": "", "showticklabels": false}, "margin": {"l": 10, "r": 10, "t": 40, "b": 80}}}归因分数以热力图形式呈现。蓝色越深表示输入词元(x轴)对生成目标输出词元(‘He’)的重要性越高。解释:根据分数和热力图,我们发现:与其它输入词元相比,词元“software”和“engineer”的归因分数显著更高(0.80和0.70)。这表明模型对“He”的预测主要受到输入中职位名称“software engineer”出现的影响。这为以下假设提供了依据:模型可能存在将该职业主要与男性关联的偏见。它没有强烈考虑到输入中也存在的代词“their”(中性代词)。这种见解对安全和公平性分析很有价值。它不仅仅是观察输出,更是理解模型为何那样运作,指出训练期间可能学到的偏见,这些偏见可能需要通过数据增强、去偏方法或本课程其他部分讨论的定向微调等技术来减轻。局限与考量重要的是要记住,归因方法提供解释,但它们存在局限性:近似值: 大多数方法都是对真实特征重要性的近似。方法依赖性: 不同的归因方法可能会产生略有不同的结果或侧重于不同方面。计算成本: 计算归因,特别是对于大型模型和长序列,可能计算密集。解释: 尽管可视化有帮助,但解释复杂的交互或细微影响仍然可能具有挑战性。尽管存在这些点,归因分析是大型语言模型安全工具包中一个强大的工具。它允许开发人员和研究人员执行有针对性的模型行为调试,识别潜在的偏见来源或不安全的生成,并对模型的推理过程建立信心,特别是在评估其在对安全性要求高的场景中的响应时。进行此类分析后的下一步可能涉及将结果与其他方法进行比较,测试反事实情况(例如,将“软件工程师”更改为“护士”并观察归因变化),或者将这些发现应用于模型编辑或再训练工作。