【免费下载链接】cannbot-skills CANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。 【免费下载链接】cannbot-skills 项目地址: https://gitcode.com/cann/cannbot-skills

name: model-infer-precision-debug description: 基于 PyTorch 框架的昇腾 NPU 模型推理精度问题诊断技能。当前主要覆盖 KVCache / FlashAttention 相关精度问题,包括 Prefill/Decode 对齐、cache 更新错误、slot/block mapping 错误、attention 路径切换后的精度异常等。触发场景:优化改造后精度验证未通过、模型输出与基线存在显著偏差、Prefill 和 Decode 精度表现不一致、出现 NaN/Inf、量化模式下精度异常放大等。

推理精度问题诊断技能

当前主要覆盖 KVCache / FA 相关精度问题,其他推理精度问题后续补充。 不覆盖运行时错误(crash、hang、OOM、算子约束违反等见 model-infer-runtime-debug)。

按"症状分类 → 快速验证 → 分模块定位 → 逐层对比 → 陷阱修复"的分层策略排查精度问题。完整校验工具见 references/fa_debug_utils.md

重要原则

  • 对比驱动:所有精度判定必须基于与基线(eager 模式 / 优化前)的数值对比,不可凭直觉
  • 逐步缩小范围:先定位阶段(Prefill/Decode)→ 再定位模块(KVCache 写入/FA 计算/后处理)→ 最后定位参数
  • 最小改动验证:每次只修改一个变量,验证后再进入下一个
  • 保留现场:调试过程中的中间 tensor 和日志必须保留,用于后续分析
  • 实际执行测试:所有调试步骤由 Agent 实际执行,不将测试工作委托给用户

排查工作流

接到精度调试任务后,按以下步骤执行:

第一步:收集信息 — 获取精度症状、优化阶段、执行/量化模式、基线输出

第二步:快速诊断 — 根据症状分类表判断方向 → 执行快速验证清单(含 FA 入参检查)→ 发现问题直接修复

第三步:分模块定位 — 二分法确定 Prefill/Decode → 针对性插桩验证 → PA 场景执行专项排查

第四步:精细定位(按需) — 使用逐层对比框架,dump 基线 → compare 优化后 → 定位首次偏差层

第五步:修复与验证 — 参照常见陷阱表修复 → 重新运行完整精度验证

第六步:输出调试报告 — 问题概要、根因分析、修复措施、修复验证、遗留风险


第二步:快速诊断

2.1 症状分类

症状 可能原因 优先排查方向
输出全为重复 token 或乱码 KVCache 写入位置错误 slot_mapping / scatter_update_ 参数
首个 token 正确,后续偏移 Decode 阶段 kv_len 更新错误 kv_len 生命周期
Prefill 正确,Decode 偏差大 Decode FA 入参错误 sparse_mode / actual_seq_lengths
输出含 NaN/Inf 数值溢出 scale 参数、dtype 不匹配
精度逐层累积偏差 FA 算子数值精度 input_layout / NZ 格式 / 量化参数
多 batch 部分样本错误 batch 间缓存交叉污染 block_table 构造 / kv_len_offset
量化模式下偏差显著放大 量化参数不匹配 dequant_scale / antiquant_mode

2.2 快速验证清单

  •  
    1. dtype 一致性:KVCache tensor 与模型计算 dtype 一致(BF16 / FP16)
  •  
    1. device 一致性:所有 tensor 均在 NPU 上,无意外的 CPU tensor
  •  
    1. block_table dtype:必须为 torch.int32
  •  
    1. sparse_mode 正确:Prefill=3(因果),Decode=0(dense);sparse_mode=2 不推荐
  •  
    1. Prefill 和 Decode 的 FA 调用参数需分别配置(sparse_mode、atten_mask、actual_seq_lengths 等)
  •  
    1. scale 参数:1.0 / sqrt(head_dim),确认 head_dim 值正确
  •  
    1. num_heads / num_kv_heads:与模型配置一致,多卡时已按 rank 切分
  •  
    1. kv_len 初始值:Prefill 后 kv_len 等于输入序列长度,非 0
  •  
    1. input_layout 与 tensor shape 匹配
  •  
    1. actual_seq_lengths 构造方式与 layout 匹配(TND 用 cumsum,BSH 用原值)
  •  
    1. atten_mask 与 sparse_mode 匹配(详见附录 F.1);Decode 单 token 查询通常不需要 mask
  •  
    1. FA v1 vs v2 参数名:未混用(详见附录 F.2)
  •  
    1. Decode 阶段未使用 sparse_mode=3
  •  
    1. PA 场景下 mask 最后一维 >= block_table 第二维 × block_size
  •  
    1. MLA rope 参数:query_rope/key_rope 同时传或同时不传;rope D=64,query D 仅支持 512/128
  •  
    1. inner_precise 行无效:Prefill + 自定义 mask 有全遮蔽行时需设为 2/3
  •  
    1. kv_len 层内只读:kv_len 在 attention/cache 内部未被修改(禁止层内递增,否则各层写入位置错位导致精度彻底损坏)

