尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CANN ops-nn 算子开发指南:aclnnHardsigmoid 与 aclnnInplaceHardsigmoid 两段式接口详解

CANN ops-nn 算子开发指南:aclnnHardsigmoid 与 aclnnInplaceHardsigmoid 两段式接口详解 CANN ops-nn 算子开发指南aclnnHardsigmoid 与 aclnnInplaceHardsigmoid 两段式接口详解【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nnHardSigmoid 是神经网络中常用的分段线性激活函数用于把输入张量逐元素映射到 [0, 1] 区间同时保留线性过渡带。本文以 CANN ops-nn 仓库中 HardSigmoid 算子文档 为骨架结合该算子在仓库内的源码实现op_api、op_host、op_kernel、tests系统讲解aclnnHardsigmoid与aclnnInplaceHardsigmoid两个 aclnn 接口的产品支持情况、计算公式、函数原型、参数约束、返回码、调用示例以及两段式接口背后的执行流程与 kernel 实现原理帮助你直接在昇腾 NPU 上完成 HardSigmoid 算子的接入与调优。一、接口概览与使用场景aclnnHardsigmoid与aclnnInplaceHardsigmoid是 CANN 算子库ops-nn为 HardSigmoid 激活函数提供的两个 aclnn 接口二者实现相同的数学功能区别仅在于计算结果的落点aclnnHardsigmoid需要调用方新建一个输出张量对象out算子将计算结果写入 out 指向的 device 内存原始输入 self 保持不变。aclnnInplaceHardsigmoid无需新建输出张量算子直接在输入 self 的 device 内存上原地改写结果覆盖原输入。两者的选择依据是业务场景如果后续计算不再需要原始输入推荐使用 inplace 版本以省去一份输出张量的内存申请如果原始输入还需复用则使用非 inplace 版本。从源码实现看inplace 版本本质上是把 out 直接指向 self在 op_api/aclnn_hardsigmoid.cpp 中aclnnInplaceHardsigmoidGetWorkspaceSize内部通过auto out const_castaclTensor*(self);复用 self 作为输出再委托给aclnnHardsigmoidGetWorkspaceSize完成工作区计算因此两个接口的计算流程完全一致。二、产品支持情况根据算子文档HardSigmoid 的 aclnn 接口在以下昇腾产品上的支持情况如下产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品支持数据类型仅 FLOAT、FLOAT16、INT32不支持 BFLOAT16仓库中 HardSigmoid 算子 README 给出的支持面与此一致并补充了 Ascend 950、Atlas 200I/500 A2 等产品同样支持该算子。需要留意的是Atlas 训练系列产品910在 aclnn 接口层面不支持 BFLOAT16这一差异在 op_api/aclnn_hardsigmoid.cpp 中也有明确体现源码分别定义了ASCEND910_DTYPE_SUPPORT_LISTFLOAT、FLOAT16、INT32与ASCEND910B_DTYPE_SUPPORT_LISTFLOAT、FLOAT16、INT32、BF16两张数据类型支持表运行时根据具体芯片平台选择对应支持列表。三、功能说明与计算公式HardSigmoid 属于激活函数变种对输入张量 self 逐元素计算输出张量与输入形状相同。核心计算公式为$$ Hardsigmoid(self)clip(\alpha \times self \beta, 0, 1) $$默认 $\alpha\frac{1}{6}$$\beta\frac{1}{2}$等价于如下分段函数$$ Hardsigmoid(self)\begin{cases} 1, if(self\gt3) \ 0, if(self\le-3) \ \frac{self}{6} \frac{1}{2}, otherwise \end{cases} $$即大于 3 的元素输出恒为 1小于等于 -3 的元素输出恒为 0中间的输入做一次仿射变换后截断到 [0,1]。当输入为 INT32 时中间计算结果会被截断truncate后再转换为 INT32 输出因此 INT32 下结果只有 0 和 1 两个可能取值x/6 1/2在整数域内落在 (0,1) 之外时被截断。这一行为在测试基准 tests/assets/golden.py 中通过torch.trunc复现与 kernel 的实现保持一致。图模式下的算子原型 op_graph/hard_sigmoid_proto.h 同样声明了可选的alpha默认 0.16666666与beta默认 0.5两个属性与 aclnn 接口的默认参数完全对应该原型在注释中明确指出与 PyTorch 的torch.nn.Hardsigmoid兼容。四、两段式接口架构与函数原型与 CANN 其他 aclnn 算子一致HardSigmoid 采用两段式接口详见仓库文档 两段式接口说明先调用第一段GetWorkspaceSize接口完成入参校验、计算流程构建并返回所需 workspace 大小与执行器再调用第二段执行接口真正在 NPU 上启动计算。完整流程为aclnnHardsigmoidGetWorkspaceSize(self, out, workspaceSize, executor) ↓ 根据 workspaceSize 在 Device 侧 aclrtMalloc 申请工作区 aclnnHardsigmoid(workspace, workspaceSize, executor, stream)4.1 aclnnHardsigmoid 系列原型aclnnStatus aclnnHardsigmoidGetWorkspaceSize( const aclTensor* self, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnHardsigmoid( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)4.2 aclnnInplaceHardsigmoid 系列原型aclnnStatus aclnnInplaceHardsigmoidGetWorkspaceSize( const aclTensor* self, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnInplaceHardsigmoid( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)从 op_api/aclnn_hardsigmoid.h 的头文件声明可以看到四个接口均以ACLNN_API导出domain标注为aclnn_ops_infer头文件中还以 mermaid 图形式给出了 API 的基本计算路径self → L0::Contiguous → L0::Hardsigmoid → L0::ViewCopy → out即先把非连续输入规整为连续张量执行 HardSigmoid 计算再把结果拷回可能非连续的输出张量。五、GetWorkspaceSize 参数详解与校验逻辑5.1 aclnnHardsigmoidGetWorkspaceSize 参数表参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入激活函数输入公式中的 selfself 与 out 的 shape 一致支持空 TensorFLOAT、FLOAT16、INT32、BFLOAT16ND0-8√outaclTensor*输出激活函数输出公式中的 Hardsigmoid(self)self 与 out 的数据类型和 shape 一致FLOAT、FLOAT16、INT32、BFLOAT16ND0-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----注Atlas 训练系列产品上仅支持 FLOAT、FLOAT16、INT32 三种数据类型不支持 BFLOAT16。5.2 入参校验与返回码第一段接口会完成入参校验校验逻辑在 op_api/aclnn_hardsigmoid.cpp 的CheckParams中实现出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self 或 out 为空指针ACLNN_ERR_PARAM_INVALID161002self 或 out 的数据类型和数据格式不在支持范围之内ACLNN_ERR_PARAM_INVALID161002self 的数据类型与输出 out 的数据类型不一致ACLNN_ERR_PARAM_INVALID161002self 的 shape 与输出 out 的 shape 不一致ACLNN_ERR_PARAM_INVALID161002self 或 out 的 shape 超过 8 维源码中的校验链路与文档一一对应CheckNotNull2Tensor负责空指针检查对应 161001CheckDtypeValid按芯片平台选择ASCEND910_DTYPE_SUPPORT_LIST或ASCEND910B_DTYPE_SUPPORT_LIST校验数据类型对应 161002 第一行CheckShape通过OP_CHECK_MAX_DIM限制最大 8 维并通过OP_CHECK_SHAPE_NOT_EQUAL校验 self/out shape 一致对应 161002 后两行随后再逐项比对 self 与 out 的数据类型、存储格式是否一致。5.3 aclnnInplaceHardsigmoidGetWorkspaceSize 参数表参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入公式中的输入 self 和输出 Hardsigmoid(self)原地读写支持空 TensorFLOAT、FLOAT16、INT32、BFLOAT16ND0-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----注Atlas 训练系列产品上仅支持 FLOAT、FLOAT16、INT32。inplace 版本的校验错误场景返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self 为空指针ACLNN_ERR_PARAM_INVALID161002self 的数据类型和数据格式不在支持范围之内ACLNN_ERR_PARAM_INVALID161002self 的 shape 超过 8 维由于 inplace 版本没有独立的 out 张量因此校验项比非 inplace 版本少了“类型/格式/shape 不一致”的检查从源码看它完全复用了aclnnHardsigmoidGetWorkspaceSize的校验流程self 同时充当 out。5.4 第二段执行接口参数aclnnHardsigmoid与aclnnInplaceHardsigmoid的四个参数含义相同参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段 GetWorkspaceSize 接口获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream第二段接口内部调用框架统一的CommonOpExecutorRun(workspace, workspaceSize, executor, stream)完成计算见 op_api/aclnn_hardsigmoid.cpp返回值同样是aclnnStatus具体返回码含义参见仓库文档 aclnn 返回码说明。六、空 Tensor 与确定性约束空 Tensor 支持当 self 为空张量时第一段接口直接返回workspaceSize 0并跳过实际计算无需申请工作区。这一逻辑在 op_api/aclnn_hardsigmoid.cpp 中以self-IsEmpty()分支实现。确定性计算aclnnHardsigmoid与aclnnInplaceHardsigmoid默认采用确定性实现即相同输入在多次执行中得到逐位一致的输出便于调试与结果复现。七、完整调用示例C以下示例代码摘自算子文档展示了从设备初始化、张量构造、两段式调用到结果回拷与资源释放的完整流程编译与运行方法参考仓库文档 编译与运行样例。7.1 aclnnHardsigmoid 调用示例#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_hardsigmoid.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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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根据自己的需要处理 CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {4, 2}; std::vectorint64_t outShape {4, 2}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {-4, -3, -2, 0, 1, 2, 4, 5}; std::vectorfloat outHostData {0, 0, 0, 0, 0, 0, 0, 0}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API // 调用aclnnHardsigmoid第一段接口 uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnHardsigmoidGetWorkspaceSize(self, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnHardsigmoidGetWorkspaceSize 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;); } // 调用aclnnHardsigmoid第二段接口 ret aclnnHardsigmoid(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnHardsigmoid 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(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), 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(result[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(out); // 7. 释放device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }以文档中的输入{-4, -3, -2, 0, 1, 2, 4, 5}为例代入公式可得输出依次为{0, 0, 1/6, 1/2, 2/3, 5/6, 1, 1}其中 -4 与 -3 落在截断区间输出 04 与 5 大于 3 输出 1可以据此在本地验证算子行为。7.2 aclnnInplaceHardsigmoid 调用示例inplace 版本与 7.1 的主要差异在于不需要创建 out 张量第一段接口只传 self第二段执行后结果直接写回 self 的 device 内存回拷时读取的也是selfDeviceAddr最终只释放 self 相关资源。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_hardsigmoid.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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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根据自己的需要处理 CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {4, 2}; void* selfDeviceAddr nullptr; aclTensor* self nullptr; std::vectorfloat selfHostData {-4, -3, -2, 0, 1, 2, 4, 5}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API // 调用aclnnInplaceHardsigmoid第一段接口 uint64_t inplaceWorkspaceSize 0; aclOpExecutor* inplaceExecutor; ret aclnnInplaceHardsigmoidGetWorkspaceSize(self, inplaceWorkspaceSize, inplaceExecutor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceHardsigmoidGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* inplaceWorkspaceAddr nullptr; if (inplaceWorkspaceSize 0) { ret aclrtMalloc(inplaceWorkspaceAddr, inplaceWorkspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret;); } // 调用aclnnInplaceHardsigmoid第二段接口 ret aclnnInplaceHardsigmoid(inplaceWorkspaceAddr, inplaceWorkspaceSize, inplaceExecutor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceHardsigmoid 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(selfShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr, size * sizeof(float), 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(result[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); // 7. 释放device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); if (inplaceWorkspaceSize 0) { aclrtFree(inplaceWorkspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }7.3 示例代码执行要点两段式接口缺一不可必须先调用 GetWorkspaceSize 拿到workspaceSize与executor再申请工作区内存、调用执行接口workspaceSize为 0 时无需申请工作区空 Tensor 场景即为 0。aclrtMalloc使用ACL_MEM_MALLOC_HUGE_FIRST申请大页内存aclrtMemcpy分别承担 host→device 的输入搬入与 device→host 的结果搬出。aclCreateTensor以 ND 格式创建张量并显式传入 strides支持构造非连续张量示例代码计算了连续张量的 strides若构造切片视图则需按实际 stride 填写。资源释放顺序与申请顺序相反先aclDestroyTensor再aclrtFree各 device 内存最后销毁 stream、复位 device 并aclFinalize。仓库 examples 目录 提供了可直接运行的对应样例test_aclnn_hard_sigmoid.cpp、test_aclnn_inplace_hard_sigmoid.cpp另有图模式构图样例arch35/test_geir_hard_sigmoid.cpp可供参考。八、源码级原理从 aclnn 到底层 Kernel8.1 Host 侧算子定义与 shape 推导在 op_host/hard_sigmoid_def.cpp 中注册了名为HardSigmoid的算子定义输入input_x与输出output_y均支持 FLOAT、FLOAT16、BF16、INT32 四种类型与 ND 格式并声明了可选的alpha默认 1/6与beta默认 0.5属性。算子配置开启动态 shape 支持DynamicShapeSupportFlag(true)与动态 rank 支持DynamicRankSupportFlag(true)同时通过DynamicCompileStaticFlag(true)支持静态编译为昇腾 950ascend950平台配置了 AICore 后端。shape/类型推导在 op_host/hard_sigmoid_infershape.cpp 中实现InferShape4HardSigmoid直接将输入 shape 拷贝为输出 shapeInferDataType4HardSigmoid将输入数据类型直接赋给输出印证了文档中“输出与输入 shape、数据类型一致”的约束。8.2 第一段接口的计算图构建aclnnHardsigmoidGetWorkspaceSize并非只做参数校验它同时完成了计算图的构建与 workspace 大小估算op_api/aclnn_hardsigmoid.cpp创建OpExecutorCREATE_EXECUTOR()用于承载后续 L0 算子节点执行CheckParams完成入参校验空 Tensor 直接返回workspaceSize0调用l0op::Contiguous(self)把可能非连续的 self 规整为连续张量调用l0op::HardSigmoid(selfContiguous)构造 HardSigmoid 计算节点调用l0op::ViewCopy(hardsigmoidOpOut, out)将计算结果写回用户提供的 out支持非连续 out从 executor 汇总得到workspaceSize并ReleaseTo释放给调用方。第二段接口则通过框架统一的CommonOpExecutorRun把这张图在指定 stream 上真正下发执行。这也解释了为什么“非连续 Tensor”被文档标注为支持非连续输入在内部被 Contiguous 归一化非连续输出由 ViewCopy 承接。8.3 AICore Kernel 计算实现kernel 侧实现在 op_kernel/arch35/hard_sigmoid.cpp 中采用 EnQue/DeQue 三级流水CopyIn / Compute / CopyOut架构跨管道同步由 Queue 自动管理并按数据类型分三条计算路径FLOAT原生 fp32 计算y Muls(x, alpha) → Adds(y, beta) → Mins(y, 1.0) → Maxs(y, 0.0)即 affine 变换后按上下界截断FLOAT16/BFLOAT16先 Cast 升到 fp32 计算 affine 变换再按目标类型舍入降回bf16 用CAST_ROUNDfp16 用CAST_RINT最后在原生 dtype 域做 clampINT32先用CAST_RINT升 fp32 计算再经 affine clamp最后用CAST_TRUNC截断转回 INT32——与文档“INT32 计算结果截断后转换”的描述一致。tiling 参数在 op_kernel/arch35/hard_sigmoid_tiling_data.h 中定义包括totalElements元素总数、blockFactor单核元素数、ubFactor单次 UB 搬运/计算元素数以及alpha、beta其中缓冲深度HARD_SIGMOID_BUFFER_NUM 2双缓冲由 Host/Kernel 共享头约束确保 tiling 推导的 UB 预算与 kernel 实际占用一致。8.4 测试与正确性验证仓库为 HardSigmoid 提供了完整的测试支撑单测tests/ut/op_host/op_api/test_aclnn_hardsigmoid.cpp 覆盖 aclnn 接口层的调用与返回码tests/ut/op_host/test_hard_sigmoid_infershape.cpp 覆盖 shape/类型推导tests/ut/op_kernel/test_hard_sigmoid.cpp 覆盖 kernel 计算。ST 用例tests/st/aclnnHardsigmoid/atk_aclnnHardsigmoid.json 与 executor_aclnnHardsigmoid.py 提供端到端测试入口。golden 基准tests/assets/golden.py 用 PyTorch 实现参考计算浮点类型用torch.where按 0/1 边界截断仿射结果INT32 额外torch.trunc后转回与 kernel 语义对齐其输入注入逻辑还专门构造了-3、3、±inf、nan、±tiny等边界值与特殊值用例如hard_sigmoid_fp32_special用于验证分段边界与特殊输入的处理。九、与其他调用方式的关系除了 aclnn 两段式接口HardSigmoid 算子还支持图模式调用图模式算子 IRop_graph/hard_sigmoid_proto.h 以REG_OP注册了HardSigmoid算子的输入input_x、输出output_y以及alpha、beta两个属性图模式下可通过算子 IR 直接构图op_graph/hard_sigmoid_graph_infer.cpp 提供了图模式的数据类型推导。调用示例见 examples/arch35/test_geir_hard_sigmoid.cpp。ONNX 前端适配framework/hard_sigmoid_onnx_plugin.cpp 提供 ONNX 框架的算子插件映射可将 ONNX 图中的 HardSigmoid 节点转换为本算子。无论哪种调用方式最终都落到 op_kernel/arch35/hard_sigmoid.cpp 中的 AICore kernel 执行保证计算结果的一致性。十、常见问题与使用建议报 161001ACLNN_ERR_PARAM_NULLPTR检查 self以及非 inplace 版本的 out是否为有效指针通常是在调用前未创建 aclTensor。报 161002ACLNN_ERR_PARAM_INVALID依次排查数据类型是否在支持列表内Atlas 训练系列不支持 BF16、self 与 out 的数据类型/格式/shape 是否一致、张量维度是否超过 8 维。是否需要申请 workspace以第一段接口返回的workspaceSize为准为 0 时无需申请空 Tensor 输入时返回 0属于正常情况。inplace 版本的内存语义执行后 self 的原数据被覆盖如果调用方仍持有对原输入的引用需自行保证后续不再使用旧值。INT32 输入的语义结果会先截断再转 INT32最终输出仅包含 0/1与浮点版本的连续输出行为不同精度对齐测试请参考 tests/assets/golden.py 中的binary_equal校验标准。参考文档与源码索引算子接口文档activation/hard_sigmoid/docs/aclnnHardsigmoidaclnnInplaceHardsigmoid.md算子 READMEactivation/hard_sigmoid/README.mdaclnn 接口声明与实现op_api/aclnn_hardsigmoid.h、op_api/aclnn_hardsigmoid.cppHost 算子定义与推导op_host/hard_sigmoid_def.cpp、op_host/hard_sigmoid_infershape.cppKernel 实现与 tiling 定义op_kernel/arch35/hard_sigmoid.cpp、op_kernel/arch35/hard_sigmoid_tiling_data.h图模式算子 IRop_graph/hard_sigmoid_proto.h调用样例examples/test_aclnn_hard_sigmoid.cpp、examples/test_aclnn_inplace_hard_sigmoid.cpp测试基准与单测tests/assets/golden.py、tests/ut/op_host/op_api/test_aclnn_hardsigmoid.cpp通用机制文档两段式接口说明、aclnn 返回码说明、编译与运行样例【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表