训练营简介
报名链接
https://www.hiascend.com/developer/activities/cann20252#cann-camp-2502-intro
目录
昇腾AI算子ST测试深度实战:打造"零缺陷"算子的质检与验收指南
第一章:设计"测试蓝图"------精通ST测试用例定义文件
[1.1 从模板到杰作:深度解析JSON字段](#1.1 从模板到杰作:深度解析JSON字段)
[1.2 高级蓝图:应对复杂场景](#1.2 高级蓝图:应对复杂场景)
[第二章:自动化测试流水线------msopst run的深度掌控](#第二章:自动化测试流水线——msopst run的深度掌控)
[2.1 核心执行命令与参数精解](#2.1 核心执行命令与参数精解)
[2.2 进阶操作:主控面板msopst.ini](#2.2 进阶操作:主控面板msopst.ini)
[3.1 Fuzz测试:用自动化风暴寻找薄弱环节](#3.1 Fuzz测试:用自动化风暴寻找薄弱环节)
[3.2 自定义验证:当标准答案不标准时](#3.2 自定义验证:当标准答案不标准时)
第四章:解读"质检报告"------从st_report.json中洞察真相
昇腾AI算子ST测试深度实战:打造"零缺陷"算子的质检与验收指南
在昇腾AI的宏大生态中,每一个自定义算子都如同一颗颗精密的"引擎零件"。在它被装配到庞大复杂的AI应用"火箭"之前,必须经过最严格、最全面的质检流程。这个流程,就是ST(System Test)测试。它不再是单元测试的"纸上谈兵",而是将算子置于真实的昇腾硬件"试炼场"中,验证其在真实环境下的功能正确性、精度稳定性和性能表现。
msopst工具,正是我们这个"质检与验收实验室"的核心设备。本教程将摒弃传统手册的命令罗列,以一位资深"质检工程师"的身份,带您全面掌握如何使用msopst,为您的算子签发"准飞证"。我们将从搭建实验室开始,到设计复杂的测试蓝图,再到自动化执行高级测试案例,并最终解读权威的质检报告。
序章:搭建"质检实验室"------环境准备与核心认知
在启动任何测试之前,一个稳定、合规的实验室环境是所有结果可靠性的基石。
第一步:校准我们的测试设备
-
硬件确认 :使用
npu-smi info命令,获取您服务器的Chip Name(如Ascend910B3)。这个名称是贯穿所有配置文件的"设备身份标签",错一个字符都将导致测试失败。请郑重地记录下来。 -
软件安装 :确保您已按照《CANN软件安装指南》,以"训练&推理&开发调试 "模式安装了
Ascend-cann-toolkit。这个模式包含了msopst、ATC模型转换工具以及运行时所需的所有组件。 -
工具定位 :
msopst的可执行文件通常位于CANN安装路径下的python/site-packages/bin目录。为了方便调用,建议将此目录添加到系统的PATH环境变量中。
第二步:核心认知------ST测什么,不测什么?
理解测试的边界至关重要,能帮助我们设定合理的期望。
- ST测试会覆盖 :
- 算子实现文件:您的TBE(Python)或AI CPU(C/C++)核心计算逻辑。
- 算子原型定义:定义算子输入、输出、属性的元数据。
- 算子信息库 :描述算子支持的数据类型、格式、形状等信息的
.ini文件。
- ST测试不会覆盖 :
- 算子适配插件:这部分负责将算子接入到TensorFlow、PyTorch等上层框架中,ST测试不涉及框架层面的调用。
前提:确保"待测品"已就位
msopst测试的是一个已经**部署到算子库(OPP)**中的算子。如果您的自定义算子还只是一个源码工程,请先完成编译和部署(通常执行.run安装包),否则msopst将找不到测试对象。
第一章:设计"测试蓝图"------精通ST测试用例定义文件
msopst的核心是驱动一个JSON文件------我们称之为"测试蓝图"。这个蓝图的质量,直接决定了测试的广度和深度。msopst create命令可以生成一个基础模板,但真正的艺术在于如何精心雕琢它。
1.1 从模板到杰作:深度解析JSON字段
让我们从一个Add算子的模板开始,逐层深入,理解每个字段的战略意义。
[
{
"case_name": "Test_Add_001_FP16_SquareMatrix",
"op": "AddCustom",
"error_threshold": [0.001, 0.01],
"input_desc": [
{
"name": "x1",
"format": ["ND"],
"type": ["float16"],
"shape": [128, 128],
"data_distribute": ["uniform"],
"value_range": [[-1.0, 1.0]]
},
{
"name": "x2",
"format": ["ND"],
"type": ["float16"],
"shape": [128, 128],
"data_distribute": ["uniform"],
"value_range": [[-1.0, 1.0]]
}
],
"output_desc": [
{
"name": "y",
"format": ["ND"],
"type": ["float16"],
"shape": [128, 128]
}
]
}
]
"蓝图"核心字段深度解读与实战经验:
-
case_name: 不只是个名字 。给它一个具有描述性的名称,如Test_Add_001_FP16_SquareMatrix,当你有上百个测试用例时,这将是你快速定位问题的生命线。 -
op: 待测品的型号 。必须与你在算子信息库(.ini)中定义的op_type完全一致。 -
error_threshold: 质检的"合格标准" 。这是一个包含两个浮点数的列表:[threshold1, threshold2]。threshold1(点误差阈值) :算子输出与期望结果之间,允许的单个数据点的最大绝对误差。对于FP16,设置0.001是合理的;对于FP32,可以设置得更小,如1e-6。threshold2(误差率阈值) :允许超过threshold1误差的数据点,占总数据点的最大比例。设置为0.01,意味着最多允许1%的点误差超标。- 实战经验:这个双重阈值机制非常科学。它允许偶尔的"毛刺",但能有效防止整体结果的系统性偏移。你可以通过调整这两个值来平衡测试的严格程度。
-
input_desc&output_desc: 测试数据的"基因编码"。format: 数据的物理形态 。"ND"(N-Dimensional):通用格式,适用于不涉及特殊硬件对齐的简单算子。"NC1HWC0":深度学习图像算子的标准形态 。C0是一个与硬件强相关的固定值(通常是16),代表一个计算单元能处理的通道数。C1 = C / C0。使用这种格式能让算子在AI Core上达到最佳性能。如果你的算子是卷积、池化等,强烈建议测试此格式。"FRACTAL_NZ":矩阵计算的高效形态,尤其适用于GEMM(通用矩阵乘法)类算子。
type: 数据的精度类型 。"float16","float32":测试浮点算子的基本盘。"int32","int8":测试整数算子。"bfloat16": Atlas A2系列的优势类型,如果你的算子支持,务必测试。- 高级技巧 :
format和type字段的值是一个数组 (如["ND", "NC1HWC0"])。这意味着msopst会自动组合 这些格式和类型,生成多个测试子用例。例如,一个input_desc中指定了2种format和2种type,msopst会为你生成2x2=4个测试组合。这是实现测试覆盖最大化的利器。
shape: 数据的尺寸 。- 可以是静态的,如
[128, 128]。 - 也可以包含
-1来表示动态维度,如[32, -1]。此时,你需要配合shape_range字段来指定动态维度的范围,如"shape_range": [[1, 1024]]。
- 可以是静态的,如
data_distribute&value_range: 生成有意义的测试数据 。"uniform":均匀分布,最通用的选择。"normal":正态分布,适合模拟真实世界的数据特征。"relu":先均匀分布,再经过ReLU激活。如果你的算子位于ReLU层之后,使用这种分布能更好地模拟其输入特征。value_range决定了数据的范围,这对于测试边界条件(如溢出)至关重要。
1.2 高级蓝图:应对复杂场景
-
包含属性的算子(如
Conv2D):"attr": [ { "name": "strides", "type": "list_int", "value": [1, 1, 1, 1] }, { "name": "pads", "type": "list_int", "value": [0, 0, 0, 0] } ] -
动态多输入算子(如
AddN) : 输入的name必须是{attr_name} + index的形式,如x0,x1,x2,且数量必须与算子属性N的值匹配。
第二章:自动化测试流水线------msopst run的深度掌控
"测试蓝图"设计完毕,现在是启动我们自动化测试流水线的时候了。
2.1 核心执行命令与参数精解
./msopst run -i ./test_cases/AddCustom.json -soc Ascend910B3 -out ./test_results -err_thr "[0.001, 0.01]"
-i: 指向你的"测试蓝图"JSON文件。-soc: 硅的身份 。再次强调,必须与npu-smi info的结果匹配。-out: "质检报告"的输出目录。msopst会在这里生成一个带时间戳的子目录,结构清晰。-err_thr: 动态质检标准 。这个命令行参数可以覆盖JSON文件中的error_threshold。这对于快速实验非常方便,例如,你想先用一个宽松的标准跑通,再用严格的标准测试。
2.2 进阶操作:主控面板msopst.ini
对于更复杂的测试控制,我们需要动用实验室的"主控面板"------msopst.ini文件(通常在CANN安装路径下)。
核心功能与实战场景:
-
只生成测试代码,不执行(模式2) : 在
msopst.ini中设置:only_gen_without_run = True only_run_without_gen = False使用场景 :你想手动检查
msopst自动生成的C++测试代码,甚至在其中加入自定义的打印或调试逻辑,然后再编译运行。这给了开发者极大的灵活性。 -
开启性能剖析模式 : 在
msopst.ini中设置:performance_mode = True使用场景 :当你不仅关心功能正确性,还想初步了解算子的性能表现时。开启后,测试结果目录会多出一个
prof/JOBxxx/summary文件夹,里面的op_summary_0_1.csv文件包含了算子的执行耗时等关键性能指标。这相当于在进行ST测试的同时,顺便做了一次轻量级的性能摸底。 -
指定错误报告(
-err_report) : 在执行命令时加上-err_report true。 使用场景 :当测试失败时,仅仅知道"失败"是不够的。这个参数会让msopst将那些超出error_threshold的具体数据点(期望值 vs. 实际值)导出为CSV文件。这是定位精度问题的"物证"。
第三章:高级测试技术------Fuzz测试与自定义验证
标准测试用例覆盖了常规场景,但真正的" bug"往往隐藏在极端、异常的边缘。这时,我们需要更高级的测试技术。
3.1 Fuzz测试:用自动化风暴寻找薄弱环节
Fuzz测试的核心思想是:用自动化脚本生成海量的、随机的、甚至"不合常理"的测试参数,来对算子进行压力测试。
实战流程:
-
编写Fuzz脚本(
my_fuzzer.py):import random import numpy as np def fuzz_branch(): # 随机生成1-4维的shape,每维大小在1-256之间 dims = random.randint(1, 4) shape = [random.randint(1, 256) for _ in range(dims)] # 根据shape生成随机数据 value_x1 = np.random.uniform(-10, 10, size=shape).astype(np.float16) value_x2 = np.random.uniform(-10, 10, size=shape).astype(np.float16) # 返回给msopst的字典结构 return { "input_desc": { "x1": {"shape": shape, "value": value_x1}, "x2": {"shape": shape, "value": value_x2} }, "output_desc": {"y": {"shape": shape}} } -
在JSON蓝图"激活"Fuzz:
[ { "case_name": "Test_Add_Fuzz", "op": "AddCustom", "fuzz_impl": "./my_fuzzer.py:fuzz_branch", // 指向你的脚本和函数 "fuzz_case_num": 500, // 生成500个测试用例 "input_desc": [ { "name": "x1", "shape": "fuzz", // 标记此字段由fuzz脚本生成 "type": ["float16"], "format": ["ND"] }, // ... x2, y 也是类似配置 ] } ]
经验之谈 :Fuzz测试是发现由shape、stride等参数组合导致的隐藏bug的终极武器。它可以测试出那些你认为"理论上没人会这么用"但实际上可能发生的边界情况。
3.2 自定义验证:当标准答案不标准时
有时,算子的期望结果无法用一个简单的数学公式描述,或者你想用一个高精度的NumPy实现作为"黄金标准"。
实战流程:
-
编写期望函数(
my_expect_func.py):import numpy as np # 注意:函数签名必须与JSON中定义的输入输出name对应 # 可选输入需要设置默认值,如 x3=None def calc_expect_func(x1, x2, y=None): # x1, x2, y 都是字典,其"value"键存放实际numpy数组 res = x1["value"] + x2["value"] return [res, ] # 返回必须是一个list -
在JSON蓝图"链接"自定义函数:
{ "case_name": "Test_Add_Custom_Expect", "op": "AddCustom", "calc_expect_func_file": "/path/to/my_expect_func.py:calc_expect_func", // 路径:函数名 // ... input_desc, output_desc ... }经验之谈 :这种方式将
msopst从一个简单的数据比较器,升级为一个灵活的测试框架。你可以用NumPy, SciPy等任何Python库来生成期望结果,极大地增强了测试的灵活性和权威性。
第四章:解读"质检报告"------从st_report.json中洞察真相
测试执行完毕,一份详尽的st_report.json报告会生成在结果目录的根目录。这是我们分析测试结果的最终依据。
报告核心字段解读:
summary: 一目了然的判决书 。test case count: 总共执行了多少个测试子用例。success count: 成功通过的用例数。failed count: 失败的用例数。如果这个数不为0,你的工作才真正开始。
report_list: 每个案例的详细卷宗 。这是一个列表,每个元素对应一个测试用例的详细信息。case_name: 失败的用例名称。status: 用例的执行状态("success"或"failed")。stage_result: 更细粒度的执行阶段信息,告诉你是在"模型转换"阶段失败了,还是在"模型执行"阶段失败了,或是"精度比对"阶段失败了。这是定位问题的关键线索。
trace_detail: 完整的执行日志。如果上述信息还不够,可以在这里找到完整的、按时间顺序排列的执行步骤和对应的输出/错误信息。
故障排除思路:
- 看
summary,确认是否有失败。 - 看
report_list,找到失败的case_name。 - 看该用例的
stage_result,定位失败阶段。 - 如果是精度失败,检查对应的
*_error_report.csv文件,分析误差数据。 - 如果是执行失败,查看
trace_detail,寻找具体的错误信息(如内存不足、算子不存在等)。
第五章:跨平台挑战------开发与运行环境分离
在企业级开发中,你的"实验室"(开发环境,可能是x86服务器)和"发射场"(运行环境,可能是aarch64服务器)往往是分离的。这时,ST测试流程就需要一些额外的步骤。
核心挑战:在x86上编译的测试可执行文件,无法在aarch64上运行。
解决方案与流程:
-
在开发环境(x86)上:
- 修改
msopst.ini,设置only_gen_without_run = True(模式2)。 - 同时,配置好
HOST_ARCH="aarch64"和TOOL_CHAIN,指明目标环境的架构和交叉编译器路径。 - 执行
./msopst run ...。此时,msopst会生成测试代码,并使用指定的交叉编译器生成一个可在aarch64上运行的main可执行文件。
- 修改
-
迁移"测试套件":
- 将
msopst生成的整个{timestamp}/{OpType}目录打包,拷贝到你的运行环境(aarch64服务器)。
- 将
-
在运行环境上:
- 进入拷贝过来的
run/out目录。 - 直接执行
./main。这个可执行文件会加载同目录下的模型文件(.om)和测试数据,完成最终的测试。
- 进入拷贝过来的
这个流程确保了即使在跨平台场景下,我们也能利用强大的msopst工具链,完成对算子的最终验收。
结语:从"测试"到"质量保证"的升华
通过本教程的深度解析,我们看到的不再是一个个孤立的命令,而是一整套完整的、可扩展的、面向质量的算子验证体系。从精心设计测试蓝图,到驾驭自动化流水线,再到运用Fuzz和自定义验证等高级技术,我们已将msopst从一个简单的工具,升华为保障算子质量的"战略武器"。
请记住,每一次msopst的成功运行,都是对算子稳定性的一次强力背书;每一次失败的深度分析,都是对算子健壮性的一次淬炼。掌握这套方法论,你将不仅仅是一个算子的开发者,更是一个能够为算子"质量"负责的守护者。
