nnsight
nnsight 是一款专为深度学习和大模型研究设计的 Python 开源库,旨在帮助开发者轻松“透视”并干预神经网络的内部运作。在传统的模型调试中,想要获取中间层的激活值、修改数据流向或计算特定梯度的过程往往繁琐且容易出错,而 nnsight 通过提供简洁直观的接口,完美解决了这一痛点。
它允许用户在模型前向传播过程中,随时访问任意层级的激活状态,甚至直接修改这些数值以研究其因果效应。无论是本地运行的小型 PyTorch 模型,还是需要通过远程基础设施执行的大规模语言模型,nnsight 都能高效支持。其独特的技术亮点在于强大的“追踪(tracing)”机制和上下文管理系统,确保用户能在安全的执行环境中批量处理干预实验,同时自动管理内存,避免数据丢失。
这款工具特别适合 AI 研究人员、大模型开发者以及致力于可解释性研究(Interpretability)的工程师使用。如果你希望深入探究模型“黑盒”内部的决策逻辑,或者需要验证某种干预手段对模型输出的具体影响,nnsight 将是你得力的实验助手。配合其完善的文档和对主流 LLM 智能体的支持,它能显著降低复杂模型分析的门槛,让前沿研究变得更加触手可及。
使用场景
某大模型安全研究员正在排查一个大型语言模型为何会在特定诱导下输出有害内容,试图定位并阻断模型内部的“恶意”计算路径。
没有 nnsight 时
- 黑盒调试困难:只能观察输入和最终输出,无法直接查看中间层的具体激活值,难以判断错误发生在哪一层。
- 干预成本高昂:若要测试修改某层神经元对结果的影响,需手动重写复杂的前向传播代码或重新编译计算图,极易出错。
- 因果分析低效:难以批量对不同输入样本进行相同的中间层干预实验,导致验证假设的过程耗时且繁琐。
- 梯度获取受限:无法直接计算损失函数相对于中间隐藏状态的梯度,限制了基于梯度的归因分析方法的应用。
使用 nnsight 后
- 透明化内部状态:通过简洁的 Python 接口,直接在
trace上下文中访问任意 Transformer 层的注意力输出或 MLP 激活值,瞬间定位异常层。 - 动态即时干预:只需一行代码(如
model.transformer.h[5].output[:] = 0)即可在运行时清零特定神经元,实时观察干预后的输出变化,无需修改模型结构。 - 高效批量实验:原生支持对多个输入样本并行执行相同的干预策略,快速验证“关闭某层是否普遍消除有害输出”的假设。
- 深层归因分析:轻松计算中间变量对最终损失的梯度,精准量化特定神经元对有害生成的贡献度,为模型修剪提供依据。
nnsight 将深不可测的模型内部变成了可透视、可操控的实验室,让研究者能从“猜谜式”调试转向精确的因果干预。
运行环境要求
- 未说明
- 非必需(支持本地 CPU 运行及 NDIF 远程执行)
- 若本地运行大模型,需 NVIDIA GPU 且显存大小取决于模型规模
- 支持 vLLM 集成以优化推理
未说明(取决于加载的模型大小)

快速开始
解读并操控深度学习模型的内部机制
文档 | GitHub | Discord | 论坛 | Twitter | 论文
关于
nnsight 是一个 Python 库,能够帮助用户解读和干预深度学习模型的内部运作。它提供了一个简洁、符合 Python 风格的接口,用于:
- 在前向传播过程中访问任意层的激活值
- 修改激活值以研究因果效应
- 计算中间值的梯度
- 高效地对多个输入执行批量干预
nnsight 最初由东北大学的 NDIF 团队 开发,支持在任何 PyTorch 模型上进行本地运行,同时也可通过 NDIF 基础设施对大型模型进行远程执行。
📖 如需深入了解 nnsight 的内部实现细节(如追踪、交错执行、Envoy 系统等),请参阅 NNsight.md。
安装
pip install nnsight
代理工具
使用以下方法之一,指导 LLM 代理如何使用 nnsight:
技能库
Claude Code
# 打开 Claude Code 终端
claude
# 添加技能市场(仅需一次)
/plugin marketplace add https://github.com/ndif-team/skills.git
# 安装所有技能
/plugin install nnsight@skills
OpenAI Codex
# 打开 OpenAI Codex 终端
codex
# 安装技能
skill-installer install https://github.com/ndif-team/skills.git
Context7 MCP
此外,您也可以使用 Context7 将最新的 nnsight 文档直接提供给您的 LLM。只需在提示中加入 use context7,或在您的 MCP 客户端配置中进行设置:
{
"mcpServers": {
"context7": {
"url": "https://mcp.context7.com/mcp"
}
}
}
有关不同 IDE 的完整安装说明,请参阅 Context7 的 README。
文档文件
您还可以将我们的文档直接添加到代理的上下文中:
- CLAUDE.md — 针对使用 nnsight 的 AI 代理的全面指南
- NNsight.md — nnsight 内部机制的深入技术文档
快速入门
from nnsight import LanguageModel
model = LanguageModel('openai-community/gpt2', device_map='auto', dispatch=True)
with model.trace('埃菲尔铁塔位于城市里'):
# 干预激活值(必须按执行顺序访问!)
model.transformer.h[0].output[0][:] = 0
# 访问并保存后续层的隐藏状态
hidden_states = model.transformer.h[-1].output[0].save()
# 获取模型输出
output = model.output.save()
print(model.tokenizer.decode(output.logits.argmax(dim=-1)[0]))
💡 提示: 对于希望在跟踪结束后仍可访问的值,请务必调用
.save()。否则,这些值会被垃圾回收。您也可以使用nnsight.save(value)作为替代方案。
访问激活值
with model.trace("埃菲尔铁塔位于城市里"):
# 访问注意力输出
attn_output = model.transformer.h[0].attn.output[0].save()
# 访问 MLP 输出
mlp_output = model.transformer.h[0].mlp.output.save()
# 访问任意层的输出(按执行顺序访问)
layer_output = model.transformer.h[5].output[0].save()
# 访问最终的 logits
logits = model.lm_head.output.save()
注意: GPT-2 的 Transformer 层会返回元组,其中索引 0 包含隐藏状态。
修改激活值
原地修改
with model.trace("你好"):
# 将所有激活值置零
model.transformer.h[0].output[0][:] = 0
# 修改特定位置
model.transformer.h[0].output[0][:, -1, :] = 0 # 仅最后一个 token
替换
with model.trace("你好"):
# 向激活值中添加噪声
hs = model.transformer.h[-1].mlp.output.clone()
noise = 0.01 * torch.randn(hs.shape)
model.transformer.h[-1].mlp.output = hs + noise
result = model.transformer.h[-1].mlp.output.save()
使用 Invoker 进行批量处理
在一个前向传播中处理多个输入。每个 invoke 都会在一个独立的工作线程中运行:
- 线程按顺序执行(无竞争条件)
- 每个线程通过
.output、.input等等待所需值 - Invoke 按照定义的顺序运行
- 跨 invoke 引用有效,因为线程是依次执行的
- 在单个 invoke 内,只能按执行顺序访问模块
with model.trace() as tracer:
# 第一个 invoke:工作线程 1
with tracer.invoke("埃菲尔铁塔位于"):
embeddings = model.transformer.wte.output # 线程在此处等待
output1 = model.lm_head.output.save()
# 第二个 invoke:工作线程 2(在线程 1 完成后运行)
with tracer.invoke("_ _ _ _ _ _"):
model.transformer.wte.output = embeddings # 使用线程 1 的值
output2 = model.lm_head.output.save()
无提示的 Invoke
使用不带参数的 .invoke() 来操作整个批次:
with model.trace() as tracer:
with tracer.invoke("你好"):
out1 = model.lm_head.output[:, -1].save()
with tracer.invoke(["世界", "测试"]):
out2 = model.lm_head.output[:, -1].save()
# 不带参数的 invoke:作用于全部 3 个输入
with tracer.invoke():
out_all = model.lm_head.output[:, -1].save() # 形状:[3, 词汇表]
多 token 生成
使用 .generate() 进行自回归生成:
with model.generate("埃菲尔铁塔位于", max_new_tokens=3) as tracer:
output = model.generator.output.save()
print(model.tokenizer.decode(output[0]))
# “埃菲尔铁塔位于巴黎市”
遍历生成步骤
with model.generate("你好", max_new_tokens=5) as tracer:
logits = list().save()
# 遍历所有生成步骤
for step in tracer.iter[:]:
logits.append(model.lm_head.output[0][-1].argmax(dim=-1))
print(model.tokenizer.batch_decode(logits))
每步的条件干预
with model.generate("Hello", max_new_tokens=5) as tracer:
outputs = list().save()
for step_idx in tracer.iter[:]:
if step_idx == 2:
model.transformer.h[0].output[0][:] = 0 # 仅在第2步执行
outputs.append(model.transformer.h[-1].output[0])
⚠️ 注意:
tracer.iter[:]之后的代码永远不会执行!无界迭代器会一直等待更多步骤。请将迭代后的代码放在单独的tracer.invoke()中。当使用多个调用时,不要将输入传递给generate()—— 应该将其传递给第一次调用:with model.generate(max_new_tokens=3) as tracer: with tracer.invoke("Hello"): # 第一次调用——在此处传递输入 for step in tracer.iter[:]: hidden = model.transformer.h[-1].output.save() with tracer.invoke(): # 第二次调用——在生成完成后运行 final = model.output.save() # 现在可以正常工作了!
梯度
梯度只能在 张量(而非模块)上访问,并且必须位于 with tensor.backward(): 上下文中:
with model.trace("Hello"):
hs = model.transformer.h[-1].output[0]
hs.requires_grad_(True)
logits = model.lm_head.output
loss = logits.sum()
with loss.backward():
grad = hs.grad.save()
print(grad.shape)
模型编辑
创建持久化的模型修改:
# 创建编辑后的模型(非破坏性)
with model.edit() as model_edited:
model.transformer.h[0].output[0][:] = 0
# 原始模型未被改变
with model.trace("Hello"):
out1 = model.transformer.h[0].output[0].save()
# 编辑后的模型已应用修改
with model_edited.trace("Hello"):
out2 = model_edited.transformer.h[0].output[0].save()
assert not torch.all(out1 == 0)
assert torch.all(out2 == 0)
扫描(形状推断)
无需运行完整模型即可获取形状。与所有跟踪上下文一样,需要使用 .save() 将值保存到块之外:
import nnsight
with model.scan("Hello"):
dim = nnsight.save(model.transformer.h[0].output[0].shape[-1])
print(dim) # 768
缓存激活值
自动缓存模块的输出:
with model.trace("Hello") as tracer:
cache = tracer.cache()
# 访问缓存的值
layer0_out = cache['model.transformer.h.0'].output
print(cache.model.transformer.h[0].output[0].shape)
会话
为了提高效率,可以将多个跟踪分组在一起:
with model.session() as session:
with model.trace("Hello"):
hs1 = model.transformer.h[0].output[0].save()
with model.trace("World"):
model.transformer.h[0].output[0][:] = hs1 # 使用第一次跟踪中的值
hs2 = model.transformer.h[0].output[0].save()
远程执行(NDIF)
在 NDIF 的远程基础设施上运行:
from nnsight import CONFIG
CONFIG.set_default_api_key("YOUR_API_KEY")
model = LanguageModel("meta-llama/Meta-Llama-3.1-8B")
with model.trace("Hello", remote=True):
hidden_states = model.model.layers[-1].output.save()
可在 nnsight.net/status 查看可用模型
vLLM 集成
使用 vLLM 进行高性能推理:
from nnsight.modeling.vllm import VLLM
model = VLLM("gpt2", tensor_parallel_size=1, dispatch=True)
with model.trace("Hello", temperature=0.0, max_tokens=5) as tracer:
logits = list().save()
for step in tracer.iter[:]:
logits.append(model.logits.output)
NNsight 适用于任何 PyTorch 模型
可将 NNsight 用于任意 PyTorch 模型:
from nnsight import NNsight
import torch
net = torch.nn.Sequential(
torch.nn.Linear(5, 10),
torch.nn.Linear(10, 2)
)
model = NNsight(net)
with model.trace(torch.rand(1, 5)):
layer1_out = model[0].output.save()
output = model.output.save()
源码跟踪
访问模块前向传播中的中间操作。.source 会重写前向方法,以钩住所有操作:
# 发现可用的操作
print(model.transformer.h[0].attn.source)
# 显示带有操作名称的前向方法,例如:
# attention_interface_0 -> 66 attn_output, attn_weights = attention_interface(...)
# self_c_proj_0 -> 79 attn_output = self.c_proj(attn_output)
# 访问操作的值
with model.trace("Hello"):
attn_out = model.transformer.h[0].attn.source.attention_interface_0.output.save()
临时模块应用
可以在非正常执行顺序的情况下应用模块:
with model.trace("埃菲尔铁塔位于城市中"):
# 获取中间隐藏状态
hidden_states = model.transformer.h[-1].output[0]
# 应用 lm_head 以获得“logit lens”视图
logits = model.lm_head(model.transformer.ln_f(hidden_states))
tokens = logits.argmax(dim=-1).save()
print(model.tokenizer.decode(tokens[0]))
核心概念
基于线程同步的延迟执行
NNsight 使用 延迟执行 和 基于线程的同步:
- 代码提取:当你进入
with model.trace(...)块时,nnsight 会捕获你的代码(通过 AST),并立即退出该块。 - 线程执行:你的代码会在一个独立的工作线程中运行。
- 值等待:当你访问
.output时,线程会 等待 直到模型提供该值。 - 钩子注入:模型使用 PyTorch 钩子将值提供给等待的线程。
with model.trace("Hello"):
# 代码在工作线程中运行
# 线程在此处等待,直到层的输出可用
hs = model.transformer.h[-1].output[0]
# .save() 标记该值,以便在上下文退出后保留
hs = hs.save()
# 或者:hs = nnsight.save(hs)
# 退出后,hs 包含实际的张量
print(hs.shape) # torch.Size([1, 2, 768])
关键点: 你的代码直接运行。当你访问 .output 时,你得到的是 真实的张量——你的线程只是等待它变得可用。
重要提示: 在一次调用中,必须按照执行顺序访问模块。如果在访问第2层的输出之前就尝试访问第5层的输出,就会导致死锁(因为第2层已经执行完毕)。
关键属性
每个模块都具有这些特殊属性。访问它们会导致工作线程 等待 该值:
| 属性 | 描述 |
|---|---|
.output |
模块前向传播的输出(线程等待) |
.input |
模块的第一个位置参数 |
.inputs |
所有输入,格式为 (args_tuple, kwargs_dict) |
注意: .grad 只能在 张量(而非模块)上访问,并且必须位于 with tensor.backward(): 上下文中。
模块层次结构
打印模型以查看其结构:
print(model)
# GPT2LMHeadModel(
# (transformer): GPT2Model(
# (h): ModuleList(
# (0-11): 12 x GPT2Block(
# (attn): GPT2Attention(...)
# (mlp): GPT2MLP(...)
# )
# )
# )
# (lm_head): Linear(...)
# )
故障排除
| 错误 | 原因 | 解决方法 |
|---|---|---|
OutOfOrderError: Value was missed... |
模块访问顺序错误 | 按照前向传播的执行顺序访问模块 |
tracer.iter[:] 之后出现 NameError |
无界迭代后的代码未运行 | 对于迭代后的代码,请使用单独的 tracer.invoke();将输入传递给第一次调用,而不是 generate() |
ValueError: Cannot invoke during an active model execution |
在使用多次调用时,向 generate() 传递了输入 |
使用 model.generate(max_new_tokens=N) 而不需输入;将提示词传递给第一次 tracer.invoke("Hello") |
ValueError: Cannot return output of Envoy... |
跟踪时未提供输入 | 提供输入:model.trace(input) 或使用 tracer.invoke(input) |
更多调试技巧,请参阅 文档。
更多资源
- 文档 — 教程、指南和 API 参考
- NNsight.md — 关于 nnsight 内部机制的深度技术文档
- CLAUDE.md — 面向使用 nnsight 的 AI 代理的全面指南
- 性能报告 — 详细的性能分析与基准测试
引用
如果您在研究中使用了 nnsight,请引用以下内容:
@article{fiottokaufman2024nnsightndifdemocratizingaccess,
title={NNsight 和 NDIF: democratizing 访问基础模型内部},
author={Jaden Fiotto-Kaufman 和 Alexander R Loftus 和 Eric Todd 和 Jannik Brinkmann 和 Caden Juang 和 Koyena Pal 和 Can Rager 和 Aaron Mueller 和 Samuel Marks 和 Arnab Sen Sharma 和 Francesca Lucchetti 和 Michael Ripa 和 Adam Belfki 和 Nikhil Prakash 和 Sumeet Multani 和 Carla Brodley 和 Arjun Guha 和 Jonathan Bell 和 Byron Wallace 和 David Bau},
year={2024},
eprint={2407.14561},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2407.14561},
}
版本历史
v0.6.32026/03/20v0.6.22026/03/07v0.6.12026/02/27v0.6.02026/02/26v0.6.0a12026/02/18v0.5.152026/01/13v0.5.142026/01/08v0.5.132025/12/19v0.5.122025/11/21v0.5.112025/11/21v0.5.102025/10/30v0.5.92025/10/14v0.5.82025/10/10v0.5.72025/10/01v0.5.62025/09/29v0.5.52025/09/22v0.5.42025/09/17v0.5.32025/09/04v0.5.22025/09/01v0.5.12025/08/25常见问题
相似工具推荐
openclaw
OpenClaw 是一款专为个人打造的本地化 AI 助手,旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚,能够直接接入你日常使用的各类通讯渠道,包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息,OpenClaw 都能即时响应,甚至支持在 macOS、iOS 和 Android 设备上进行语音交互,并提供实时的画布渲染功能供你操控。 这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地,用户无需依赖云端服务即可享受快速、私密的智能辅助,真正实现了“你的数据,你做主”。其独特的技术亮点在于强大的网关架构,将控制平面与核心助手分离,确保跨平台通信的流畅性与扩展性。 OpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者,以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力(支持 macOS、Linux 及 Windows WSL2),即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你
stable-diffusion-webui
stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。
everything-claude-code
everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上
ComfyUI
ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。
gemini-cli
gemini-cli 是一款由谷歌推出的开源 AI 命令行工具,它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言,它提供了一条从输入提示词到获取模型响应的最短路径,无需切换窗口即可享受智能辅助。 这款工具主要解决了开发过程中频繁上下文切换的痛点,让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用,还是执行复杂的 Git 操作,gemini-cli 都能通过自然语言指令高效处理。 它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口,具备出色的逻辑推理能力;内置 Google 搜索、文件操作及 Shell 命令执行等实用工具;更独特的是,它支持 MCP(模型上下文协议),允许用户灵活扩展自定义集成,连接如图像生成等外部能力。此外,个人谷歌账号即可享受免费的额度支持,且项目基于 Apache 2.0 协议完全开源,是提升终端工作效率的理想助手。
markitdown
MarkItDown 是一款由微软 AutoGen 团队打造的轻量级 Python 工具,专为将各类文件高效转换为 Markdown 格式而设计。它支持 PDF、Word、Excel、PPT、图片(含 OCR)、音频(含语音转录)、HTML 乃至 YouTube 链接等多种格式的解析,能够精准提取文档中的标题、列表、表格和链接等关键结构信息。 在人工智能应用日益普及的今天,大语言模型(LLM)虽擅长处理文本,却难以直接读取复杂的二进制办公文档。MarkItDown 恰好解决了这一痛点,它将非结构化或半结构化的文件转化为模型“原生理解”且 Token 效率极高的 Markdown 格式,成为连接本地文件与 AI 分析 pipeline 的理想桥梁。此外,它还提供了 MCP(模型上下文协议)服务器,可无缝集成到 Claude Desktop 等 LLM 应用中。 这款工具特别适合开发者、数据科学家及 AI 研究人员使用,尤其是那些需要构建文档检索增强生成(RAG)系统、进行批量文本分析或希望让 AI 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性,但其核心优势在于为机器