ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CANN ops-nn 算子解析:aclnnSiluBackward 两段式接口实现 SiLU 反向传播梯度计算

2026/9/20 20:11:28 拓冰建站 浏览量
CANN ops-nn 算子解析:aclnnSiluBackward 两段式接口实现 SiLU 反向传播梯度计算 人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载本篇文章以 CANN 神经网络算子库 ops-nn 中 aclnnSiluBackward 官方文档 为主体结合仓库内算子定义、Host 侧 API 实现、AscendC Kernel 与 Tiling 源码系统讲解如何通过 aclnnSiluBackwardGetWorkspaceSize / aclnnSiluBackward 两段式接口在 Atlas A2 系列产品上完成 SiLUSwish激活函数反向传播的梯度计算。读完本文你将掌握该算子的数学原理、参数约束、错误码语义、完整可编译的调用示例以及从 aclnn API 到 AI Core Kernel 的底层实现链路。产品支持情况aclnnSiluBackward 当前支持的产品如下产品是否支持Atlas A2 训练系列产品 / Atlas 800I A2 推理产品√功能说明与计算公式aclnnSiluBackward 是 aclnnSilu 激活函数即 SiLU也称 Swishβ1的反向传播接口用于计算 SiLU 激活函数的梯度。它在网络的反向阶段根据正向输入x与上游传回的梯度gradOutput计算输入侧的梯度gradInput是训练 SiLU 激活层必不可少的算子。SiLU 函数公式$$ y x \cdot \sigma(x) $$SiLU 函数的导数$$ \sigma(x) \frac{1}{1 e^{-x}} $$$$ y \sigma(x) \cdot (1 x \cdot (1 - \sigma(x))) $$$$ gradInput gradOutput \cdot \sigma(x) \cdot (1 x \cdot (1 - \sigma(x))) $$其中 $\sigma(x)$ 为 Sigmoid 函数$y$ 为 SiLU 函数$y$ 为 SiLU 函数的导数gradOutput为反向传播中上游传来的梯度gradInput为算子的最终输出。从仓库的 Kernel 实现experimental/activation/silu_grad/op_kernel/silu_grad.h可以看到该公式在 Vector 单元上被拆解为如下指令序列float 路径// SiLU 梯度公式: dx dy * sigmoid(x) * (1 x * (1 - sigmoid(x))) Muls(tmp1, xLocal, -1.0f, processDataNum); // tmp1 -x Exp(tmp1, tmp1, processDataNum); // tmp1 e^(-x) Adds(tmp1, tmp1, 1.0f, processDataNum); // tmp1 1 e^(-x) Duplicate(dxLocal, 1.0f, processDataNum); // dx 1 Div(tmp2, dxLocal, tmp1, processDataNum); // tmp2 sigmoid(x) Sub(tmp1, dxLocal, tmp2, processDataNum); // tmp1 1 - sigmoid(x) Mul(tmp1, xLocal, tmp1, processDataNum); // tmp1 x * (1 - sigmoid(x)) Adds(tmp1, tmp1, 1.0f, processDataNum); // tmp1 1 x * (1 - sigmoid(x)) Mul(tmp1, tmp2, tmp1, processDataNum); // tmp1 sigmoid(x) * (1 x * (1 - sigmoid(x))) Mul(dxLocal, dyLocal, tmp1, processDataNum); // dx dy * tmp1对于 FLOAT16 / BF16 输入Kernel 会先将数据Cast到 float32 计算最后再以CAST_RINT舍入模式转回原类型输出以保证中间过程精度见 silu_grad.h。两段式接口与函数原型与 CANN 其他 aclnn 算子一致aclnnSiluBackward 采用两段式接口先调用aclnnSiluBackwardGetWorkspaceSize完成入参校验、构图与算子执行器创建并返回计算所需的 workspace 大小再调用aclnnSiluBackward传入 workspace 与执行器在指定 Stream 上异步执行计算。第一段接口原型aclnnStatus aclnnSiluBackwardGetWorkspaceSize( const aclTensor* gradOutput, const aclTensor* self, aclTensor* gradInput, uint64_t* workspaceSize, aclOpExecutor** executor)第二段接口原型aclnnStatus aclnnSiluBackward( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)从 Host 侧实现experimental/activation/silu_grad/op_host/op_api/aclnn_silu_backward.cpp可以还原第一段接口的完整内部流程对workspaceSize与executor做公共入参检查创建 OpExecutor依次执行空指针、数据类型、shape 三组参数校验CheckParams若gradOutput或self为空 Tensor直接返回workspaceSize 0并快速退出通过l0op::Contiguous将非连续输入转换为连续张量调用底层l0op::SiluGrad构建 SiluGrad 算子计算流程通过l0op::ViewCopy将计算结果回拷到输出gradInput兼容其非连续场景通过executor-GetWorkspaceSize()获取 workspace 大小并转移执行器。第二段接口则统一调用CommonOpExecutorRun完成实际计算aclnn_silu_backward.cpp。aclnnSiluBackwardGetWorkspaceSize 参数说明第一段接口参数如下参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorgradOutput输入表示 SiLU 激活函数正向输出的梯度公式中的 gradOutput支持空 TensorgradOutput、self 与 gradInput 的 shape 一致三者数据类型一致BFLOAT16、FLOAT16、FLOATND0-8√self输入表示用于计算激活函数的张量公式中的 x支持空 TensorgradOutput、self 与 gradInput 的 shape 一致三者数据类型一致BFLOAT16、FLOAT16、FLOATND0-8√gradInput输出backward 计算的输出为 SiLU 正向输入的梯度值即对输入求导后的结果公式中的 gradInput支持空 TensorgradOutput、self 与 gradInput 的 shape 一致三者数据类型一致BFLOAT16、FLOAT16、FLOATND0-8√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----上述约束与源码中CheckDtypeValid、CheckShapeValid的逻辑一一对应aclnn_silu_backward.cpp支持的数据类型列表在源码中定义为DTYPE_SUPPORT_LIST {DT_FLOAT16, DT_FLOAT, DT_BF16}在支持 broadcast 的平台IsRegbase为真上允许gradOutput与self做广播后与gradInput对齐在不支持广播的场景下严格要求三者 shape 一致混合精度场景gradOutput 与 self 类型不同下要求输出 gradInput 为 FLOATBF16 仅在 DAV_2201Atlas A2 系列等架构上受支持源码通过CheckSocVersionIsSupportBf16做了平台级校验。返回值与错误码返回aclnnStatus状态码具体参见 aclnn 返回码。第一段接口会完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 gradOutput、self 或 gradInput 是空指针ACLNN_ERR_PARAM_INVALID161002gradOutput、self 或 gradInput 的数据类型不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002gradOutput、self 和 gradInput 的数据类型不同ACLNN_ERR_PARAM_INVALID161002gradOutput、self 和 gradInput 的 shape 不同aclnnSiluBackward 参数说明第二段接口参数如下参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnSiluBackwardGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream返回值为aclnnStatus状态码具体参见 aclnn 返回码。约束说明无额外约束。实际限制均已通过第一段接口的参数校验与平台能力校验体现数据类型、shape 对齐、维度上限 8、BF16 平台支持等。调用示例下面为完整调用示例具体编译和执行过程请参考编译与运行样例。仓库中还提供了可直接参考的工程化样例 test_aclnn_silu_grad.cpp 与对应单元测试 tests/ut/op_api/test_aclnn_silu_grad.cpp。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_silu_backward.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法资源初始化 auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. 固定写法device/stream初始化参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t gradOutputShape {2, 3}; std::vectorint64_t selfShape {2, 3}; std::vectorint64_t gradInputShape {2, 3}; void* gradOutputDeviceAddr nullptr; void* selfDeviceAddr nullptr; void* gradInputDeviceAddr nullptr; aclTensor* gradOutput nullptr; aclTensor* self nullptr; aclTensor* gradInput nullptr; std::vectorfloat gradOutputHostData {1, 1, 1, 1, 1, 1}; std::vectorfloat selfHostData {1, 2, 3, 4, 5, 6}; std::vectorfloat gradInputHostData {0, 0, 0, 0, 0, 0}; // 创建gradOutput aclTensor ret CreateAclTensor(gradOutputHostData, gradOutputShape, gradOutputDeviceAddr, aclDataType::ACL_FLOAT, gradOutput); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建gradInput aclTensor ret CreateAclTensor(gradInputHostData, gradInputShape, gradInputDeviceAddr, aclDataType::ACL_FLOAT, gradInput); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API需要修改为具体的API名称 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnSiluBackward第一段接口 ret aclnnSiluBackwardGetWorkspaceSize(gradOutput, self, gradInput, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnSiluBackwardGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } // 调用aclnnSiluBackward第二段接口 ret aclnnSiluBackward(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnSiluBackward failed. ERROR: %d\n, ret); return ret); // 4. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧需要根据具体API的接口定义修改 auto size GetShapeSize(gradInputShape); std::vectorfloat outData(size, 0); ret aclrtMemcpy(outData.data(), outData.size() * sizeof(outData[0]), gradInputDeviceAddr, size * sizeof(outData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i size; i) { LOG_PRINT(out result[%ld] is: %f\n, i, outData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(gradOutput); aclDestroyTensor(self); aclDestroyTensor(gradInput); // 7. 释放device资源需要根据具体API的接口定义修改 aclrtFree(gradOutputDeviceAddr); aclrtFree(selfDeviceAddr); aclrtFree(gradInputDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }以示例数据推算当self {1, 2, 3, 4, 5, 6}、gradOutput全为 1 时由公式dx dy · σ(x) · (1 x·(1 - σ(x)))可得到各位置对应的梯度输出可作为验证结果正确性的参考。底层实现纵深从 aclnn API 到 AI Core Kernel1. 算子注册与 Shape 推导SiluGrad 算子的 IR 定义位于 experimental/activation/silu_grad/op_host/silu_grad_def.cpp输入dy、x与输出dx均为必选参数支持 FLOAT16 / FLOAT / BF16 三种数据类型数据格式为 NDAI Core 计算配置为ascend910bAtlas A2 系列对应的芯片架构。Shape 推导位于 experimental/activation/silu_grad/op_host/silu_grad_infershape.cpp输出dx的 shape 直接继承输入dy的 shape输出数据类型继承输入数据类型保证反向传播中张量形状在图上无缝传递。2. Tiling 策略单缓冲与双缓冲Host 侧 Tiling 逻辑位于 experimental/activation/silu_grad/op_host/silu_grad_tiling.cpp其核心决策流程为获取平台信息UB 内存大小与可用核数GetPlatformInfo依据输入 shape 与数据类型FLOAT 为 4 字节、其余为 2 字节计算输入总字节数与 UB 块大小以“每核约 1024 个元素”为目标计算实际并行核数finalCoreNum并对平台核数、数据块上限做三向取小避免过度并行通过CalculateCoreBlockNums计算每个核处理的数据量区分smallCoreDataNum、bigCoreDataNum与tailBlockNum实现核间负载均衡当单核 tile 数不超过 2 时选择单缓冲模式schMode 0增大 tile 消除尾部开销否则选择双缓冲流水线模式schMode 1并通过 TilingKey 下发。对应的 Tiling 数据结构定义于 silu_grad_tiling_data.h包含smallCoreDataNum、bigCoreDataNum、tileDataNum、tailBlockNum四个字段调度模式常量定义于 silu_grad_tiling_key.h。3. AI Core Kernel 执行Kernel 主体位于 experimental/activation/silu_grad/op_kernel/silu_grad.cpp 与 silu_grad.h每个 AI Core 根据coreId与tailBlockNum计算自身负责的全局数据偏移分别通过bigCoreDataNum或smallCoreDataNum定位dy、x、dx三段 Global Memory 缓冲CopyIn使用DataCopyPad将 dy 与 x 拷入 UBCopyOut将结果写回单缓冲模式ProcessSingleBuffer对单个 tile 串行执行 CopyIn → Compute → CopyOut双缓冲模式ProcessDoubleBuffer通过预加载首 tile、主循环重叠“下一 tile 的 CopyIn 与当前 tile 的 Compute/CopyOut”实现 DMA 与 Vector 单元并行末段排空流水线计算统一以 float32 进行FP16/BF16 输入输出通过Cast完成精度保底转换。4. L0 层 API 封装Host 侧 L0 封装位于 experimental/activation/silu_grad/op_host/op_api/silu_grad.cppl0op::SiluGrad先对两个输入做 broadcast shape 推导混合精度下输出类型取 FLOAT随后调用SiluGradAiCore通过ADD_TO_LAUNCHER_LIST_AICORE将算子加入 AI Core 启动列表最终返回输出张量供第一段接口构图与 ViewCopy 使用。总结aclnnSiluBackward 是 CANN ops-nn 仓库中 SiLUSwish激活函数反向传播的标准化 aclnn 接口覆盖 Atlas A2 训练系列与 Atlas 800I A2 推理产品支持 FLOAT16 / FLOAT / BF16 三种数据类型、ND 格式、0-8 维 shape 以及非连续 Tensor。通过两段式接口调用配合源码级的参数校验、broadcast 推断、Tiling 调度单/双缓冲与 float32 中间精度计算可安全高效地完成神经网络训练中 SiLU 层的梯度回传。算子整体实现、测试与调用样例可在 experimental/activation/silu_grad 目录下完整查阅。赞分享人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载相关推荐CANN ops-nn aclnnSiluBackward 接口详解Silu 反向传播算子原理、两段式调用与 NPU 源码实现CANN ops nn aclnnSiluBackward 接口详解Silu 反向传播算子原理、两段式调用与 NPU 源码实现 本篇技术指南围绕 CANN 神人工智能算子库深度学习CANNAscendCANN ops-nn 算子解析aclnnSeluBackward 两段式接口实现 SELU 反向梯度计算CANN ops nn 算子解析aclnnSeluBackward 两段式接口实现 SELU 反向梯度计算 aclnnSeluBackward 是 CANN人工智能算子库深度学习CANNAscendCANN ops-nn 算子详解aclnnSoftmaxGrad 两段式接口实现 Softmax 反向梯度计算CANN ops nn 算子详解aclnnSoftmaxGrad 两段式接口实现 Softmax 反向梯度计算 Softmax 反向传播是神经网络训练中最常见人工智能算子库深度学习CANNAscend上一篇Seq2Seq模型未知词替换策略基于注意力机制的智能解决方案终极指南 下一篇Ollama 输出对不上3 步锁定可复现实验环境创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考