CANN ops-nn ApplyAdamD 算子 ST 精度测试指南:基于 ATK kernel 后端的单算子真机验证
2026/9/20 10:51:56 网站建设 项目流程
  • 人工智能
  • 算子库
  • 深度学习
  • CANN
  • Ascend

【免费下载链接】ops-nn

本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。

项目地址:https://gitcode.com/cann/ops-nn
点击查看免费下载

本指南围绕experimental/optim/apply_adam_d下 ATK(Ascend Test Kit)用例集tests/st/aclnnApplyAdamD展开,讲解如何对已部署到 OPP 的ApplyAdamD自定义算子(host tiling + AI Core kernel)进行 kernel 后端精度对拍:以手写 FP32 融合 Adam golden 为基准,通过aclopCompileAndExecuteV2单算子动态执行在真机(ascend910b / dav-2201)上 launch 内核,并与cpu后端逐一比对。读完本文,你将掌握该用例集的设计动机、精度判据(MERE/MARE 浮点社区标准)、三个测试文件的分工与实现原理,以及完整运行步骤。

一、测试对象与背景:ApplyAdamD 算子

ApplyAdamD是沿用图模式(REG_OP(ApplyAdamD))原型的 Adam 单步更新算子:根据 Adam 算法对参数张量var做一次更新,并输出更新后的varmv三个张量。其调用形态为10 输入 / 3 输出 / 2 属性,数据格式均为 ND,支持静态/动态 shape 与动态 rank。

计算公式(内部统一升精度到 FP32 计算,两分支分母均使用更新后v):

lr' = learning_rate * sqrt(1 - beta2_power) / (1 - beta1_power) m_new = beta1 * m + (1 - beta1) * grad v_new = beta2 * v + (1 - beta2) * grad * grad
  • useNesterov = falsevar_new = var - lr' * m_new / (epsilon + sqrt(v_new))
  • useNesterov = truevar_new = var - lr' * (m_new * beta1 + (1 - beta1) * grad) / (epsilon + sqrt(v_new))

其中 6 个 Adam 标量参数(beta1Powerbeta2Powerlrbeta1beta2epsilon)在 aclnn 接口中均为shape size 为 1 的aclTensor(而非aclScalar),varOutmOutvOut可与varmv复用 Device 地址实现原地更新。算子原型已标注DEPRECATED(建议新业务使用ApplyAdam),本experimental任务补齐的是ascend910b(Atlas A2 系列,DAV_2201)端侧原生 AscendC kernel + tiling + aclnn 调用入口,详细信息见 算子 README 与 aclnnApplyAdamD 接口文档。

正是因为该算子同时具备「6 个运行期标量张量输入」「三输出原地更新」「多核 tiling + 双缓冲 kernel」等典型特征,才需要一套独立于 aclnn 示例路径的精度验证手段——这正是本 ST 用例集存在的意义。

二、测试架构总览:kernel 后端与 cpu 后端对拍

本目录保留的是 ATKkernel后端精度用例,覆盖已部署ApplyAdamD的 host tiling + AI Core kernel。其核心验证链路为:

aclopCompileAndExecuteV2("ApplyAdamD", 10in/3out/2attr, ACL_ENGINE_SYS, ACL_COMPILE_SYS) → 单算子动态执行,在真机(ascend910b / dav-2201)launch 已部署的自定义内核 → 与 cpu 后端(手写 FP32 融合 Adam golden)逐输出比对

测试目录共三个文件(见 目录):

文件作用
atk_ApplyAdamD.jsonATK 用例集(约定唯一 ST 工件):3 dtype × useNesterov{0,1} + 边界(rank0/1/2/4/empty)+ 极值(grad=0 / ε=1e-12 / β1_power=1e-6),判据single_bm(MERE/MARE 浮点社区标准,含 atol 组合容差),perf=not_key(软目标)
executor_ApplyAdamD.pyATK 执行器:cpu_apply_adam_d(FP32 融合 Adam golden,BaseApi)+kernel_apply_adam_d(KernelBaseApi,子进程隔离 ACL 上下文经aclop_runner.py真机 launch)
aclop_runner.py独立 ACL 单算子 launch 原语(ctypes →libascendcl.so+libacl_op_compiler.so);派发的实现(custom_nn vs builtin)由ASCEND_CUSTOM_OPP_PATH在 OPP 层决定

其中 aclnn 两段式调用路径由 examples/test_aclnn_apply_adam_d.cpp 单独覆盖,本用例集专注 kernel 后端,两者互为补充。

三、精度判据与用例设计:atk_ApplyAdamD.json 详解

atk_ApplyAdamD.json是 ATK 用例集的唯一 ST 工件,每一条 case 描述一组输入规格与精度标准。整体遵循「3 dtype × useNesterov{0,1} + 边界 + 极值」的矩阵设计,实测共18 个用例(README 记载真机实测 18/18 = 100% 通过,误差远低于 spec 阈值)。

3.1 精度判据 single_bm

每个 case 的standard字段统一声明:

"standard": { "acc": { "single_bm": { "type": "high_performance", "fp32_error": 0.0001220703125, "fp16_error": 0.0009765625, "bf16_error": 0.0078125 } }, "perf": "not_key" }
  • single_bm:MERE/MARE 浮点社区标准(Mean Error Ratio / Max Error Ratio 体系),按 dtype 设置误差上界,并支持 atol 组合容差(即abs_diff <= atol + rtol * |golden|形式的容差判定,见下节 selftest 中的实现)。
  • 三种 dtype 的容差分别为 fp321.220703125e-4、fp169.765625e-4、bf167.8125e-3,与 kernel 内部「统一升 FP32 计算、再降回原 dtype」的精度水平匹配。
  • perf = "not_key":性能为软目标,不作为通过/失败的判定项。

3.2 输入规格设计

每个 case 声明 10 个输入(9 个 tensor + 1 个 bool 属性)。核心 case(如core_fp32_nes0/core_fp32_nes1)的典型规格如下:

  • var:fp32,shape[4, 16],按正态分布生成(mean ∈ [-1, 1],std ∈ [0.1, 0.5]);
  • m:fp32,shape[4, 16],mean ∈ [-0.5, 0.5],std ∈ [0.05, 0.2];
  • v:fp32,shape[4, 16],mean ∈ [0.5, 1.0],std ∈ [0.01, 0.05]——v 从严格正区间生成,保证v_new = beta2*v + (1-beta2)*grad² >= 0sqrt(v_new)为实数,这是 Adam 数学有效性的前提;
  • grad:fp32,shape[4, 16],mean ∈ [-0.5, 0.5],std ∈ [0.05, 0.3];
  • 6 个标量参数beta1_power=0.9、beta2_power=0.999、lr=0.001、beta1=0.9、beta2=0.999、epsilon=1e-7,均为固定有效常量(shape[1],std 为 0),保证「用例 json 自身即可保证融合数学的有效性」;
  • use_nesterov:bool 属性,core_*_nes0为 false、core_*_nes1为 true,分别覆盖两条 var 更新分支。

用例版本号按core_<dtype>_nes<0/1>命名(core_fp32_nes0core_fp32_nes1core_fp16_nes0core_fp16_nes1core_bf16_nes0core_bf16_nes1等),dtype 覆盖 fp32 / fp16 / bf16 三种。除核心 case 外,还包含 rank0/1/2/4 维度边界、empty 空张量、grad=0、ε=1e-12、β1_power=1e-6 等极值场景,用于验证 tiling 与 kernel 的边界处理(尾块、空张量早退、标量极端取值)。

四、CPU golden:executor_ApplyAdamD.py 中的 FP32 参考实现

executor_ApplyAdamD.py 注册了两个 ATK 执行器:

4.1 golden 端:cpu_apply_adam_d(BaseApi)

_golden_fp32用 PyTorch 浮点运算手写 Adam 单步:

lr_t = lr * torch.sqrt(1.0 - beta2_power) / (1.0 - beta1_power) m_out = m + (1.0 - beta1) * (grad - m) v_out = v + (1.0 - beta2) * (grad * grad - v) numerator = (m_out * beta1 + (1.0 - beta1) * grad) if nesterov else m_out var_out = var - lr_t * numerator / (epsilon + torch.sqrt(v_out))

注意 golden 的写法与 kernel 公式严格同构(m + (1-beta1)*(grad - m)等价于beta1*m + (1-beta1)*grad),这是两者数值可比的前提。golden 全程在FP32 精度下计算(与 arch kernel 内部 FP32 计算对齐),最终把三个输出round回算子 dtype(fp16/bf16 的「理想低精度结果」)后返回。

4.2 关键约定:不改变生成输入

执行器注释明确约定:两个执行器都不做init_by_input_data覆写、不修改生成的输入——golden 与 kernel 两端必须消费逐字节一致的生成数据,以保证对拍公平性。同时按算子输出顺序恰好返回三个输出(var, m, v),ATK 会逐个分别比对。

4.3 设备端:kernel_apply_adam_d(KernelBaseApi)

设备端执行器将 10 个输入张量按名字写出为原始字节文件(bf16 通过view(torch.int16)保留位模式),连同 dtype/nesterov/shape 元信息写入临时目录,然后以子进程方式调用aclop_runner.py --io <dir>真机 launch 已部署的算子,最后读回var_out.bin/m_out.bin/v_out.bin还原为张量。子进程方案的核心动机是:ACL 上下文完全隔离,避免与 ATK / torch_npu 已初始化的 ACL 环境冲突(aclop_runner.py内部执行干净的aclInit/aclFinalize)。

五、launch 原语:aclop_runner.py 的 ctypes 实现

aclop_runner.py 是整个对拍的「被测设备 launch 原语」,不依赖任何 Python 算子框架,纯 ctypes 直连 CANN 运行库:

occ.aclopCompileAndExecuteV2(b"ApplyAdamD", 10, in_desc, in_buf, 3, out_desc, out_buf, attr, ACL_ENGINE_SYS, ACL_COMPILE_SYS, None, stream)

5.1 关键实现细节

  • 动态加载ctypes.CDLL("libascendcl.so", RTLD_GLOBAL)+ctypes.CDLL("libacl_op_compiler.so", RTLD_GLOBAL),并手工声明aclCreateTensorDescaclCreateDataBufferaclopSetAttrBoolaclrtMalloc/Memcpy/CreateStream/SynchronizeStreamaclopCompileAndExecuteV2等函数签名;
  • dtype 映射{"fp32": (ACL_FLOAT, 4), "fp16": (ACL_FLOAT16, 2), "bf16": (ACL_BF16, 2)}(ACL_BF16 = 27);
  • 输入顺序:严格对齐算子定义 apply_adam_d_def.cpp 的注册顺序var, m, v, beta1_power, beta2_power, lr, beta1, beta2, epsilon, grad
  • 输出原地语义:3 个输出的aclDataBuffer复用输入地址(in-place ref),即 varOut/mOut/vOut 与 var/m/v 同址,验证原地更新路径;
  • 空张量处理aclrtMalloc(0)非法,代码alloc = max(nb, esz)保证空 tensor 也有合法指针;空输出直接返回空字节;
  • rank-0 支持dims = (c_int64 * max(nd, 1))(...),rank 0 构造为维度 0 的合法 desc;
  • 属性设置use_locking=0(端侧不影响数值)与use_nesterov(0/1)通过aclopSetAttrBool写入;
  • 错误处理aclGetRecentErrMsg捕获失败详情,失败即抛RuntimeError

5.2 两种运行模式

模式入口说明
ATK 模式--io <dir>读取<dir>/meta.json+ 各<name>.bin(小端原始字节),执行算子后写回var_out.bin/m_out.bin/v_out.bin,不做任何数值解释
自检模式--selftest独立验证 launch 胶水:按给定 dtype/n/nesterov 生成确定性数据(如 var 初始0.5 + 0.001*(i%97)),真机执行后与 FP32 融合 CPU golden 以 spec 容差(abs > atol + rtol*|gold|判 bad)逐元素比对,打印maxRel与通过结果

--selftest模式还内嵌了 fp16/bf16 的浮点编解码器(如 bf16 采用round-to-nearest-evenf2bf16),并复刻与 golden 完全一致的公式与容差逻辑,可在不依赖 ATK 的情况下快速冒烟验证单算子链路。

六、运行方法

6.1 前置条件

  1. ApplyAdamD自定义算子包部署到 OPP(构建产物含 op_host tiling + op_kernel 内核);
  2. export ASCEND_CUSTOM_OPP_PATH=<vendors/custom_nn>,使 OPP 层优先派发自定义实现(custom_nnvs builtin 由该环境变量决定);
  3. 具备 ascend910b(Atlas A2 训练/推理系列,DAV_2201)真机环境。

算子包构建命令参见 算子 README:

bash build.sh --pkg --experimental --soc=ascend910b --ops=apply_adam_d -j16

6.2 执行测试

用 ATK 跑该用例集的accuracy 任务(kernel 后端 vs cpu 后端对拍):

atk --task accuracy --case-dir experimental/optim/apply_adam_d/tests/st/aclnnApplyAdamD

按 README 记载,真机实测结果18/18 = 100% 通过,误差远低于 spec 阈值。

6.3 单独验证 launch 胶水(可选)

python3 aclop_runner.py --selftest --dtype fp32 --n 1024 --nesterov 0 python3 aclop_runner.py --selftest --dtype bf16 --n 1024 --nesterov 1

七、底层实现佐证:tiling 与 kernel 是如何被测试覆盖的

该 ST 用例集名为aclnnApplyAdamD但走 kernel 后端,其意义在于独立验证 host tiling + AI Core kernel(不经 aclnn 两段式封装)。结合源码可以看到测试真正覆盖的工程实现:

7.1 host tiling:apply_adam_d_tiling.cpp

  • 单一 tiling 策略:元素总数多核切分 + UB 切分 + 双缓冲;
  • TilingKeyD_T(fp16/bf16/fp32) × USE_NESTEROV(0/1)共 6 组合,经ASCENDC_TPL_SEL_PARAM(context, dtypeKey, useNesterov)模板选择;
  • 多核切分blockFactor按 32B 对齐(fp32: 8 元素、fp16/bf16: 16 元素),并设MIN_ELEM_PER_CORE = 2048下限——小 shape 不铺满全部 AIV 核,避免单核 launch/标量加载/同步开销超过计算本身(n=4096 时约派发 2 核,与 builtin 行为对齐);空 tensor 时usedCoreNum=1, blockFactor=0
  • UB 切分:预留 48KB(系统 + 标量缓冲 + 同步),fp32 按 64 B/elem、fp16/bf16 按 52 B/elem 折算ubFactor,并按 256 对齐取整;
  • 输入契约校验:tiling 前置执行CheckInputContract——m/v/grad 与 var 完全同 shape、6 个标量GetShapeSize()==1、10 输入同 dtype,违规即拒绝下发(避免 kernel 越界 GM 读)。

7.2 AI Core kernel:apply_adam_d_kernel.h

  • 模板类ApplyAdamD<T, USE_NESTEROV>BUFFER_NUM=2双缓冲流水(CopyIn/Compute/CopyOut);
  • 标量读取:6 个标量为运行期 GM[1]张量,Init中经DataCopyPad(32B 对齐槽位)→ UB →GetValue一次性读取,fp16/bf16 先Cast升 FP32 再读,lrT_ = lr * sqrt(1-b2p)/(1-b1p)在标量侧完成,不入热路径
  • 内部计算:fp32 路径直接向量化(Muls/Add/Mul/Sqrt/Adds/Div/Sub),fp16/bf16 路径先Cast四路升 FP32,计算完成后以RoundMode::CAST_RINT降回原 dtype——这正对应了用例集「理想低精度结果」的 golden 约定;
  • 边界处理:尾块/非对齐由DataCopyPad(按字节 blockLen)统一处理;blockLength_ <= 0Process早退(空/rank0 场景)。

7.3 aclnn 两段式路径(配套验证)

若需验证 aclnn 接口路径,可参考 test_aclnn_apply_adam_d.cpp:aclrtMalloc建 10 个aclTensor(6 个标量 shape[1])→aclnnApplyAdamDGetWorkspaceSize取 workspace 与 executor → 按需aclrtMallocworkspace →aclnnApplyAdamD执行 →aclrtSynchronizeStream后拷回并打印var_out/m_out/v_out。示例还展示了输出与输入复用同一 Device 地址构造原地语义的写法,与用例集中 in-place ref 验证互为印证。

八、小结与延伸

tests/st/aclnnApplyAdamD用「一份 JSON 用例工件 + 一对执行器 + 一个 ctypes launch 原语」构建了轻量、可复现、与 aclnn 路径解耦的 kernel 精度验证闭环:JSON 保证数据与判据唯一可信,golden 端保证参考实现与内核公式同构,子进程 + 独立 ACL 上下文保证设备端 launch 干净隔离。这套模式对同类「图模式原型 + 实验性 AscendC 实现」的算子(多标量张量输入、原地多输出)具有直接参考价值。

延伸阅读:

  • 算子整体设计与调用方式:experimental/optim/apply_adam_d/README.md
  • aclnn 两段式接口文档(参数表、返回码、约束):experimental/optim/apply_adam_d/docs/aclnnApplyAdamD.md
  • 图模式 IR:experimental/optim/apply_adam_d/op_graph/experimental_apply_adam_d_proto.h
  • 示例构建与运行脚本:experimental/optim/apply_adam_d/examples/run.sh
  • 单元测试(infershape/tiling 上下文级):experimental/optim/apply_adam_d/tests/ut/
  • 人工智能
  • 算子库
  • 深度学习
  • CANN
  • Ascend

【免费下载链接】ops-nn

本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。

项目地址:https://gitcode.com/cann/ops-nn
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询