第三步:分模块定位

3.1 排查策略:二分法定位

模型输出与基线不一致
    │
    ├─ 1. 仅运行 Prefill,对比输出
    │   ├─ Prefill 输出一致 → 问题在 Decode 阶段(跳到 3.3)
    │   └─ Prefill 输出不一致 → 问题在 Prefill 阶段(跳到 3.2)
    │
    ├─ 2. Decode 第 1 步 vs 第 N 步
    │   ├─ 第 1 步就不一致 → FA 入参或 KVCache 初始状态问题
    │   └─ 第 N 步开始不一致 → kv_len 累计更新或缓存溢出问题
    │
    └─ 3. 逐层对比(跳到第四步)

3.2 Prefill 阶段精度排查

KVCache 写入验证:在 scatter_update_ 调用前后插桩,检查:

  1. 写入前缓存是否为零(首次 Prefill)
  2. 写入后缓存内容与 key_states 是否一致(阈值 1e-6)
  3. scatter_update_ 的 axis 参数与 layout 是否匹配(BSH → axis=1, TND → axis=0)

FA 入参验证:打印并校验 Q/K/V shape、actual_seq_qlen/kvlen、sparse_mode、是否含 NaN/Inf

3.3 Decode 阶段精度排查

KVCache 写入验证

  1. 验证 slot_mapping 计算(连续缓存:expected_slot = kv_len.view(-1,1) + kv_len_offset;PA:通过 block_table 索引)
  2. 验证写入位置未越界
  3. 验证 kv_len 逐步递增正确

FA 入参验证

  1. sparse_mode 应为 0,actual_seq_qlen 每个值应为 1(MTP 除外)
  2. block_table dtype 必须为 int32
  3. KV cache 已写入区域无 NaN/Inf

