Skip to content

feat(agent): precision skill (msprobe dump compare + triage + report) - #1084

Open
weich97 wants to merge 1 commit into
mindspore-ai:masterfrom
weich97:feat/skill-precision
Open

weich97 wants to merge 1 commit into
mindspore-ai:masterfrom
weich97:feat/skill-precision

Conversation

@weich97

@weich97 weich97 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Paired: GitHub #1084 ↔ GitCode !1731

这次改了什么

本 PR 原先提交的是 precision skill 的骨架。在真机上实跑 msprobe 后发现它
假设的接口大部分不存在:采集靠一个 msprobe 根本不读的环境变量、配置 schema
的字段位置错、step 解析会漏步、compare 的命令行旗标全不对、比对表的列名
与实际不符。最严重的一条是静默假通过——在确实发散的真实比对表上,骨架的
判定逻辑逐行跳过、输出「无差异」并返回 0。

骨架未合入,故原地重做并强推覆盖(原骨架提交 87cceec2 备份在本地)。
现在接口、字段、列名、判据全部对着实采数据写,并带自校验。

实采验证

msprobe 随 CANN 9.0.0 / torch_npu 2.9.0.post2 安装,Ascend 910 单卡。
被测场景:3 层 Linear(512,512,bias=False) bf16,固定种子,lr=0(权重不动),
三次独立进程:

跑 内容 loss
A 基线 0.03508098
B 同代码同种子 0.03508098(与 A 相同)
C 把 layer 1 权重乘 1.01 0.03579336

即 A-vs-B 必须判无差异、A-vs-C 必须定位到 layer 1 的权重。

骨架的八条缺陷(逐条实机坐实)

# 骨架的断言 实测结果
1 MSPROBE_CONFIG_PATH 是 msprobe 读取的公共环境钩子 ❌ 包内 grep 0 处;唯一被读的是 MSPROBE_LOG_LEVEL。插桩只在进程内构造 PrecisionDebugger 时发生,导出变量再拉起命令采集不会发生
2 可在一个进程里采多份 dump ❌ PrecisionDebugger 是进程级单例,第二次构造是静默空操作,第二份 dump 目录全空
3 配置顶层有 scope ❌ 真 schema 为顶层 task/dump_path/rank/step/level + 按 task 命名的嵌套块;顶层 scope 被忽略,且骨架漏了 rank(多卡必需)
4 --step 0-2 → 配置写 [0, 2] ❌ 实际落盘 step0/step2,静默跳过 step1
5 msprobe -f pytorch compare -np … -bp … ❌ rc=2 usage error。真实为 msprobe compare -tp … -gp … -o …,无 -f、无 -np/-bp
6 -tp 可指 dump 根目录 ❌ 路径检查直接拒(FileCheckException),必须指到 step 目录
7 比对表有 Cosine 列 ❌ 只有 task: tensor 才有;而骨架的采集默认写 task: statistics,该模式下每行都找不到
8 MaxRelativeErr 可直接 float() ❌ statistics 模式是带百分号的字符串('1.1049723756906076%'),tensor 模式才是裸比值

第 7+8 两条叠加的后果就是静默假通过:statistics 表里 Cosine 不存在、
MaxRelativeErr 解析抛 ValueError 被当成「该行不可比较」,于是每一行都被
跳过
。用骨架代码跑真实表:

真实比对表 期望 骨架判定
statistics A-vs-B 无差异 no divergence, exit 0 ✅
statistics A-vs-C 应报发散 no divergence, exit 0 ❌ 假通过(真有 17/30 行发散)
tensor A-vs-B 无差异 no divergence, exit 0 ✅
tensor A-vs-C 应报首因 #7 linear.1.forward.**output**.0 ⚠️ 报的是下游症状

第九条:band 会盖住首因

tensor A-vs-C 里,真正被扰动的那一行与它传播出去的那一行落在 band 两侧:

行 身份 MaxRelativeErr Cosine 在 0.99 / 0.02 band 内?
linear.1.forward.input.1 被扰动的权重(首因) 0.013333 0.999998 是 → 被滤掉
linear.1.forward.output.0 它的输出(症状) 8.833333 0.999994 否 → 被报出
flowchart LR
    L0["linear.0<br/>Max diff = 0"] --> W1
    W1["linear.1.input.1<br/>权重 ×1.01<br/>rel 0.0133 / cos 0.999998"]:::cause --> O1
    O1["linear.1.output.0<br/>rel 8.83 / cos 0.999994"]:::sym --> I2
    I2["linear.2.input.0<br/>rel 8.83"]:::sym
    classDef cause fill:#fde2e1,stroke:#a5322e,stroke-width:2px
    classDef sym fill:#fff6e0,stroke:#b07d2b
Loading

即「噪声带大到能忽略噪声,就大到能盖住首因」。这与本 skill 自己的硬规则
「报首因不报末端症状」直接冲突,因此判据改为默认逐位(任何非零差异
即发散),band 退为 --max-rel / --cosine-min 显式选项。

