
CANN ops-nn 中 SwishGrad 算子解析原理、实现与 aclnnSwishBackward 调用指南【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn导读SwishGrad 是 CANN 神经网络算子库ops-nn中用于计算 Swish 激活函数梯度的反向算子实现在仓库的 experimental/activation/swish_grad 目录下面向 Atlas A2 训练系列产品/Atlas 800I A2 推理产品提供 fp16、fp32、bf16 三种精度的梯度计算能力。本文以 swish_grad/README.md 为核心骨架结合仓库中算子定义、Host 侧 tiling、Kernel 侧昇腾 AI Core 实现与单元测试完整讲解 SwishGrad 的数学原理、参数约束、两段式 aclnn 接口调用流程与源码级实现机制帮助开发者快速上手在 NPU 上完成 Swish 反向传播的接入与调试。一、算子功能与数学原理1.1 功能定位SwishGrad 算子是 Swish 激活函数activation/swish的反向传播算子求 Swish 函数的梯度。它接收反向传播传入的梯度grad、正向输入x以及可调节参数scale输出对输入x的梯度grad_x。从算子库的接口关系来看上层对外暴露的 API 名为aclnnSwishBackward其功能说明文档 docs/aclnnSwishBackward.md 明确指出它是 aclnnSwish仓库实际路径为 activation/swish/docs/aclnnSwish.md激活函数的反向传播用于计算 Swish 激活函数的梯度。这与 torch 生态中的swish_backward语义一一对应。1.2 计算公式根据 swish_grad/README.md 的功能说明SwishGrad 的计算公式如下$$ y sigmoid(scalex) xsigmoid(scale*x) $$$$ sigmoid sigmoid*(1 - sigmoid) $$其中scale为可调节的斜率参数。更精确的推导形式见 docs/aclnnSwishBackward.mdSwish 正向函数$$ s(x) x*\sigma(\beta x) $$其导数$$ s^\prime(x) \beta s(x)\sigma(\beta x)(1-\beta s(x)) \sigma(\beta x)*(1\beta x(1-\sigma(\beta x))) $$最终输出梯度$$ gradInput gradOutput * s^\prime(x) $$其中 Sigmoid 函数定义为$$ \sigma(x) {\frac{1} {1{e}^{-x}}} $$可见 README 中的两条公式本质是先算 sigmoid 值再算 1 - sigmoid即导数因子最后与梯度逐元素相乘这一计算链的简写。对照 op_kernel/swish_grad.h 的Compute实现可以逐行印证该公式σ(βx) 1 / (1 exp(-βx)) σ(βx) σ(βx) * (1 - σ(βx)) s(x) σ(βx) βx * σ(βx) // 即 σ(βx) * (1 βx(1 - σ(βx))) grad_x grad * s(x)1.3 参数说明README 中对算子原语的参数定义如下参数名输入/输出/属性描述数据类型数据格式grad输入待进行 SwishGrad 计算的入参公式中的 gradfp16、fp32、bf16ND, FRACTAL_NZ, NC1HWC0x输入待进行 SwishGrad 计算的入参公式中的 xfp16、fp32、bf16ND, FRACTAL_NZ, NC1HWC0scale输入待进行 SwishGrad 计算的入参公式中的 scalefp321标量y输入待进行 SwishGrad 计算的入参暂时不参与运算fp16、fp32、bf16ND, FRACTAL_NZ, NC1HWC0grad_x输出待进行 SwishGrad 计算的出参公式中的输出fp16、fp32、bf16ND, FRACTAL_NZ, NC1HWC0约束说明无。需要指出两点值得注意的细节算子定义中的y输入暂时不参与运算。这在 op_kernel/swish_grad.cpp 的 kernel 入口中可以看到swish_grad内核函数虽然接收GM_ADDR y但 op_kernel/swish_grad.h 的Init只为grad、x、grad_x建立了 GlobalBuffery并未被读取属于为后续版本预留的入参。上述表格描述的是底层算子的参数形态对上层用户而言真正接触的是aclnnSwishBackward接口的参数见第三节。二、算子源码结构SwishGrad 在仓库中采用标准的 CANN 算子工程布局experimental/activation/swish_grad/ ├── docs/aclnnSwishBackward.md # aclnn 接口使用文档 ├── examples/test_aclnn_swish_grad.cpp # 可运行调用样例 ├── op_host/ │ ├── op_api/ # l0 算子接口封装l0op::SwishGrad │ │ ├── aclnn_swish_backward.cpp/h # 对外 aclnn 两段式接口 │ │ └── swish_grad.cpp/h # l0 算子封装与 launcher │ ├── swish_grad_def.cpp # 算子原语注册OpDef │ ├── swish_grad_infershape.cpp # 输出 shape 推导 │ └── swish_grad_tiling.cpp # Host 侧 tiling 计算 ├── op_kernel/ │ ├── swish_grad.cpp # AI Core 内核入口__global__ __aicore__ │ ├── swish_grad.h # Kernel 类实现双缓冲流水 │ ├── swish_grad_tiling_data.h # tiling 参数结构体 │ └── swish_grad_tiling_key.h # tiling key └── tests/ut/ # op_api / op_host / op_kernel 三级单测整个调用链为aclnnSwishBackwardGetWorkspaceSize→l0op::SwishGradHost 侧拼装算子→ADD_TO_LAUNCHER_LIST_AICORE下发 → AI Core 内核swish_grad执行。下面逐层展开。2.1 算子定义注册op_defswish_grad_def.cpp 中通过OP_TYPE_REGISTER/OP_ADD注册名为SwishGrad的算子四个数据对象输入grad、x、y输出grad_x数据类型支持DT_FLOAT16、DT_FLOAT、DT_BF16格式支持ND、NC1HWC0、FRACTAL_NZ三种属性sconfAttrType(OPTIONAL).Float(1.0)即可选的 float 型斜率参数默认值为 1.0对应公式中的scale/βAICore 配置DynamicCompileStaticFlag(true)、DynamicRankSupportFlag(true)、DynamicShapeSupportFlag(true)、PrecisionReduceFlag(true)注册到ascend910bSoC对应 Atlas A2 系列并通过ExtendCfgInfo(opFile.value, swish_grad)指定 kernel 入口文件名为swish_grad.cpp。2.2 输出 shape 推导infershapeswish_grad_infershape.cpp 的实现非常直接读取输入x的 shape并将输出grad_x的 shape 赋值为与之完全相同*yShape *xShape即逐元素反向算子输出与输入同 shape、同 dtype。这与 README 中grad、x 与 grad_x 的 shape/数据类型一致的约束一致。2.3 Host 侧 tilingswish_grad_tiling.cppswish_grad_tiling.cpp 负责在 Host 侧根据输入规模与硬件资源计算切分方案GetPlatformInfo通过platform_ascendc::PlatformAscendC获取 UBUnified Buffer大小、AI Core 核数以及块对齐粒度GetUbBlockSizeGetWorkspaceSize查询系统库 API 所需 workspace 大小并回填给框架GetShapeAttrsInfo根据输入存储 shape 与数据类型字节数计算总数据量结合 UB 容量与双缓冲需求BUFFER_NUM 2确定每个核负责的数据量bigCoreDataNum/smallCoreDataNum、tile 内数据量tileDataNum以及尾部数据tailDataNum。最终产出的 tiling 数据结构定义在 swish_grad_tiling_data.hstruct SwishGradTilingData { uint64_t smallCoreDataNum; uint64_t bigCoreDataNum; uint64_t finalBigTileNum; uint64_t finalSmallTileNum; uint64_t tileDataNum; uint64_t smallTailDataNum; uint64_t bigTailDataNum; uint64_t tailBlockNum; float sconf; };其核心思想是多核负载均衡 尾部数据处理把总数据量按核数均分若不能整除则前tailBlockNum个核多承担bigCoreDataNum个数据其余核承担smallCoreDataNum个数据每个核内部再按tileDataNum切成多个 tile 循环处理最后的余量作为尾 tile 单独处理。2.4 AI Core 内核实现op_kernelKernel 入口 swish_grad.cpp 通过REGISTER_TILING_DEFAULT读取 tiling 数据实例化NsSwishGrad::KernelSwishGradDTYPE_X并调用Init与Process。核心实现位于 swish_grad.h其设计要点如下1双缓冲流水线TPipe上建立了 VECIN 队列inQueueGrad、inQueueX、inQueueY与 VECOUT 队列outQueueGrad缓冲区个数均为BUFFER_NUM 2实现 CopyIn → Compute → CopyOut 三阶段重叠执行。2fp16/bf16 与 fp32 两条计算路径对float16_t/bfloat16_t先把x通过Cast提升到 float 计算使用临时缓冲区tmpQueue0/1/2经历Muls乘 β→Muls(-1.0)→Exp→Adds(1.0)→Div得到 σ(βx)→Sub得到 1-σ(βx)→Mul得到 βx·σ(βx)→Add(1.0)得到 1βx(1-σ(βx))→Mul与 σ(βx) 相乘 → 再与 grad 相乘最后以CAST_RINT舍入模式转回半精度输出对float直接在浮点路径上完成同样序列的运算全程使用 float 精度输出不降精度。3多核数据切片Init中根据coreId与tailBlockNum的关系选择大核/小核的数据区间与 GlobalBuffer 偏移每个核只处理自己负责的连续数据段。内核从sconf即 scale/β读取斜率参数其值由 Host 侧在aclnnSwishBackwardGetWorkspaceSize中从betaOptional标量转换而来缺省为 1.0f。2.5 测试用例佐证仓库为算子提供了三级单元测试可用于验证实现与本文结论op_api 层tests/ut/op_api/test_aclnn_swish_backward.cpp覆盖 float32、float16、bf16 等场景构造{2,5}的 gradOutput、self值域 [-1,1]与 beta如 1.1f、0.01f断言aclnnSwishBackwardGetWorkspaceSize返回ACLNN_SUCCESS并设置了精度容差op_host 层tests/ut/op_host/test_swish_grad_tiling.cpp验证 tiling 计算产物各核数据量、tile 数等符合预期op_kernel 层tests/ut/op_kernel/test_swish_grad.cpp直接对 AI Core 内核做数值验证。三、aclnnSwishBackward 接口详解3.1 产品支持情况产品是否支持Atlas A2 训练系列产品/Atlas 800I A2 推理产品√对应到 SoC 层面算子原语注册于ascend910b平台见 swish_grad_def.cpp且 aclnn_swish_backward.cpp 中的CheckSocVersionIsSupportBf16显示 bf16 支持范围限定在ASCEND910B至ASCEND910E之间的 SoC 版本。3.2 两段式接口模型与 CANN 其他 aclnn 算子一致aclnnSwishBackward采用两段式接口详见 docs/zh/context/two_phase_api.md先调用GetWorkspaceSize接口完成入参校验、算子拼装并获取所需 workspace 大小与执行器再调用执行接口真正下发计算。第一段接口原型aclnnStatus aclnnSwishBackwardGetWorkspaceSize( const aclTensor* gradOutput, // 正向输出梯度公式中的 gradOutput const aclTensor* self, // Swish 激活函数输入公式中的 x const aclScalar* betaOptional, // 可调节参数 β标量空指针时按 1.0 计算 aclTensor* gradInput, // 输出对输入的梯度 uint64_t* workspaceSize, aclOpExecutor** executor)第二段接口原型aclnnStatus aclnnSwishBackward( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)3.3 第一段接口参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 TensorgradOutput输入Swish 激活函数正向输出的梯度公式中的 gradOutput支持空 TensorgradOutput、self 与 gradInput 的 shape 一致三者的数据类型一致BFLOAT16、FLOAT16、FLOATND0-8√self输入用于计算激活函数的张量公式中的 x支持空 Tensor同上BFLOAT16、FLOAT16、FLOATND0-8√betaOptional输入可调节参数控制 Swish 函数的形状和斜率公式中的 β数据类型需可转换为 FLOAT参见 docs/zh/context/deduction_relationship.md 的互推导关系空指针时以 1.0 计算----gradInput输出backward 计算的输出Swish 正向输入的梯度值支持空 Tensorshape 与数据类型同前两者BFLOAT16、FLOAT16、FLOATND0-8√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含算子计算流程-----返回值aclnnStatus具体返回码参见 docs/zh/context/aclnn_return_code.md。第一段接口会完成入参校验以下场景会报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 gradOutput、self 或 gradInput 是空指针ACLNN_ERR_PARAM_INVALID161002gradOutput、self、betaOptional 或 gradInput 的数据类型不在支持范围之内ACLNN_ERR_PARAM_INVALID161002gradOutput、self 和 gradInput 的数据类型不同ACLNN_ERR_PARAM_INVALID161002gradOutput、self 和 gradInput 的 shape 不同3.4 第二段接口参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口aclnnSwishBackwardGetWorkspaceSize获取executor输入op 执行器包含算子计算流程stream输入指定执行任务的 Stream返回值同样为aclnnStatus。约束说明无。3.5 接口内部实现要点aclnn_swish_backward.cpp 的实现清晰展示了接口层的处理流程可作为阅读模板参数校验CheckParams依次执行空指针检查CheckNotNull、数据类型检查CheckDtypeValid支持列表为 FLOAT16/FLOAT/BF16且校验 betaOptional 可转 FLOAT、三个张量 dtype 一致、bf16 受 SoC 版本约束与 shape 检查CheckShapeValid维度上限为MAX_SUPPORT_DIMS_NUMS三个张量 shape 一致空 Tensor 短路当gradOutput或self为空时workspaceSize直接置 0 返回不执行计算连续性处理对非连续输入调用l0op::Contiguous转为连续 Tensor对应参数表中非连续 Tensor √的支持能力高维 reshape当输入维度超过 8 时将输入展平为 1 维长 Tensor 交给 kernel计算完后再Reshape回原维度β 转换betaOptional通过ToFloat()转为 float空指针时取默认值1.0f下发与收尾调用l0op::SwishGrad完成算子拼装通过l0op::ViewCopy将结果拷贝到可能非连续的输出gradInput上最终以GetWorkspaceSize()汇总整体 workspace 需求第二段接口则直接调用框架通用执行函数CommonOpExecutorRun完成异步下发。四、完整调用示例与运行指引4.1 可运行样例仓库提供了可直接参考的完整样例 examples/test_aclnn_swish_grad.cpp编译与运行方法可参照 docs/zh/context/compile_and_run_sample.md。样例的核心流程如下为便于阅读做了精简完整代码以仓库文件为准#include acl/acl.h #include aclnn_swish_backward.h int main() { // 1. 固定写法device/stream 初始化 int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); // aclInit aclrtSetDevice aclrtCreateStream // 2. 构造输入与输出示例 shape 均为 {2, 3} std::vectorint64_t gradOutputShape {2, 3}; std::vectorint64_t selfShape {2, 3}; std::vectorint64_t gradInputShape {2, 3}; std::vectorfloat gradOutHostData {1, 1, 1, 1, 1, 1}; std::vectorfloat selfHostData {1, 2, 3, 4, 5, 6}; float betaValue 1.1f; // 用 aclrtMalloc/aclrtMemcpy 把 host 数据搬到 device并 aclCreateTensor 创建 aclTensor aclTensor* gradOut CreateAclTensor(gradOutHostData, gradOutputShape, ..., ACL_FLOAT, ...); aclTensor* self CreateAclTensor(selfHostData, selfShape, ..., ACL_FLOAT, ...); aclTensor* gradInput CreateAclTensor(/* 输出占位 */, gradInputShape, ..., ACL_FLOAT, ...); aclScalar* betaOptional aclCreateScalar(betaValue, aclDataType::ACL_FLOAT); // 3. 两段式调用 uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnSwishBackwardGetWorkspaceSize(gradOut, self, betaOptional, gradInput, workspaceSize, executor); void* workspaceAddr nullptr; if (workspaceSize 0) { aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); } ret aclnnSwishBackward(workspaceAddr, workspaceSize, executor, stream); // 4. 同步等待执行结束 aclrtSynchronizeStream(stream); // 5. 将 device 侧结果拷回 host 并打印本例输出为 gradInput grad * s(x) std::vectorfloat outData(6, 0); aclrtMemcpy(outData.data(), ..., gradInputDeviceAddr, ..., ACL_MEMCPY_DEVICE_TO_HOST); // 6-7. 释放 aclTensor / aclScalar / device 内存 / stream并 aclFinalize return 0; }样例中gradOutput {1,1,1,1,1,1}、self {1,2,3,4,5,6}、beta 1.1执行后打印的 6 个输出值即为对应位置的grad * s(x)数值可直接对照第一节的公式手工验算。4.2 调用步骤要点初始化aclInit(nullptr)→aclrtSetDevice(deviceId)→aclrtCreateStream(stream)构造 Tensorshape/stride 一致、数据类型为 FLOAT16/FLOAT/BF16、格式为 ND支持非连续 Tensor接口内部会自动Contiguous支持空 Tensor两段式调用第一段拿到workspaceSize与executor若workspaceSize 0则在 device 侧申请内存然后调用第二段aclnnSwishBackward同步与取数aclrtSynchronizeStream后通过aclrtMemcpyDEVICE_TO_HOST读取输出资源释放依次释放aclTensor/aclScalar、device 内存、workspace、stream最后aclrtResetDeviceaclFinalize。4.3 注意事项三个张量gradOutput、self、gradInput的shape 与数据类型必须完全一致否则第一段接口返回ACLNN_ERR_PARAM_INVALID161002betaOptional传nullptr时按β 1.0计算此时算子退化为 PyTorch 中默认swish的梯度形式传入时其数据类型需可互推导为 FLOATbf16 输入仅在支持 bf16 的 SoC 版本ASCEND910B~ASCEND910E上可用该算子底层为逐元素计算输出与输入同 shapey入参为预留项当前版本不参与运算。五、总结SwishGrad 算子是 ops-nn 仓库中一个典型的逐元素反向算子样例完整展示了 CANN 算子的标准五件套OpDef 注册swish_grad_def.cpp、InferShapeswish_grad_infershape.cpp、Host 侧 tilingswish_grad_tiling.cpp、AI Core 内核op_kernel/swish_grad.h与 aclnn 两段式对外接口aclnn_swish_backward.cpp。理解其数学推导σ(βx)·(1βx(1-σ(βx)))、多核均衡 tiling 策略、半精度转浮点计算的精度处理以及两段式接口的调用范式即可举一反三地接入 ops-nn 中其他同构的激活函数反向算子如 SiluGrad。实际开发中若需验证算子行为可参考仓库 tests/ut 下的 op_api、op_host、op_kernel 三级测试用例或直接编译运行 examples/test_aclnn_swish_grad.cpp 样例。【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考