3.4 分页注意力(PA)专项排查

  1. block_table 静态校验numel == batch_size × (max_seq_len // block_size)
  2. block_table 值域校验:应为 [0, total_blocks) 的排列,无重复
  3. slot_mapping 与 block_table 交叉验证

第四步:逐层精细对比

4.1 逐层对比框架

在 Transformer Block 的 8 个关键位置插入 checkpoint,与基线对比:

序号 位置 重要程度
1 Block 输入 普通
2 Attention Norm 后 普通
3 QKV 投影后 普通
4 RoPE 后 普通
5 KVCache 写入后 关键
6 FA 输出 关键
7 Attention 残差后 普通
8 Block 输出 普通

对比指标(均转为 float32 计算):max_abs_diff / max_rel_diff / cosine_similarity

判定阈值:BF16 模式 max_rel < 1e-3,量化模式 max_rel < 1e-2

使用流程:dump 基线 → compare 优化后 → 定位首次出现偏差的层和位置


第五步:常见精度陷阱与修复

5.1 KVCache 写入相关

问题 根因 修复方案
缓存写入位置偏移 scatter_update_ 的 axis 与 layout 不匹配 BSH → axis=1, TND → axis=0
多 batch 缓存交叉写入 kv_len_offset 未按 max_seq_len 对齐 kv_len_offset = arange(0, batch*max_seq_len, max_seq_len)
Prefill 后缓存部分为零 kv_len 计算错误 检查 kv_len 是否等于实际有效序列长度(具体值视框架约定)
NZ 格式下缓存异常 cache_mode 设置错误 PA 模式使用 cache_mode="PA_NZ"
融合写入算子结果不一致 npu_kv_rmsnorm_rope_cache 参数不匹配 对齐 epsilon 和 RoPE 频率

5.2 FA 算子相关

问题 根因 修复方案
Prefill 正常,Decode 偏差大 Decode 使用了 sparse_mode=3 Decode 设 sparse_mode=0
actual_seq_lengths 错误 TND 应用 cumsum,BSH 用原始值 参考 model-infer-kvcache skill 的数据流章节
Decode 首步输出为零 actual_seq_qlen 为 0 Decode 时应为 1 或 cumsum([1,...])
MLA absorb 模式 key≠value cache_nope 读写不一致 FA 中 key 和 value 都传 cache_nope
scale 参数错误 FA v1 用 scale,v2 用 softmax_scale 确认参数名与版本匹配

5.3 量化相关

问题 根因 修复方案
W8A8 精度从某层开始放大 dequant_scale 与权重不匹配 检查 per-channel/per-tensor scale
W8A8C8 KVCache 精度差 KV 缓存量化精度损失 检查 KV 量化 scale;考虑 BF16 KV
antiquant_mode 不匹配 FA v1/v2 量化接口不同 v1 用 antiquant_,v2 用 dequant_

5.4 Prefill/Decode 一致性

问题 根因 修复方案
Prefill 不走 KVCache 写入 条件分支逻辑错误 确保 Prefill 也执行 scatter_update_
Decode 使用了 Prefill 的 FA 参数配置 未分别配置 Prefill 和 Decode 分别配置 sparse_mode、atten_mask 等参数
切换阶段时 kv_len 未更新 Prefill 后未执行 kv_len += seq_len 在 Prefill→Decode 切换时确保更新
Decode 传入了 atten_mask sparse_mode=0 不需要 Decode 设 atten_mask=None

附录 F:FA 入参配置参考

表格供快速验证清单引用,完整校验代码见 references/fa_debug_utils.md,可在 FA 调用前插入 debug_fa_all_params() 一键检查。

F.1 atten_mask 与 sparse_mode 联合校验

sparse_mode 含义 atten_mask 要求 适用阶段 常见错误
0 defaultMask 可选,通常 None Decode Decode 误传 mask
1 allMask 必传 (Q_S, KV_S) 特殊 忘记传 mask
2 leftUpCausal 不推荐
3 rightDownCausal 必传 (2048, 2048) 下三角 Prefill mask 不是下三角;Decode 误用
4 band(滑窗) 必传 (2048, 2048) Prefill / Decode pre_tokens/next_tokens 错误

F.2 FA v1 vs v2 关键参数名映射

功能 FA v1 FA v2 易错点
缩放系数 scale softmax_scale 默认 1.0,传错名精度崩溃
Q head 数 num_heads num_query_heads 传错名默认 1
Q 长度 actual_seq_lengths actual_seq_qlen 名称完全不同
KV 长度 actual_seq_lengths_kv actual_seq_kvlen 名称完全不同
KV 量化 antiquant_scale/offset dequant_scale_key/value v2 分离 key/value

F.3 input_layout 与 tensor shape 匹配

layout Q 维度 K/V 维度 N 轴含义
BSH 3D [B, S, H*D] 3D [B, S, H*D] 无独立 N 轴
BNSD 4D [B, N, S, D] 4D [B, N, S, D] N = num_heads
TND 3D [T, N, D] 3D [T, N, D] N = num_heads,T = packed tokens

PA 模式下 KV 按 block_table 索引,shape 与非 PA 不同。

F.4 actual_seq_lengths 构造

layout actual_seq_qlen actual_seq_kvlen
TND 累积和 cumsum([s1, s2, ...]) 累积和
BSH / BNSD 原始值 [s1, s2, ...] 原始值

Decode 时 actual_seq_qlen 每个值通常为 1(MTP 场景除外)。

F.5 inner_precise 行无效

精度模式 行无效修正 适用场景
0 高精度 默认
1 高性能 精度要求不高
2 高精度 Prefill + 自定义 mask 有全遮蔽行
3 高性能 同上但允许精度损失

F.6 MLA rope 约束

  • query_ropekey_rope 必须同时传或同时不传
  • rope D 必须为 64,query D 仅支持 512 或 128
  • D=512 时仅支持 sparse_mode 为 0、3、4

精度调试报告模板

## 精度调试报告

### 问题概要
- 症状:{描述}
- 定位阶段:{Prefill / Decode / 两者}
- 定位模块:{KVCache 写入 / FA 计算 / 量化参数 / ...}

### 根因分析
- 原因:{具体原因}
- 影响范围:{受影响的层/模块}

### 修复措施
| 修改文件 | 修改内容 | 修改原因 |
|---------|---------|---------|
| {file} | {change} | {reason} |

### 修复验证
- 精度对比:{通过/未通过,max_rel_diff, cosine_similarity}
- 输出一致性:{token 序列对比结果}

### 遗留风险
- {如有,列出可能的后续风险}

其他已知精度问题

以下问题来自本仓已适配模型的调试经验,不属于 KVCache/FA 范畴但会导致精度异常。

问题 症状 根因 修复
MoE 专家权重未加载 MoE 输出全零或接近零,cosine~0.4-0.8 Checkpoint 用 packed tensor 但 load_weights 用 per-expert mapping,权重未正确拷贝 添加 packed expert weight loader,按 EP rank 切片后 copy_ 到参数
Zero expert 处理错误 MoE 输出偏差或切换路由算子后精度骤降至 ~40-50% 零专家 identity 贡献公式错(应用 original_input 而非 MoE 输出),或路由算子 v1 无 active_expert_range 导致零专家 ID 污染 token 排列 路由前用 mask 分离零专家,路由后加回 original_input * weight 的 identity 贡献
DP+EP token 不同步 → NaN Decode 若干步后出现 NaN,之后全部 NaN EP all_to_all 有浮点非确定性,DP 各 rank token 选择分叉 → KV cache 累积偏差 每步 Decode 后 dist.broadcast(next_token, src=0) 强制所有 DP rank 使用相同 token
跳过训练组件的 scale 缺失 输出语义正确但严重重复/退化 模型训练时有额外组件,跳过后 embedding 幅度不匹配 保留训练时的 normalization factor

【免费下载链接】cannbot-skills CANNBot 是面向 CANN 开发的用于提升开发效率的系列智能体,本仓库为其提供可复用的 Skills 模块。 【免费下载链接】cannbot-skills 项目地址: https://gitcode.com/cann/cannbot-skills

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