ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CANN ops-nn SiluGrad(aclnnSiluBackward)算子深度解析:原理、接口调用与源码实现

CANN ops-nn SiluGrad(aclnnSiluBackward)算子深度解析:原理、接口调用与源码实现 CANN ops-nn SiluGradaclnnSiluBackward算子深度解析原理、接口调用与源码实现【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nnSiluGrad 是 CANN ops-nn 算子库中 SiLUSigmoid Linear Unit又称 Swish激活函数反向传播算子的统称对外以aclnnSiluBackward两段式接口暴露用于根据反向传播输入梯度gradOutput与正向输入self计算输入侧梯度gradInput。本文以 activation/silu_grad/README.md 为骨架结合该算子完整的 op_api、op_host、op_kernel 源码与测试用例系统讲解其数学原理、产品支持范围、参数约束、两段式调用方法以及底层实现机制读者读完即可独立完成aclnnSiluBackward的工程接入与结果验证。一、算子功能与数学原理1.1 功能定位SiluGrad是正向 aclnnSilu 接口即 SiLU/Swish 激活函数的反向传播实现根据反向传播传入的梯度gradOutput与正向计算时的输入self计算得到对输入x的梯度gradInput。它是深度神经网络中 SiLU 激活层自动求导链路的关键一环常见于 SwiGLU 门控结构、Transformer 前馈网络等场景。从源码注释看算子原型定义中明确标注Compatible with the Torch operator SiluGrad即与 PyTorch 的SiluGrad算子对齐见 op_graph/silu_grad_proto.h。1.2 计算公式SiluGrad 的计算建立在 SiLU 激活函数及其导数之上核心公式如下$$ \sigma(x) {\frac{1} {1{e}^{-x}}} $$$$ s(x) x\sigma(x) $$$$ s^\prime(x) \sigma(x)(1x-x\sigma(x)) $$$$ gradInput gradOutput * s^\prime(x) $$其中 $\sigma(x)$ 为 sigmoid 函数$s(x)$ 为 silu 函数$s^\prime(x)$ 为 silu 函数的导数。需要特别注意的是本算子不依赖正向输出而是直接以正向输入self即公式中的 $x$参与导数计算。这意味着调用方只需保留激活层的输入即可完成反向传播无需额外缓存正向输出张量这是与根据正向输出反推梯度类算子例如部分 SwishGrad 变体的关键区别。这一设计在 docs/aclnnSiluBackward.md 的功能说明中同样有明确描述根据silu反向传播梯度与正向输出计算silu的梯度输入且参数表中self的语义为公式中的x且对应正向的输入参数。二、产品支持情况SiluGrad 算子在不同硬件产品上的支持情况如下表源自 activation/silu_grad/README.md产品是否支持Ascend 950PR/Ascend 950DT√Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品√从算子注册配置可以印证这一支持矩阵op_host/silu_grad_def.cpp 中仅针对ascend950与ascend350两个架构添加了 AICore 配置AICore().AddConfig(ascend950, ...)与AICore().AddConfig(ascend350, ...)而ascend350对应 Atlas A2/A3 系列产品ascend950对应 Ascend 950 系列与上表中支持的产品一一对应未注册的架构如 Atlas 200I/500 A2、Atlas 推理系列自然不支持该算子。此外op_host/config/ascend350/silu_grad_binary.json 与 op_host/config/ascend950/silu_grad_binary.json 两份二进制算子配置文件进一步佐证了算子在两个架构上的落地形态。三、参数说明3.1 输入输出参数SiluGrad 共三个张量参数两个输入、一个输出全部为 ND 格式支持 1~8 维参数名输入/输出/属性描述数据类型数据格式gradOutput输入表示输入梯度。公式中的 gradOutput。BFLOAT16、FLOAT16、FLOATNDself输入表示输入数据。公式中的 x且对应正向的输入参数。BFLOAT16、FLOAT16、FLOATNDgradInput输出表示对输入数据 self 求的梯度。公式中的 gradInput。BFLOAT16、FLOAT16、FLOATND各参数在 docs/aclnnSiluBackward.md 中有更细粒度的约束gradOutput输入支持空 TensorgradOutput、self 与 gradInput 的数据类型和 shape 一致三者的 shape 满足 broadcast 关系维度shape为 1~8支持非连续 Tensor。self输入支持空 Tensor约束与 gradOutput 相同且对应正向算子的输入参数。gradInput输出gradOutput、self 与 gradInput 的数据类型和 shape 一致三者的 shape 满足 broadcast 关系维度shape为 1~8支持非连续 Tensor。3.2 平台相关的数据类型差异Atlas 训练系列产品数据类型仅支持 FLOAT16、FLOAT不支持 BFLOAT16见 README.md 与 docs/aclnnSiluBackward.md 中的平台标注。3.3 约束说明README 中标注该算子约束说明无即对调用方没有额外的格式、对齐或特殊 shape 限制只需满足上述参数数据类型与 shape 约束即可。确定性计算方面aclnnSiluBackward默认采用确定性实现见 docs/aclnnSiluBackward.md 约束说明小节。四、两段式接口aclnnSiluBackward 调用说明与其他 CANN aclnn 算子一致aclnnSiluBackward采用两段式接口设计必须先调用aclnnSiluBackwardGetWorkspaceSize获取计算所需 workspace 大小以及包含算子计算流程的执行器再调用aclnnSiluBackward执行计算。4.1 第一段接口aclnnSiluBackwardGetWorkspaceSizeaclnnStatus aclnnSiluBackwardGetWorkspaceSize( const aclTensor* gradOutput, const aclTensor* self, aclTensor* gradInput, uint64_t* workspaceSize, aclOpExecutor** executor)参数明细参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorgradOutputaclTensor*输入表示输入梯度。公式中的 gradOutput。支持空 TensorgradOutput、self 与 gradInput 的数据类型和 shape 一致三者的 shape 满足 broadcast 关系。BFLOAT16、FLOAT16、FLOATND1-8√selfaclTensor*输入表示输入数据。公式中的 x且对应正向的输入参数。支持空 TensorgradOutput、self 与 gradInput 的数据类型和 shape 一致三者的 shape 满足 broadcast 关系。BFLOAT16、FLOAT16、FLOATND1-8√gradInputaclTensor*输出表示对输入数据 self 求的梯度。公式中的 gradInput。gradOutput、self 与 gradInput 的数据类型和 shape 一致三者的 shape 满足 broadcast 关系。BFLOAT16、FLOAT16、FLOATND1-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小。-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程。-----注Atlas 训练系列产品上数据类型仅支持 FLOAT16、FLOAT。返回值与错误码返回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 不同或不满足 broadcast 关系。这些错误码的校验逻辑可以在 op_api/aclnn_silu_backward.cpp 的CheckParams中逐条对应先检查空指针CheckNotNull返回ACLNN_ERR_PARAM_NULLPTR再检查数据类型是否在DTYPE_SUPPORT_LISTFLOAT16/FLOAT/BF16内且三者 dtype 是否一致混合精度场景校验输出是否为 FLOAT最后检查 shape 是否一致或满足 broadcast 关系CheckShapeValid均返回ACLNN_ERR_PARAM_INVALID。4.2 第二段接口aclnnSiluBackwardaclnnStatus aclnnSiluBackward( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)参数明细参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnSiluBackwardGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。返回值仍为aclnnStatus状态码参见 aclnn返回码。4.3 源码视角两段接口内部做了什么从 op_api/aclnn_silu_backward.cpp 可以还原两段接口的完整行为第一段GetWorkspaceSize依次执行创建OpExecutor→CheckParams参数校验 → 空 Tensor 快速路径gradOutput或self为空时直接返回 workspaceSize0见 L165-L169→ 通过l0op::Contiguous将两个输入转换为连续 Tensor → 调用l0op::SiluGrad构建算子计算节点 → 通过l0op::ViewCopy将中间结果拷贝到可能非连续的输出gradInput上 → 汇总并返回 workspace 大小与执行器。第二段aclnnSiluBackward则是调用CommonOpExecutorRun(workspace, workspaceSize, executor, stream)完成实际计算见 L194-L199。l0op::SiluGrad定义于 op_api/silu_grad.cpp先对两个输入做 broadcast 形状推导再决定输出数据类型当gradOutput与self的 dtype 不同时输出gradInput提升为DT_FLOAT混合精度中间计算否则与输入保持一致随后调用SiluGradAiCore通过ADD_TO_LAUNCHER_LIST_AICORE(SiluGrad, ...)下发到 AICore 执行。五、完整调用示例仓库在 examples/test_aclnn_silu_grad.cpp 提供了可直接编译运行的完整样例docs/aclnnSiluBackward.md中也给出了同款示例代码。以下为完整代码以 2×3 的 FLOAT 张量为例输入梯度全 1、输入数据 1~6#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* gradOutDeviceAddr nullptr; void* selfDeviceAddr nullptr; void* gradInputDeviceAddr nullptr; aclTensor* gradOut nullptr; aclTensor* self nullptr; aclTensor* gradInput nullptr; std::vectorfloat gradOutHostData {1, 1, 1, 1, 1, 1}; std::vectorfloat selfHostData {1, 2, 3, 4, 5, 6}; std::vectorfloat gradInputHostData {0, 0, 0, 0, 0, 0}; // 创建gradOut aclTensor ret CreateAclTensor(gradOutHostData, gradOutputShape, gradOutDeviceAddr, aclDataType::ACL_FLOAT, gradOut); 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名称 int64_t dim 0; uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnSiluBackward第一段接口 ret aclnnSiluBackwardGetWorkspaceSize(gradOut, 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(gradOut); aclDestroyTensor(self); aclDestroyTensor(gradInput); // 7. 释放device资源需要根据具体API的接口定义修改 aclrtFree(gradOutDeviceAddr); aclrtFree(selfDeviceAddr); aclrtFree(gradInputDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例代码的编译与执行过程请参考 编译与运行样例。对上述样例做一次手工验算可以直观验证公式当gradOutput全为 1、self {1,2,3,4,5,6}时每个位置的输出即为 $s^\prime(x)\sigma(x)(1x-x\sigma(x))$例如 $x1$ 时 $s^\prime(1)\approx0.9093\times(11-1\times0.9093)\approx0.9917$。5.1 关于非连续 Tensor 与空 Tensor非连续 Tensor接口层会通过l0op::Contiguous将非连续输入归一化为连续 Tensor 后参与计算并通过l0op::ViewCopy把结果写回非连续的输出张量因此调用方无需自己预处理非连续张量docs/aclnnSiluBackward.md 参数表中非连续Tensor一列为 √。空 Tensor当gradOutput或self为空 Tensor 时第一段接口直接返回workspaceSize 0与一个可用的执行器第二段接口空转完成不触发实际计算调用方可按正常流程继续无需额外分支处理。六、源码级实现解析从图协议到 AICore Kernel6.1 图协议层op_graphop_graph/silu_grad_proto.h 使用REG_OP宏定义算子原型输入dy即 gradOutput、输入x即 self、输出dx即 gradInput三者均支持DT_FLOAT16、DT_FLOAT、DT_BF16格式为 ND默认支持 1~8 维并标注与 Torch 的SiluGrad算子兼容。6.2 Host 侧算子定义与 Infershapeop_hostop_host/silu_grad_def.cpp 通过OpDef注册算子输入dy/x与输出dx均要求必填并声明了 9 种数据类型组合含同 dtype 三组与混合精度六组AICore 配置开启动态编译、动态 rank、动态 shape 支持且PrecisionReduceFlag(true)表明允许精度降低优化。op_host/silu_grad_infershape.cpp 定义 shape/dtype 推导规则InferShape复用 elewise 通用推导InferShape4ElewiseInferDataTypeForSiluGrad则实现两输入 dtype 相同则输出同型否则输出提升为 FLOAT的规则与 op_api 层silu_grad.cpp中的 dtype 决策逻辑完全一致。6.3 Tiling 与算子分档op_host/arch35op_host/arch35/silu_grad_tiling.cpp 完成 tiling 计算是算子性能的关键GetOpKey依据dy、x、dx三者的 dtype 组合返回 9 档 opKeyOP_KEY_1 ~ OP_KEY_9例如FLOAT16×FLOAT16→FLOAT16为 OP_KEY_1、FLOAT16×BF16→FLOAT为 OP_KEY_4、FLOAT×BF16→FLOAT为 OP_KEY_9 等GetComputeMap为每档组合配置 broadcast 计算的位宽与 buffer 参数maxDtypeBits/minDtypeBits/bufferDivisor等DoOpTiling收集输入/输出 storage shape 与 strides调用通用BroadcastTiling生成 block 切分、UB 切分与尾部处理参数并最终生成 tilingKey、blockDim 与 workspace固定 32 字节。Tiling 过程会读取平台信息AIV 核数、UB 大小支持从SiluGradCompileInfo获取编译期信息兼顾动态 shape 场景。6.4 Kernel 实现与 tilingKey 分发op_kernelop_kernel/silu_grad_apt.cpp 是 AICore Kernel 的入口函数silu_grad其核心特征是按 tilingKey 静态分发到 18 个模板特化实现见 L37-L71同 dtype 三组SiluGradF16、SiluGradBf16、SiluGradF32各分nddma_with_loops与nddma_without_loops两个变体混合精度六组SiluGradDtypeComb0~SiluGradDtypeComb5同样各分 loops / without_loops 两个变体。with_loops / without_loops 对应两种内存搬运策略当张量维数较大UB 内最大维数达 8且需要 NDDMA 多轮搬运时走 loops 版本当维数较小时UB 内最大维数为 5走无循环的展开版本后者通过减少循环开销提升小 shape 场景的吞吐。kernel 入口还做了 AIC 核守卫g_coreType AscendC::AIC直接返回与KERNEL_TYPE_AIV_ONLY任务类型声明表明该算子仅在 AIV 核上执行。具体的逐元素导数计算模板实现位于 op_kernel/arch35/ 目录下的 18 个头文件中。6.5 二进制算子配置op_host/configop_host/config/ascend950/silu_grad_binary.json 与 op_host/config/ascend350/silu_grad_binary.json 记录了算子二进制分档bin_filename与输入输出规格所有分档的 shape 均为[-2]表示动态 shapeformat 为 NDparamType 为 requireddtype 组合包括bf16×bf16→bf16、fp16×fp16→fp16、fp32×fp32→fp32以及六种混合精度组合输出一律为 fp32与 tiling 层的 opKey 分档一一对应。七、测试与验证仓库为 SiluGrad 提供了覆盖各层的测试用例可用于验证算子正确性op_api 单测tests/ut/op_api/test_aclnn_silu_backward.cpp直接调用aclnnSiluBackward两段接口并比对结果op_host 单测tests/ut/op_host/arch35/test_silu_grad_tiling.cpp 验证 tiling 数据生成tests/ut/op_host/test_silu_grad_infershape.cpp 验证 shape/dtype 推导op_kernel 单测tests/ut/op_kernel/test_silu_grad_apt.cpp 验证 kernel 计算正确性系统测试STtests/st/aclnnSiluBackward/ 目录下提供atk_aclnnSiluBackward.json与executor_aclnnSiluBackward.py以及 arch35 下的 CSV 用例清单ttk_aclnn_silu_backward_st.csv、ttk_kernel_silu_grad_st.csv可配合 ATK/TTK 工具做端到端验收golden 数据生成tests/assets/golden.py 用于生成期望输出可作为手工验算公式 $gradInput gradOutput \cdot \sigma(x)(1x-x\sigma(x))$ 的参考实现。八、常见问题速查现象/诉求处理方式报错 161001ACLNN_ERR_PARAM_NULLPTR检查 gradOutput、self、gradInput 三个 aclTensor 是否都已正确创建未空指针传入。报错 161002ACLNN_ERR_PARAM_INVALID依次排查dtype 是否在 FLOAT16/FLOAT/BF16 范围内三者 dtype 是否一致混合精度下输出必须为 FLOATshape 是否一致或满足 broadcast 关系。Atlas 训练系列产品上使用 BF16不支持。Atlas 训练系列产品仅支持 FLOAT16、FLOAT需先做类型转换。输入为高维8 维张量接口层通过MAX_SUPPORT_DIMS_NUMS8 维上限校验拒绝高维张量建议先 reshape 到 ≤8 维再调用。非连续张量 / 空张量无需预处理接口内部自动处理Contiguous 归一化、空 Tensor 短路。需要确定性的梯度结果aclnnSiluBackward默认即确定性实现无需额外开关。总结SiluGrad 算子以aclnnSiluBackward两段式接口承载 SiLU 激活函数的反向传播计算数学上即gradInput gradOutput * σ(x)(1x-xσ(x))覆盖 FLOAT16/FLOAT/BF16 三种数据类型与九种 dtype 组合支持 1~8 维 ND 张量、broadcast、非连续与空 Tensor。从图协议op_graph、算子定义与推导op_host、tiling 分档op_host/arch35到按 tilingKey 静态分发的 AICore Kernelop_kernel整个实现链路完整可追溯并配有从单测到端到端 ST 的验证体系。开发者可直接参考 examples/test_aclnn_silu_grad.cpp 完成接入并按 编译与运行样例 的指引构建运行。【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表