精度验收标准(等价改动 / 一致性判定)
本标准回答一个问题:这次改动到底有没有改变结果,凭什么这么判。
"等价替换"是性价比最高的一类优化:不改模型、不改数值、只换实现,往往能拿到数倍收益;它同时也是
最容易自欺的一类优化——"看起来一样"和"逐位一样"差着好几个数量级,而重写的错误几乎总是出在
索引 / 相位 / 顺序上,不体现在平均误差里。所以本标准的写法是反过来的:先把"等价"分层定义
清楚,再让实现与验收各自证明它。
谁在什么时候调用本标准
| 场景 | 调用方式 |
|---|
| 未使用有损特性(量化/稀疏/缓存/步数裁剪/换权重)的任何优化 | 强制按本标准验收一致性(用户裁决:未用有损特性 ⇒ 强制一致性验收) |
| 等价替换 / 融合 / 拷贝消减 / 编译与图下发 | 按本标准判"改动算不算无损" |
| 并行切分(多卡 all_reduce 顺序、tile 边界) | 按本标准判一致性;切分自身的方法论归 vae-opt / dit-parallel-opt |
| 有损档(量化、稀疏、缓存、少步蒸馏、VAE 解码替换) | 本标准只提供**第三级(质量门)**判据;档位选择与实施归 dit-perf-opt,流程收口归 model-auto-optimization |
不属本标准的:选定"用哪种优化/开哪一档"(→ dit-perf-opt)、并行形态选型
(→ dit-parallel-opt)、收益数字的测量与上报(→ perf-gate,
性能验收标准)。本标准只出"一致性合格与否"的结论。
第一步:把"等价"分层定义清楚
不要笼统说"无损"。先选一层,并写进交付说明:
| 层级 | 定义 | 判据 | 适用 |
|---|
| L1 逐位等价 | 与参考实现 bitwise 相同(torch.equal) | 固定输入逐元素对拍,逐形状做 | 交付物要求"字节不变"时(等价替换的默认目标) |
| L2 数值门 | 差异在明确阈值内(SNR / 相对 L2 / max|Δ|) | 给出阈值与理由,并说明下游容差 | 无法逐位(换 dtype、换累加/归约顺序、多卡归约序)但可接受 |
| L3 有损门 | 允许改变产物,但需质量指标 + 视觉门 | PSNR/SSIM + 视觉判卷记录(第三级质量门) | 精度、稀疏、量化、换权重类改动 |
铁律:L1 与 L2 不能混用。"平均误差很小"不是逐位等价;一旦层级变了(例如为了性能把
dtype 下转),必须显式改口径并说明,不允许沿用"无损"的名义交付。
第二步:三级验收序(按①②③顺序做,缺一不可跳级)
| 级 | 做什么 | 判据 | 通不过怎么办 |
|---|
| ① 同配置重跑逐位 | 同一配置(同一拓扑 / 同卡数 / 同 seed / 同形状)重跑两次,逐位对拍 | torch.equal 成立、逐形状都成立 | 先怀疑确定性来源:归约顺序、原子加、tile 边界、kernel 选择——这是环境问题,不是"允许的差异",必须先解释清楚再谈级别下移 |
| ② 跨配置数值门 + 产物 md5 不变 | 基线配置 vs 改动配置(含多卡/不同 tile/不同并行形态)同窗对照 | 产物 md5(与字节数)与基线一致;若无法逐位,则给出明确数值门与理由(L2) | md5 变了就是改变了结果:按本文"门控、回退与否决记录"走回退或降级声明,不许含糊 |
| ③ 质量门(有损档才启用) | 冻结 baseline 产物 → 定量门 → 视觉门 → off-identity 归因 | 定量 + 视觉 + off-identity 三门同过才可宣称"该有损档可用" | 回退或调档,带证据记录;细则与方法全文见 references/quality-gate.md |
- ①② 是一致性判据,③ 是可接受性判据:前者问"结果变了吗",后者问"变了以后还能用吗"。
- 有损档里 ①② 也不能跳:换权重类改动同样要证明"接口接对了"(同 latent 对拍),
再谈画质档位(见
references/quality-gate.md「判定要点」的训练感知档两层口径)。
- ③ 里下列情形必须先读
references/content-health-gate.md:① 执行侧无图像输入、
视觉门只能记 inconclusive 而仍要判"能不能用";② 有损档 / 换入外部训练权重档
(少步蒸馏适配器、VAE 解码替换)的产物级判定;③ 某特性是载重件(关掉它就不出图/出坏图)
而要做 off-identity;④ 已有纯数值研究给出"更差/不精确"的结论、要用服务端产物级 A/B 收口。
- 三级验收的证据都要留:判据 + 实测数字 + 结论。跨配置只能同窗对照;跨窗口的绝对值不可比,
收益口径一律引用性能验收标准
perf-gate。
- 复核独立性(多 agent 场景):三级验收的结论记录须由未参与该实施的实例复核后才进报表或
对外宣称;同一实例自证只算自验证(证据阶梯第 6 级见
../perf-gate/SKILL.md;角色与交付件见
../model-auto-optimization/references/agent-roles-and-handoff.md §2/§6)。
满足本标准的实现约定(实现期约定,不是验收期才做)
定位说明:本节约束的是怎么写实现。它的产物是"实现自带的对拍开关/日志",供验收期
三级验收直接消费。验收期不补写这段代码就等于第一级没做——因为 bug 会在第一次运行时
暴露,而不是等到端到端产物对比。
把等价性写进代码,让它在每次调用时自己对拍(脚本骨架 scripts/bit_exact_harness.py):
def replace(module, reference):
def forward(*args, **kwargs):
y_ref = reference(*args, **kwargs) # 原始实现(可只在调试/首调用时跑)
y_new = new_impl(*args, **kwargs)
if y_new.shape != y_ref.shape or not torch.equal(y_new, y_ref):
raise AssertionError(
"not bit-exact: shape %s vs %s, max|d|=%s" %
(tuple(y_new.shape), tuple(y_ref.shape),
float((y_new.float() - y_ref.float()).abs().max())))
return y_new
module.forward = forward
要点与原因:
- 逐形状断言:等价性常常依赖形状(同一替换可能只在某个片长/tile/分辨率下逐位精确)。
判据必须是"每个形状都断言过",而不是"试过一个形状"。
- 不满足就回退:把前置条件实现成显式判断,条件不满足时走原实现并打日志——
而不是默默给出不等价结果。
- 对拍成本可控:参考实现可以限制在开发期/首调用/抽样调用,避免长期拖慢线上;
但验收期必须全量对拍。
- 别只对拍最终输出:中间张量也值得对拍(重写错误常出在中间相位/顺序上)。
- 断言与门控配套:新实现一律 env-gated、默认关(默认路径逐位不变),并留一行回退
(门控与回退纪律见
../perf-gate/SKILL.md「门控纪律」)。
判据不达标 → 排障入口
三级里任何一级不达标(md5 变了、差异远超数值门、结果"看着不对"但无报错、无 NaN、
md5 稳定),不要靠调精度或加断言硬压结论,转排障流程:
- 入口:
references/silent-failure-localization.md —— 真值参照 → 指标量化 → 逐层打点找
首个分叉 → 算子隔离 → 规模阈值扫描 → 陈旧内存 vs 数值漂移判定 → 三种规避与三层回归。
- 重写错误的形态学(先怀疑索引/相位/顺序,再怀疑数值):分组是否按正确维度、步长与空洞是否
对应、padding 落点是否一致、通道顺序是否被重排、累积顺序是否改变、相位接线约定是否统一。
误差很大时不要去调精度。
- 对象特有的判定实验见各技能的
troubleshooting-{对象}.md;本标准只留一处通用流程。
门控、回退与否决记录
- 新实现一律 env-gated:默认关(保证默认路径逐位不变),采纳后默认开,并留一行回退;
改动前备份原文件并记录 md5。
- 记录否决:每条否决写成"判据 + 实测数字 + 结论",而不是"试过不行"。常见形态:
- 换写法 ≠ 换实现:改写后落到同一个 kernel、同速(先确认 kernel 真的变了);
- 产物字节变了:md5 改变且收益落在噪声内 → 判非无损,回退;
- "极小"仍不是逐位:差异 ~1e-6 量级也判非无损;
- 链过长:多段融合的中间态越多越难保逐位,先缩短链;
- 减少调用数 ≠ 减少时间:看设备侧总账(换了小算子可能引入更多大算子);
- 等价 ≠ 可用:把峰值显存/交换量算进来(等价但 OOM 仍是否决)。
反模式
- 用"平均误差很小/看起来一样"当逐位等价(必须
torch.equal 或明确阈值);
- 只在一个形状上验证就宣布无损(等价性常随形状变化);
- 跳过①直接看端到端产物(非确定性来源会被误读成"允许的差异");
- 改完不回做端到端 md5(中间层对了、整链仍可能因别的路径变化而变);
- 断言只在验收期临时补(实现里没有 ⇒ 回归时无人再跑);
- 用跨窗口数字宣称收益,或把有损档的"质量通过"当成"无损"(层级混用)。
参考文件与加载时机
| 何时读 | 读哪个 |
|---|
| 动手写替换/改动实现、要断言模板与控制成本 | references/bit-exact-harness.md + scripts/bit_exact_harness.py |
| 判据不达标、结果"看着不对"、要定位静默错误 | references/silent-failure-localization.md(排障入口;配套 scripts/layer_bisect.py / scripts/op_threshold_sweep.py) |
| 走第三级质量门(有损档验收、视觉门写法、off-identity、阈值校准) | references/quality-gate.md |
| 无视觉输入仍要判"能不能用"、载重件的 off-identity、要用产物级 A/B 收口纯数值研究 | references/content-health-gate.md(机读内容健康度门:四量口径 + 「距参考远≠塌陷」+ 载重件对照取法) |
| 需要"什么算等价、什么算必被否决"的判据摘要 | references/equivalence-criteria.md |
| 要出收益数字、写进总览表 | 不在本技能:../perf-gate/SKILL.md(性能验收标准) |
保真口径必须先定:先建"忠实参考",再比数值与性能
规则:比较"优化档 vs 交付档"之前,先确认两档在"是否忠实应用了模型的功能组件(适配器/LoRA、
插件钩子、可选分支)"上同源。
事故形态:曾有一个性能门控把 MLP 的 fc1()/fc2() 快路径旁路掉了,于是适配器的 delta
有一半被静默丢弃(权重已绑定、不报错)。后果:任何"本档 vs 交付档"的对比都是不同源的对比,且
质量门会把"算法差异"当噪声:
| 比较 | 逐帧 RGB PSNR | 判定 |
|---|
| 优化档 vs 忠实参考(应用全部 delta) | 落在已标定下限之上 | ✅ 过门 |
| 优化档 vs 交付档(丢 delta) | 低于已标定下限 | ❌ 低于种子下限 ⇒ 不同源 |
(逐帧 PSNR 读数与种子下限见会话产物归档 {run_results_dir}/archive/。)
做法:
- 先建立忠实参考(把该应用的都应用上:适配器、钩子、可选分支),把它当作数值与性能的基准;
- 与非忠实配置的对比只能作为"代价/收益"的旁证,并且必须显式标注"非同一计算";
- 门控旁路类改动,必须在组件启用(有适配器、有插件)的条件下复测旧的"逐字节一致"结论。
维护与更新
- 复核方法:每次改动本标准或引用文件后,用
evals/evals.json 的三类探针自检——
①等价分层与实现期断言是否仍被强调;②"差异极小仍非逐位"是否仍判不合格;
③「未使用有损特性 ⇒ 强制一致性验收 + 重写错误先查索引/相位/顺序」是否仍能命中。
- 失效信号(出现任一即需重测/重写结论):硬件 / 驱动 / CANN / 框架版本变化(同一重写可能落到
不同 kernel,收益与精度都变);形状 / 分辨率 / 时长 / 分块参数变化(等价性可能是形状的函数);
dtype 下转(层级从 L1 掉到 L2);权重或预处理约定变化;并行拓扑 / 卡数变化(状态边界与交换量变)。
- 同源联动:第三级质量门的工具与判定标准在仓库
evals/(契约 evals/README.md、
伪影标准 evals/rubrics/visual-artifact-gate.md、定量工具 evals/scripts/quality_compare.py);
evals/ 变更时同步 references/quality-gate.md。排障流程被 vae-opt /
vae-opt 等技能单向引用,改其章节编号时要同步引用方。