第十条:阈值出处是编的

骨架注释称阈值「与 rules/precision-acceptance.md 对齐」。核对该规则:它写的是
1000 步 loss 轨迹的平均绝对误差 ≤0.02 或平均相对误差 ≤2%,是端到端
验收口径;规则通篇不提余弦。所以

  • _DEFAULT_MAX_REL = 0.02 把 loss 轨迹的平均绝对误差挪用成单算子张量的最大
    相对误差,两者不是一回事;
  • _DEFAULT_COSINE = 0.99 在规则里没有任何出处。

已据实改写:规则是端到端验收口径,不作逐张量容差;bf16 并列噪声按该规则的
ulp 口径判。

另两条附带发现

  • msprobe 自己的 Result 列不是判据:实测在 MaxRelativeErr=9368.47 的行上
    仍写 'pass'。现在报告里带出来供参考,绝不拿它当门槛。
  • dump 路径有字符校验:目录名含方括号/逗号会被判非法路径而直接抛异常
    (按配置值命名目录很容易踩到)。collect_dump.py 提前拦并给出可操作提示。

修后验证(同一批真实比对表)

真实比对表 期望首因 新实现判定
statistics A-vs-B 无 no divergence ✅
statistics A-vs-C linear.1.forward.input.1 #5 linear.1.forward.input.1(Max diff=4.88e-4) ✅
tensor A-vs-B 无 no divergence ✅
tensor A-vs-C linear.1.forward.input.1 #5 linear.1.forward.input.1(MaxAbsErr=4.88e-4) ✅

两种 dump task 指向同一行首因,且与注入扰动的那一层逐位吻合
(权重 l2norm 13.06 × 1% ≈ bf16 粒度下 4.88e-4)。

step 语义也在各自独立进程里逐形式验过(单例会混淆同进程内的第二次采集,
这正是上一轮我自己测错的原因):

配置写法 实际落盘
[0, 1, 2] step0, step1, step2 ✅
[0, 2] ← 骨架对 --step 0-2 的产物 step0, step2 ❌ 漏 step1
["0-2"] step0, step1, step2 ✅ msprobe 自行展开
[] 全部步 ✅ 空=全采

脚本把区间展开成显式整数列表,这样写出的配置可被逐条校验;
collect_dump.py 校验每个配置步是否产出了含 dump.json 的 step 目录,
run 退出码 0 但没采到东西一律判失败——空 dump 在比对侧正是被读成「无差异」。

代码结构

采集拆成两端,与已过门禁的 profiling(#1085)/ memory-analysis(#1103)同形:

端 文件 职责
启动侧 scripts/collect_dump.py 写真 schema 配置、导出 PRECISION_DUMP_*、拉起 run、逐 step 校验
运行侧 scripts/precision_hook.py(新增) 进程内 start() / capture() / step();对重复 start() 直接抛错
比对侧 scripts/diff_dump.py 真 CLI + 按 task 解析列 + 逐位判据

骨架导出的 MEM/PROF 式死接口问题在此一并消除:PRECISION_DUMP_* 现在
有消费者,采集侧与运行侧共用一份窗口。

SKILL.md 据实收敛

references/align-flow.md 按实机重写(真 schema、真 CLI、两种列集、两种
相对误差格式、单例约束、路径字符校验)。硬规则新增三条:默认逐位判据、
「没采到不等于通过」、「不拿 msprobe 的 Result 列当判据」;并写明验收规则的
0.02 是 1000 步 loss 口径、不得当逐张量容差。

测试

tests/ut/tools/test_precision_dump.py,42 条,CPU only。fixture 复刻真实
列名与两种相对误差格式。覆盖:区间展开与枚举的区别、启动侧与运行侧对同一
spec 展开一致、真 schema 的嵌套/rank/无顶层 scope、路径字符校验、百分号
解析、按 Cosine 判表模式、statistics 表能查出发散(即假通过的回归)、两种
task 指向同一首因、unsupported 行不算发散、nan 在任何 band 下都报、
band 下报出的是下游症状(首因 order 更小)、Result 列只带出不判据、step
目录解析的五种情形、逐步校验的缺步/未终结/全齐三种状态。

pytest tests/ut/tools 42 条全过;pylint 干净(仅剩 C9004 sys-path-mutation,
与已过门禁的 #1072 / #1085 / #1103 同形),lizard 零超限(最大 CCN 10),
check_agents_catalog 通过。

@weich97

weich97 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

/check-pr

@weich97

weich97 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@mindspore-ai-bot

Copy link
Copy Markdown

🔵 The pipeline #554 is running. Please wait a moment... (Link)

@mindspore-ai-bot

Copy link
Copy Markdown
Project Name Build_Stage Build Result Details(Full/Incremental) Detail
Coverage Line Coverage ✅ SUCCESS 65%/0% Link

@mindspore-ai-bot

Copy link
Copy Markdown
Platform Type Module Excution Time Time Limit
_torch - - -
- activation_memory 111.96325s 120.0s
- multicore 75.9395s 120.0s
- shard 147.823s 300.0s
- pipeline_parallel 484.084s 600.0s
- swap_optimizer 68.122s 120.0s
- optimizer 210.174s 300.0s
- distributed_checkpoint 64.123s 120.0s
- fully_shard 261.823s 600.0s
- tensor_parallel 193.292s 300.0s
- init_weights 61.097s 118.75s
- dtensor 61.299s 120.0s
- accuracy 72.983s 120.0s
- clip_grad 65.257s 120.0s
- context_parallel 63.284s 120.0s
- expert_parallel 66.905s 120.0s
- process_group 60.314s 90.0s
- llama3_examples 56.088s 120.0s

@mindspore-ai-bot

Copy link
Copy Markdown
Project Name Build_Stage Build Result Details
Hyper-parallel_Github_Gate - ✅ SUCCESS 554

Add .agent/skills/precision: collect a precision dump (MindStudio msprobe),
compare a candidate dump against a baseline to find the first module /
operator where precision diverges, and localize nan / loss-spike /
non-convergence problems. The acceptance bar is rules/precision-acceptance.md;
this skill produces the evidence that rule judges.

The scripts are written against a real msprobe run (CANN 9.0.0 /
torch_npu 2.9.0.post2 / Ascend 910) rather than an assumed interface:

- Dump collection must instrument in process. msprobe reads exactly one
  MSPROBE_* variable (MSPROBE_LOG_LEVEL) and has no config-path variable, so
  exporting one and launching a command instruments nothing and leaves an
  empty dump - which then compares as "no divergence". precision_hook.py is
  the in-process end that reads the PRECISION_DUMP_* window collect_dump.py
  exports, mirroring prof_hook / mem_hook.
- PrecisionDebugger is a process-wide singleton, so baseline and candidate
  are separate launches; start() raises on a second call instead of silently
  producing one dump and one empty directory.
- The config schema nests per-task options under a block named after the
  task, and carries rank; a top-level scope is not part of the schema.
- step is a list of steps to dump, not a range: [0, 2] dumps steps 0 and 2
  and skips step 1, so a range is expanded to explicit integers.
- compare takes -tp / -gp / -o on a step directory; there is no -f framework
  flag and no -np / -bp, and a dump root is rejected by its path check.
- The metric columns differ by dump task, and MaxRelativeErr is a percent
  string in statistics mode against a bare ratio in tensor mode. Parsing the
  percent form with a plain float() raises, and treating that as "not
  comparable" skips every diverging row - reading out as no divergence on a
  run that diverged.
- Judgement is bitwise by default so the row reported is the first cause. A
  band is opt-in: on a measured single-layer perturbation the perturbed
  weight sits inside a 0.99 / 0.02 band while its propagated output sits
  outside, so a banded scan reports the symptom and drops the cause. The
  acceptance rule's 0.02 is a 1000-step loss-trajectory bound and is not
  reused as a per-tensor tolerance.
- A non-finite metric is reported under any band; msprobe's own Result
  column is carried for context and never gated on, having been observed as
  pass on a row whose relative error is four digits.

collect_dump.py verifies each configured step produced a step directory with
a dump.json, so a run that exits 0 having dumped nothing fails rather than
feeding an empty comparison.

tests/ut/tools/test_precision_dump.py: 42 cases, CPU only, fixtures carrying
the real column names and both relative-error formats.
@weich97
weich97 force-pushed the feat/skill-precision branch from 87cceec to 325877e Compare October 12, 2026 08:22
@weich97

weich97 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

/check-pr

@weich97

weich97 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@mindspore-ai-bot

Copy link
Copy Markdown

🔵 The pipeline #608 is running. Please wait a moment... (Link)

@mindspore-ai-bot

Copy link
Copy Markdown
Project Name Build_Stage Build Result Details(Full/Incremental) Detail
Coverage Line Coverage ✅ SUCCESS 65%/0% Link

@mindspore-ai-bot

Copy link
Copy Markdown
Platform Type Module Excution Time Time Limit
_torch - - -
- activation_memory 111.275s 120.0s
- multicore 71.31737s 120.0s
- shard 151.912s 300.0s
- fully_shard 264.705s 600.0s
- pipeline_parallel 477.847s 600.0s
- swap_optimizer 70.617s 120.0s
- accuracy 71.277s 120.0s
- optimizer 218.528s 300.0s
- tensor_parallel 196.55s 300.0s
- dtensor 66.602s 120.0s
- init_weights 66.193s 118.75s
- clip_grad 64.455s 120.0s
- distributed_checkpoint 67.363s 120.0s
- expert_parallel 68.535s 120.0s
- context_parallel 67.891s 120.0s
- process_group 56.342s 90.0s
- llama3_examples 60.299s 120.0s

@mindspore-ai-bot

Copy link
Copy Markdown
Project Name Build_Stage Build Result Details
Hyper-parallel_Github_Gate - ✅ SUCCESS 608

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants