CANN ops-nn 算子 MishGradV2(aclnnMishBackward)接口与实现深度解析
【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn
MishGradV2 是 CANN ops-nn 算子库中用于计算 Mish 激活函数反向梯度的 AscendC 算子,对外通过aclnnMishBackward两段式 ACLNN 接口提供服务。本文以 experimental/activation/mish_grad_v2/README.md 为骨架,结合仓库内 op_host、op_kernel、examples 与 tests 源码,完整梳理其功能公式、ACLNN 接口原型、参数约束、源码实现原理以及编译运行与单测方法,帮助开发者在 Atlas 系列产品上快速接入该反向算子。
产品支持情况
MishGradV2 算子在不同硬件产品上的支持情况如下表所示(来自 README 与 aclnnMishBackward 接口文档):
| 产品 | 是否支持 |
|---|---|
| Ascend 950PR/Ascend 950DT | √ |
| Atlas A3 训练系列产品/Atlas A3 推理系列产品 | √ |
| Atlas A2 训练系列产品/Atlas A2 推理系列产品 | √ |
| Atlas 200I/500 A2 推理产品 | × |
| Atlas 推理系列产品 | √ |
| Atlas 训练系列产品 | √ |
需要特别注意的是,不同产品在数据类型支持上存在差异:Atlas 训练系列产品仅支持 FLOAT16、FLOAT32;而 Ascend 950PR/Ascend 950DT、Atlas A3 系列、Atlas A2 系列产品额外支持 BFLOAT16。这一点在源码op_host/op_api/aclnn_mish_backward_v2.cpp中也有对应体现,详见后文「源码实现原理」小节。
功能说明
- 算子功能:计算 Mish 激活函数的反向梯度。
- 命名说明:目录
experimental/activation/mish_grad_v2复用了ascend-kernel/csrc/ops/mish_grad中已经验证的 AscendC 实现,目录名保留v2仅用于区分新实现,对外导出的 ACLNN 接口名称仍为aclnnMishBackward,调用方无需感知版本差异。 - 入口文件:
op_host/op_api/aclnn_mish_backward_v2.cpp是对外 ACLNN 两段式接口入口。 - 内部封装:
op_host/op_api/mish_grad_v2.h与op_host/op_api/mish_grad_v2.cpp提供内部l0op::MishGradV2封装,其函数签名在 mish_grad_v2.h 中声明为:
namespace l0op { const aclTensor* MishGradV2(const aclTensor* grad, const aclTensor* x, const aclTensor* tanhx, aclOpExecutor* executor); }其中tanhx参数为可选输入,传nullptr时内核内部会自行计算 tanh(softplus(x))(详见后文内核实现部分)。
计算公式
Mish 反向梯度(即对输入 x 的梯度)计算公式为:
$$ xGrad = grad \times \left(tanhx + x \times \frac{1 - tanhx^2}{1 + e^{-x}}\right) $$
其中:
$$ tanhx = tanh(softplus(x)),\quad softplus(x) = relu(x) + \log(1 + e^{-|x|}) $$
这里的 softplus 采用了数值稳定的形式:relu(x) + log(1 + e^{-|x|}),避免在 x 绝对值较大时出现指数溢出。示例代码 test_aclnn_mish_grad_v2.cpp 中给出了对应的 CPU 参考实现(StableSoftplus与MishGradRef),可用于验证算子计算结果。
调用方式
| 调用方式 | 是否支持 |
|---|---|
| ACLNN 调用 | 是 |
ACLNN 接口
函数原型
MishGradV2 采用 ACLNN 两段式调用模式,整体流程是先调用aclnnMishBackwardGetWorkspaceSize获取 workspace 大小与 op 执行器,再调用aclnnMishBackward实际执行计算(两段式接口的通用说明可参考 docs/zh/context/two_phase_api.md):
aclnnStatus aclnnMishBackwardGetWorkspaceSize( const aclTensor *gradOutput, const aclTensor *self, aclTensor *gradInput, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnMishBackward( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream);详细的参数与返回值说明见 docs/aclnnMishBackward.md,下文对两个接口分别展开。
参数说明
三个 Tensor 参数的通用说明如下(数据格式均为 ND,维度 0-8,均支持非连续 Tensor):
| 参数名 | 输入/输出 | 描述 | 数据类型 | 数据格式 |
|---|---|---|---|---|
| gradOutput | 输入 | 上游反向传播输入梯度。 | FLOAT、FLOAT16、BFLOAT16(仅 Ascend910B 及后续同代 SoC 支持) | ND |
| self | 输入 | Mish 正向输入。 | FLOAT、FLOAT16、BFLOAT16(仅 Ascend910B 及后续同代 SoC 支持) | ND |
| gradInput | 输出 | Mish 反向梯度计算结果。 | FLOAT、FLOAT16、BFLOAT16(仅 Ascend910B 及后续同代 SoC 支持) | ND |
第一段接口 aclnnMishBackwardGetWorkspaceSize 参数
| 参数名 | 输入/输出 | 描述 | 使用说明 |
|---|---|---|---|
| gradOutput(aclTensor*) | 输入 | 上游反向传播输入梯度。 | 支持空 Tensor;shape 必须与 self 和 gradInput 完全一致;数据类型必须与 self 和 gradInput 完全一致。 |
| self(aclTensor*) | 输入 | Mish 正向输入。 | 支持空 Tensor;shape 必须与 gradOutput 和 gradInput 完全一致;数据类型必须与 gradOutput 和 gradInput 完全一致。 |
| gradInput(aclTensor*) | 输出 | Mish 反向梯度计算结果。 | 支持空 Tensor;shape 必须与 gradOutput 和 self 完全一致;数据类型必须与 gradOutput 和 self 完全一致。 |
| workspaceSize(uint64_t*) | 输出 | 返回需要在 Device 侧申请的 workspace 大小。 | - |
| executor(aclOpExecutor**) | 输出 | 返回 op 执行器,包含算子计算流程。 | - |
第一段接口会完成入参校验,校验失败的返回码及错误码如下(aclnn 返回码的整体说明可参考 docs/zh/context/aclnn_return_code.md):
| 返回码 | 错误码 | 描述 |
|---|---|---|
| ACLNN_ERR_PARAM_NULLPTR | 161001 | 传入的 grad、x 或 xGrad 是空指针。 |
| ACLNN_ERR_PARAM_INVALID | 161002 | grad、x 或 xGrad 的数据类型不在支持范围内;grad、x 和 xGrad 的数据类型不一致;grad、x 和 xGrad 的 shape 不一致;grad、x 或 xGrad 的维度大于 8;当前平台不支持 BFLOAT16。 |
第二段接口 aclnnMishBackward 参数
| 参数名 | 输入/输出 | 描述 |
|---|---|---|
| workspace | 输入 | 在 Device 侧申请的 workspace 内存地址。 |
| workspaceSize | 输入 | 在 Device 侧申请的 workspace 大小,由第一段接口 aclnnMishBackwardGetWorkspaceSize 获取。 |
| executor | 输入 | op 执行器,包含算子计算流程。 |
| stream | 输入 | 指定执行任务的 Stream。 |
两段接口均返回aclnnStatus状态码,用于判断调用是否成功。
约束说明
gradOutput、self和gradInput的 shape 必须完全一致,不支持 broadcast。gradOutput、self和gradInput的数据类型必须完全一致,不支持隐式类型提升。- 支持非连续 Tensor,内部会执行连续化和
ViewCopy(非连续 Tensor 的处理机制可参考 docs/zh/context/non_contiguous_tensor.md)。 - 支持空 Tensor。
- 默认确定性实现(确定性计算说明可参考 docs/zh/context/determinism_compute.md)。
源码实现原理
本节结合仓库源码,从接口入口、算子定义、内核计算、tiling 四个层面剖析 MishGradV2 的实现。
接口入口:参数校验与计算图构建
aclnn_mish_backward_v2.cpp 中aclnnMishBackwardGetWorkspaceSize的实现要点:
- 入参校验:通过
CheckParams依次校验三个 Tensor 非空(OP_CHECK_NULL)、数据类型在支持列表内且三者一致(OP_CHECK_DTYPE_NOT_SUPPORT/OP_CHECK_DTYPE_NOT_MATCH)、维度不超过 8 且 shape 完全一致(OP_CHECK_MAX_DIM/OP_CHECK_SHAPE_NOT_EQUAL)。这与接口文档中 161001/161002 返回码的说明一一对应。 - 平台相关数据类型:源码通过
GetDtypeSupportList()按 SoC 版本区分支持列表——Ascend910B 至 Ascend910E 区间以及 regbase 平台使用{DT_FLOAT, DT_FLOAT16, DT_BF16},其余平台仅支持{DT_FLOAT, DT_FLOAT16},与文档中「BFLOAT16 仅 Ascend910B 及后续同代 SoC 支持」的描述一致。 - 空 Tensor 短路:若
grad或x为空,直接令workspaceSize = 0返回成功,不构建计算图。 - 计算流程构建:依次构造
l0op::Contiguous(grad)、l0op::Contiguous(x)完成连续化,再调用l0op::MishGradV2(gradContiguous, xContiguous, nullptr, executor)构建梯度计算节点(tanhx传nullptr,表示由内核自行计算 tanhx),最后通过l0op::ViewCopy将结果写入xGrad,实现非连续输出的支持。
算子定义与 shape 推导
- mish_grad_v2_def.cpp 通过
OP_ADD(MishGradV2)注册算子定义:输入为grad(REQUIRED)、x(REQUIRED)、tanhx(OPTIONAL,可选),输出为x_grad(REQUIRED),均支持DT_FLOAT/DT_FLOAT16/DT_BF16与FORMAT_ND,并声明了动态 rank、动态 shape 支持以及AutoContiguous属性。 - mish_grad_v2_infershape.cpp 实现 shape 推导:输出 shape 直接继承
grad的输入 shape(*outputShape = *gradShape),这与「三个 Tensor shape 必须一致」的约束自洽。
AscendC 内核:按数据类型分派
内核入口 mish_grad_v2.cpp 根据模板参数D_T_X分派:
- float:使用
KernelMishGradFloat,在本地 buffer 上直接以 float 精度计算。 - half / bf16:使用
KernelMishGradCast,先将输入Cast到 float 精度完成全部中间运算,最后再以CAST_ROUND模式转回原精度输出,以保证 FLOAT16/BFLOAT16 下的数值精度。
内核类的核心实现位于 mish_grad_v2.h:
- tanhx 计算:
ComputeTanhxCommon按公式依次执行Relu、Abs、Exp、Ln、Add、Tanh,即数值稳定的 softplus 加 tanh;当 tiling 数据标记haveTanhx == 0(即未传入可选 tanhx 输入)时,内核会自行完成该计算,并复用暂存 buffer 存放结果。 - 梯度核心计算:
ComputeCoreCommon按公式Mul(y, tanhx, tanhx)→Muls(-1.0)→Adds(1.0)计算1 - tanhx²,Exp(-x) + 1计算分母,再Div、Mul、Add、Mul(grad)完成整体表达式,与文档公式严格一致。 - 流水与搬运:内核使用双 buffer(
BUFFER_NUM = 2)的TQue输入/输出队列实现数据搬运与计算重叠,DataCopyPad配合 32 字节对齐的AlignUp处理尾部非对齐数据,CopyOutTile通过DataCopyExtParams按有效长度写出结果。
tiling:多核切分与 tile 划分
mish_grad_v2_tiling.cpp 负责生成 tiling 数据:
- 通过
PlatformAscendC获取 AIV core 数量与 UB 内存大小; - 按 512 字节 cache line 粒度将总元素数切分为
formerNum个长度为formerLength的主块加一个tailLength尾块,实现多核负载均衡; - 根据 UB 大小(除以系数
BUFFER_COEFFICIENT = 36)计算单 tile 长度tileLength,并按 32 字节对齐; - 将
formerNum/formerLength/tailLength/tileLength/haveTanhx写入 mish_grad_v2_tiling_data.h 对应的结构体,通过ASCENDC_TPL_SEL_PARAM选择模板参数并设置blockDim。
tiling 阶段同样校验 grad 与 x 的 shape/dtype 一致(可选 tanhx 传入时一并校验),并申请系统级 workspace。
目录说明
| 路径 | 说明 |
|---|---|
| examples/test_aclnn_mish_grad_v2.cpp | aclnnMishBackward两段式调用示例。 |
| examples/run.sh | 编译并运行 example 的脚本。 |
| docs/aclnnMishBackward.md | aclnnMishBackward接口文档。 |
| tests/ut/op_host/test_aclnn_mish_backward.cpp | op_api单元测试。 |
Example 运行
示例程序 test_aclnn_mish_grad_v2.cpp 完整演示了两段式调用流程:aclInit/aclrtSetDevice/aclrtCreateStream初始化环境 →aclrtMalloc申请 Device 内存并通过aclCreateTensor构造 ND 格式aclTensor→ 调用aclnnMishBackwardGetWorkspaceSize获取 workspace 大小与 executor → 按需aclrtMalloc申请 workspace → 调用aclnnMishBackward执行 →aclrtSynchronizeStream同步后回拷结果,并与基于StableSoftplus/MishGradRef计算的 CPU 参考值按1e-5误差阈值比对,输出compat example passed表示通过。
运行方式如下:
source /usr/local/Ascend/cann/set_env.sh export LD_LIBRARY_PATH=/usr/local/Ascend/cann/opp/vendors/customize_nn/op_api/lib:${LD_LIBRARY_PATH} cd <ops-nn-repo>/experimental/activation/mish_grad_v2/examples bash run.sh其中run.sh内部会自动完成上述环境变量设置,并以g++ -std=c++17 -O2编译链接-lcust_opapi -lnnopbase -lascendcl后直接运行可执行文件;若环境变量ASCEND_HOME_PATH未设置,脚本默认使用/usr/local/Ascend/cann作为 CANN 根目录。
Tests 运行
单元测试 test_aclnn_mish_backward.cpp 覆盖了典型功能与异常分支:case_001_float(shape{2,2,3,2},精度阈值 0.0001)、case_002_float16、case_003_bfloat16(仅在 Ascend910B 至 Ascend910E 平台校验成功,其余平台断言返回ACLNN_ERR_PARAM_INVALID)、case_004_invalid_type_double与case_005_invalid_type_int32(非法类型返回ACLNN_ERR_PARAM_INVALID)、case_006_mixed_dtype_not_supported(混用 FLOAT 与 FLOAT16 报错)、case_007_empty_tensor(空 Tensor 支持)等用例,与接口文档中的约束与返回码定义相互印证。
执行方式:
source /usr/local/Ascend/cann/set_env.sh cd <ops-nn-repo> bash build.sh --experimental --ops=mish_grad_v2 -u --opapi -j8 -O2上述命令以--experimental开启实验特性目录、--ops=mish_grad_v2指定算子、-u运行单元测试、--opapi编译 op_api 接口,-j8 -O2指定并行度与编译优化等级。
【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考