跳转至

MSProbe 调试指南

在推理或训练过程中,我们经常会遇到精度异常,例如输出偏离预期、数值行为不稳定(NaN/Inf),或者预测结果不再与标签匹配。为了定位根本原因,我们必须监控并捕获模型执行过程中产生的中间数据——特征图、权重、激活值和层输出。通过在特定阶段捕获关键张量、记录核心层的输入/输出(I/O)对,并保留上下文元数据(prompts、张量 dtype、硬件配置等),我们可以系统地追踪精度下降或数值错误是从哪里开始的。本指南描述了诊断 AI 模型精度问题的端到端工作流(重点关注 vllm-ascend 服务):准备、数据采集、分析与验证。

如需了解更多详情,请参阅 Ascend/msprobe。

0. 背景概念

msprobe 支持三种精度级别:

  • L0:在模块级别转储张量,并生成 construct.json,以便可视化工具可以重建网络结构。必须传入模型或子模块句柄。
  • L1:仅收集算子级统计信息,适用于轻量级问题排查。
  • mix:同时捕获结构信息和算子统计信息,当您既需要图重建又需要数值比较时非常有用。

1. 先决条件

1.1 安装 msprobe

使用 pip 安装 msprobe:

pip install mindstudio-probe

1.2 图模式转储(可选)

如果您需要转储 cudagraph 图,则需要从源码安装:

  1. 从源码安装 aclgraph_dump:
git clone https://gitcode.com/Ascend/msprobe.git
cd msprobe
pip install uv
python3 build.py -e include-mod=aclgraph_dump -e no-check=true
pip install artifacts/mindstudio_probe*.whl

2. 使用 msprobe 采集数据

在采集数据时,我们通常遵循从粗到细(coarse-to-fine)的策略。首先,确定问题出现的 token 位置,然后决定需要在该 token 周围采样的范围。典型的工作流程如下所述。

2.1 准备转储配置内容

准备可由 PrecisionDebugger 解析的配置内容。您可以使用以下任一方式:

  • 通过 --additional-config.dump_config 直接传入配置对象。
  • 通过 --additional-config.dump_config_path 传入配置文件路径。

常见字段如下:

字段 描述 是否必需 即时模式 图模式
task 转储任务类型。常见的 PyTorch 值包括 "statistics" 和 "tensor"。统计任务收集张量统计信息(均值、方差、最大值、最小值等),而张量任务捕获任意张量。 是 ✅ ✅
dump_path 存储转储结果的目录。省略时,msprobe 使用其默认路径。 否 ✅ ✅
rank 要采样的 rank。空列表表示收集所有 rank。对于单卡任务,必须将此字段设置为 []。 否 ✅ ✅
step 要采样的 Token 迭代(次数)。空列表表示每次迭代。 否 ✅ ❌
level 转储级别字符串("L0"、"L1" 或 "mix")。L0 针对 nn.Module,L1 针对 torch.api,mix 同时收集两者。 是 ✅ ✅
async_dump 是否启用异步转储(支持 PyTorch statistics/tensor 任务)。默认为 false。 否 ✅ ❌
scope 要采样的模块范围。空列表表示收集每个模块。 否 ✅ ❌
dump_enable 在一个正在运行的训练/推理任务期间,PrecisionDebugger 中用于启用/禁用转储的动态开关。这允许在同一任务中按需开启或关闭转储。 否 ✅ ❌
list 要采样的算子范围。空列表表示收集每个算子。 否 ✅ ✅

要限制捕获的算子,请配置 list 块:

  • scope(list[str]):在 PyTorch PyNative 场景下,此字段限制转储范围。提供两个符合工具命名约定的模块或 API 名称以锁定一个范围;只会转储这两个名称之间的数据。示例:
"scope": ["Module.conv1.Conv2d.forward.0", "Module.fc2.Linear.forward.0"]
"scope": ["Cell.conv1.Conv2d.forward.0", "Cell.fc2.Dense.forward.0"]
"scope": ["Tensor.add.0.forward", "Functional.square.2.forward"]

