- 人工智能
- 算子库
- 深度学习
- CANN
- Ascend
【免费下载链接】ops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
本指南围绕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做一次更新,并输出更新后的var、m、v三个张量。其调用形态为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 * graduseNesterov = false:var_new = var - lr' * m_new / (epsilon + sqrt(v_new))useNesterov = true:var_new = var - lr' * (m_new * beta1 + (1 - beta1) * grad) / (epsilon + sqrt(v_new))
其中 6 个 Adam 标量参数(beta1Power、beta2Power、lr、beta1、beta2、epsilon)在 aclnn 接口中均为shape size 为 1 的aclTensor(而非aclScalar),varOut、mOut、vOut可与var、m、v复用 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.json | ATK 用例集(约定唯一 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.py | ATK 执行器: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 的容差分别为 fp32
1.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² >= 0、sqrt(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_nes0、core_fp32_nes1、core_fp16_nes0、core_fp16_nes1、core_bf16_nes0、core_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),并手工声明aclCreateTensorDesc、aclCreateDataBuffer、aclopSetAttrBool、aclrtMalloc/Memcpy/CreateStream/SynchronizeStream、aclopCompileAndExecuteV2等函数签名; - 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-even的f2bf16),并复刻与 golden 完全一致的公式与容差逻辑,可在不依赖 ATK 的情况下快速冒烟验证单算子链路。
六、运行方法
6.1 前置条件
- 将
ApplyAdamD自定义算子包部署到 OPP(构建产物含 op_host tiling + op_kernel 内核); export ASCEND_CUSTOM_OPP_PATH=<vendors/custom_nn>,使 OPP 层优先派发自定义实现(custom_nnvs builtin 由该环境变量决定);- 具备 ascend910b(Atlas A2 训练/推理系列,DAV_2201)真机环境。
算子包构建命令参见 算子 README:
bash build.sh --pkg --experimental --soc=ascend910b --ops=apply_adam_d -j166.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 切分 + 双缓冲;
- TilingKey:
D_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_ <= 0时Process早退(空/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上加速计算。
相关推荐
CANN ops-math 随机算子系统测试实战:aclnnBernoulli ATK ST 用例设计与统计验证
CANN ops math 随机算子系统测试实战:aclnnBernoulli ATK ST 用例设计与统计验证 导读 本文以 experimental/ran
算子库人工智能CANNCANN ops-nn 算子深度指南:ClippedSwigluGrad 反向梯度算子(aclnnClippedSwigluGrad)
CANN ops nn 算子深度指南:ClippedSwigluGrad 反向梯度算子(aclnnClippedSwigluGrad) 本文围绕 CANN 神经
人工智能算子库深度学习CANNAscendCANN ops-nn HardSigmoidV2 算子全解析:ACLNN 两段式接口、AscendC Kernel 实现与测试验证
CANN ops nn HardSigmoidV2 算子全解析:ACLNN 两段式接口、AscendC Kernel 实现与测试验证 导读 HardSigmoi
人工智能算子库深度学习CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考