level 设置决定了可以提供什么——当 level=L0 时提供模块,当 level=L1 时提供 API,当 level=mix 时既可以提供模块也可以提供 API。

  • list(list[str]):自定义算子列表。选项包括:
    • 在 PyTorch pynative 场景中,提供特定 API 的全名以仅转储这些 API。示例:"list": ["Tensor.permute.1.forward", "Tensor.transpose.2.forward", "Torch.relu.3.forward"]。
    • 当 level=mix 时,可以提供模块名称,使转储范围扩展到该模块运行期间产生的所有内容。示例:"list": ["Module.module.language_model.encoder.layers.0.mlp.ParallelMlp.forward.0"]。
    • 提供子字符串(例如 "list": ["relu"])以转储名称中包含该子字符串的所有 API。当 level=mix 时,名称中包含该子字符串的模块也会被展开。

配置示例: eager 模式:

{
  "task": "statistics",
  "dump_path": "/home/data_dump",
  "rank": [],
  "step": [],
  "level": "L1",
  "async_dump": false,

  "statistics": {
    "scope": [],
    "list": [],
    "tensor_list": [],
    "data_mode": ["all"],
    "summary_mode": "statistics"
  }
}

graph 模式:

{
  "task": "statistics",
  "level": "L1",
  "dump_path": "/home/data_dump",
  "statistics": {
    "list": []
  }
}

3. 在 vllm-ascend 中启用 msprobe

  1. 启动 vLLM,并通过 --additional-config 传入转储配置内容:
vllm serve Qwen/Qwen2.5-0.5B-Instruct \
  --dtype bfloat16 \
  --host 0.0.0.0 \
  --port 8000 \
  --additional-config '{
    "dump_config": {
      "task": "statistics",
      "level": "L1",
      "dump_path": "/data/msprobe_dump",
      "statistics": {
        "list": []
      }
    }
  }' &

仍支持兼容模式(旧版):

vllm serve Qwen/Qwen2.5-0.5B-Instruct \
  --dtype bfloat16 \
  --host 0.0.0.0 \
  --port 8000 \
  --additional-config '{"dump_config_path": "/data/msprobe_config.json"}' &

4. 发送请求并收集转储数据

  1. 照常发送推理请求,例如:
curl http://localhost:8000/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
        "model": "Qwen/Qwen2.5-0.5B-Instruct",
        "prompt": "Explain gravity in one sentence.",
        "max_completion_tokens": 32,
        "temperature": 0
      }' | python -m json.tool
  1. 每个请求都会驱动 msprobe: start -> forward -> stop -> step 序列。runner 会在每条代码路径上调用 step(),因此即使推理提前返回,你也总能获得完整的数据集。

  2. 转储文件被写入 dump_path,它们通常包含:

  3. 按算子/模块分组的张量文件。
  4. dump.json:记录 dtype、shape、min/max、requires_grad 等元数据。
  5. construct.json:当 level 为 L0 或 mix 时生成(可视化必需)。

示例目录结构: eager 模式:

├── dump_path
│   ├── step0
│   │   ├── rank0
│   │   │   ├── dump_tensor_data
│   │   │   │    ├── Tensor.permute.1.forward.pt                       # Format: {api_type}.{api_name}.{call_count}.forward.{input/output}.{arg_index}.
│   │   │   │    │                                              # arg_index is the nth input or output of the API. If an input is a list, keep numbering with decimals (e.g., 1.1 is the first element of the first argument).
│   │   │   │    ├── Module.conv1.Conv2d.forward.0.input.0.pt          # Format: {Module}.{module_name}.{class_name}.forward.{call_count}.{input/output}.{arg_index}.
│   │   │   │    └── Module.conv1.Conv2d.forward.0.parameters.bias.pt  # Module parameter data: {Module}.{module_name}.{class_name}.forward.{call_count}.parameters.{parameter_name}.
│   │   │   │                                                          # When the `model` argument passed to dump is a List[torch.nn.Module] or Tuple[torch.nn.Module], module-level data names also include the index inside the list ({Module}.{index}.*), e.g., Module.0.conv1.Conv2d.forward.0.input.0.pt.
│   │   │   ├── dump.json
│   │   │   ├── stack.json
│   │   │   ├── dump_error_info.log
│   │   │   └── construct.json
│   │   ├── rank1
│   │   │   ├── dump_tensor_data
│   │   │   │   └── ...
│   │   │   ├── dump.json
│   │   │   ├── stack.json
│   │   │   ├── dump_error_info.log
│   │   │   └── construct.json
│   │   ├── ...
│   │   │
│   │   └── rank7
│   ├── step1
│   │   ├── ...
│   ├── step2
  • rank:设备 ID。每张卡将其数据写入对应的 rank{ID} 目录。在非分布式场景下,目录直接命名为 rank。
  • dump_tensor_data:已收集的张量数据。
  • dump.json:每个 API 或模块前向数据的统计信息,包括名称、dtype、shape、max、min、mean、L2 范数(L2 方差的平方根),以及 summary_mode="md5" 时的 CRC-32。详见 dump.json 文件描述。
  • dump_error_info.log:仅当转储工具遇到错误时存在,记录失败日志。
  • stack.json:API/模块的调用栈。
  • construct.json:层次结构描述。当 level=L1 时为空。

graph 模式:

L0_dump
├── step0
│   └── rank0
│       └── dump.json
├── step1
│   └── rank0
│       └── dump.json
├── step2
│   └── rank0
│       └── dump.json
├── step3
│   └── rank0
│       └── dump.json
├── step4
│   └── rank0
│       └── dump.json
└── step5
    └── rank0
        └── dump.json

5. 分析结果

5.1 前提条件

通常需要两份转储数据集:一份来自“问题侧”(暴露精度或数值误差的运行),另一份来自“基准侧”(良好的基线)。这两份数据集不必完全相同——它们可以来自不同的分支、框架版本,甚至是替代实现(算子替换、不同的图优化开关等)。只要它们使用相同或相似的输入、硬件拓扑和采样点(step/token),msprobe 就能对它们进行比较并定位发散节点。如果找不到完全干净的基准,可以先采集问题侧的数据,手工构造最小可复现用例,然后进行自身对比。下面我们假设问题转储为 problem_dump,基准转储为 bench_dump。

5.2 可视化

使用 msprobe graph_visualize 构建或比较计算图,然后使用 TensorBoard(tb_graph_ascend 插件)打开生成的 *.vis.db 文件。

  1. 确保 dump 数据可用于可视化:
  2. dump 级别必须为 L0 或 mix,这样 construct.json 非空。
  3. 每个 rank 目录都应包含 dump.json、stack.json 和 construct.json。

  4. 选择命令模式:

  5. 单图构建:

    msprobe graph_visualize -tp <target_path> -o <output_path>
    
  6. 图比较:

    msprobe graph_visualize -tp <target_path> -gp <golden_path> -o <output_path>
    
  7. 常用可选参数:

    • -oc / --overflow_check:启用溢出标记
    • -fm / --fuzzy_match:启用节点映射的模糊匹配
    • -lm / --layer_mapping [mapping.yaml]:跨框架/层映射比较
    • -tensor_log:打印逐节点比较日志(tensor dump 场景)
    • -progress_log:打印详细进度日志
  8. 路径粒度由 graph_visualize 自动检测:

  9. 单 rank:.../step0/rank0
  10. 多 rank(批量):.../step0
  11. 多 step(批量):包含 step* 的 dump 根路径

  12. 输出文件:

  13. 单图构建:build_{timestamp}.vis.db
  14. 图比较:compare_{timestamp}.vis.db

  15. 使用输出目录启动 TensorBoard:

tensorboard --logdir <output_path> --bind_all --port <optional_port>
  1. 在可视化界面中检查结构和数值差异:
  2. 切换 rank/step 以快速定位不稳定节点。
  3. 使用搜索/过滤聚焦目标算子/模块。
  4. 在比较模式下,优先关注高亮显示的高差异节点,并追踪其周围的输入/输出/参数。

6. 故障排除

  • RuntimeError: Please enforce eager mode:重启 vLLM 并添加 --enforce-eager 参数。
  • 没有 dump 文件:确认 JSON 路径正确且每个节点都有写权限。在分布式场景下设置 keep_all_ranks,以便每个 rank 都写入自己的 dump。
  • dump 过大:先从 statistics 任务入手定位异常张量,然后使用 scope/list/tensor_list、filters、token_range 等缩小范围。
  • TypeError: PrecisionDebugger.start() got an unexpected keyword argument 'scheduled_tokens':请将 msprobe 升级到 v26.2.0 或更高版本。

附录

dump.json 文件说明

L0 级别

L0 级别的 dump.json 包含模块的前向输入/输出及参数。以 PyTorch 的 Conv2d 为例,网络代码如下:

output = self.conv2(input) # self.conv2 = torch.nn.Conv2d(64, 128, 5, padding=2, bias=True)

dump.json 包含以下条目:

  • Module.conv2.Conv2d.forward.0:模块的前向数据。input_args 表示位置参数输入,input_kwargs 表示关键字参数输入,output 存储前向输出,parameters 存储权重/偏置。

注意:当传给 dump API 的 model 参数是 List[torch.nn.Module] 或 Tuple[torch.nn.Module] 时,模块级名称会包含列表内的索引({Module}.{index}.*)。示例:Module.0.conv1.Conv2d.forward.0。

{
 "task": "tensor",
 "level": "L0",
 "framework": "pytorch",
 "dump_data_dir": "/dump/path",
 "data": {
  "Module.conv2.Conv2d.forward.0": {
   "input_args": [
    {
     "type": "torch.Tensor",
     "dtype": "torch.float32",
     "shape": [
      8,
      16,
      14,
      14
     ],
     "Max": 1.638758659362793,
     "Min": 0.0,
     "Mean": 0.2544615864753723,
     "Norm": 70.50277709960938,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.input.0.pt"
    }
   ],
   "input_kwargs": {},
   "output": [
    {
     "type": "torch.Tensor",
     "dtype": "torch.float32",
     "shape": [
      8,
      32,
      10,
      10
     ],
     "Max": 1.6815717220306396,
     "Min": -1.5120246410369873,
     "Mean": -0.025344856083393097,
     "Norm": 149.65576171875,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.output.0.pt"
    }
   ],
   "parameters": {
    "weight": {
     "type": "torch.Tensor",
     "dtype": "torch.float32",
     "shape": [
      32,
      16,
      5,
      5
     ],
     "Max": 0.05992485210299492,
     "Min": -0.05999220535159111,
     "Mean": -0.0006165213999338448,
     "Norm": 3.421217441558838,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.parameters.weight.pt"
    },
    "bias": {
     "type": "torch.Tensor",
     "dtype": "torch.float32",
     "shape": [
      32
     ],
     "Max": 0.05744686722755432,
     "Min": -0.04894155263900757,
     "Mean": 0.006410328671336174,
     "Norm": 0.17263513803482056,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.parameters.bias.pt"
    }
   }
  }
 }
}

L1 级别

L1 级别的 dump.json 记录 API 的前向输入/输出。以 PyTorch 的 relu 函数为例(output = torch.nn.functional.relu(input)),文件包含:

  • Functional.relu.0.forward:API 的前向数据。input_args 是位置参数输入,input_kwargs 是关键字参数输入,output 存储前向输出。
{
 "task": "tensor",
 "level": "L1",
 "framework": "pytorch",
 "dump_data_dir":"/dump/path",
 "data": {
  "Functional.relu.0.forward": {
   "input_args": [
    {
     "type": "torch.Tensor",
     "dtype": "torch.float32",
     "shape": [
      32,
      16,
      28,
      28
     ],
     "Max": 1.3864083290100098,
     "Min": -1.3364859819412231,
     "Mean": 0.03711778670549393,
     "Norm": 236.20692443847656,
     "requires_grad": true,
     "data_name": "Functional.relu.0.forward.input.0.pt"
    }
   ],
   "input_kwargs": {},
   "output": [
    {
     "type": "torch.Tensor",
     "dtype": "torch.float32",
     "shape": [
      32,
      16,
      28,
      28
     ],
     "Max": 1.3864083290100098,
     "Min": 0.0,
     "Mean": 0.16849493980407715,
     "Norm": 175.23345947265625,
     "requires_grad": true,
     "data_name": "Functional.relu.0.forward.output.0.pt"
    }
   ]
  }
 }
}  

mix 层级

一个 mix dump.json 文件包含 L0 和 L1 层级数据;其文件格式与上述示例相同。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend