ARTICLE DETAIL

资讯详情

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

CANN ops-nn aclnnBatchNormElemt 算子两段式接口详解:原理、参数、源码实现与完整调用示例

CANN ops-nn aclnnBatchNormElemt 算子两段式接口详解:原理、参数、源码实现与完整调用示例 人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载本文以 CANN ops-nn 神经网络算子库中的 BatchNormElemt 元素级 BatchNorm 算子为对象系统讲解aclnnBatchNormElemt两段式 aclnn 接口的功能定义、参数约束、返回码、确定性约束并结合 op_host 实现与 UT/ST 测试 剖析其底层组合计算流程。读完本文你将掌握如何在 Ascend NPU 上正确构造输入 Tensor、分两步调用该算子完成 BatchNorm 元素级归一化并能对照源码排查参数校验失败问题。一、功能说明与计算公式aclnnBatchNormElemt是一个元素级别ElemtElementwise的 BatchNorm 操作函数它将全局的均值与标准差倒数作为算子输入对输入张量x逐元素做 BatchNorm 归一化计算主要用于推理等统计量已确定的场景避免在算子内部重新统计均值方差。该算子在仓库中的位置为 norm/batch_norm_elemt其 README 说明该目录仅包含 BatchNormElemt 算子对应的 aclnn 接口。计算公式如下$$y \frac{(x - E[x])}{\sqrt{Var(x) eps}} \times weight bias$$标准差与方差的关系为$$\frac{1}{S} \frac{1}{\sqrt{Var(x) eps}}$$其中E[x]表示输入x的均值即接口入参meanVar(x)表示输入x的方差S表示输入x的标准差eps表示添加到方差中的极小值用于避免除零weight、bias分别为可选的缩放与偏置参数可传空。与 aclnnBatchNorm 相比BatchNormElemt 的关键差异在于接口直接接收训练/推理阶段已统计好的mean与invstd标准差倒数作为入参由调用方负责谁来计算统计量的职责分工算子本身只做归一化变换因此更适合被上层框架在特定推理流程中复用。二、产品支持情况依据 aclnnBatchNormElemt.md 的标注本算子在不同产品系列上的支持情况如下产品系列支持情况Ascend 950PR 950DT 系列产品支持Atlas A3 系列产品支持Atlas A2 系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品支持其中有一项重要的平台差异在Atlas 训练系列产品上入参input、weight、bias、mean、invstd、output的数据类型不支持 BFLOAT16。这一点与 op_host 源码中的数据类型支持列表相印证——aclnn_batch_norm_elemt.cpp 中定义了ASCEND910_DTYPE_SUPPORT_LIST {DT_FLOAT, DT_FLOAT16}910 平台与ASCEND910B_DTYPE_SUPPORT_LIST {DT_FLOAT, DT_FLOAT16, DT_BF16}910B 及以上平台并按当前 SoC 版本动态选择校验列表。三、函数原型与两段式调用约定与 CANN 其他 aclnn 单算子 API 一样本算子采用两段式接口设计详见 两段式接口说明必须先调用aclnnBatchNormElemtGetWorkspaceSize完成入参校验并计算所需 workspace 大小再按返回的workspaceSize在 Device 侧申请临时内存最后调用aclnnBatchNormElemt执行计算。aclnnStatus aclnnBatchNormElemtGetWorkspaceSize( const aclTensor* input, const aclTensor* weight, const aclTensor* bias, aclTensor* mean, aclTensor* invstd, double eps, aclTensor* output, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnBatchNormElemt( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)接口的正式声明可参见 aclnn_batch_norm_elemt.h。注意两段式接口的调用约束第二段接口aclnnBatchNormElemt不能重复调用先调用 GetWorkspaceSize 再多次调用执行接口属于非法用法。四、aclnnBatchNormElemtGetWorkspaceSize 参数详解第一段接口共 9 个入参/出参各参数的使用约束如下表参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorinputaclTensor*输入进行 BatchNorm 计算的输入对应公式中的x支持空 Tensor2 维对应 NC3 维对应 NCL4 维对应 NCHW5 维对应 NCDHW6-8 维对应 ND其中第 2 维固定为 channel 轴FLOAT32、FLOAT16、BFLOAT16NC、NCL、NCHW、NCDHW、ND2-8√weightaclTensor*输入可选输入BatchNorm 权重 Tensor对应公式中的weight支持空 Tensor数据类型与input一致shape 长度与input的 channel 轴长度相等FLOAT32、FLOAT16、BFLOAT16ND1√biasaclTensor*输入可选输入BatchNorm 偏置 Tensor对应公式中的bias支持空 Tensor数据类型与input一致shape 长度与input的 channel 轴长度相等FLOAT32、FLOAT16、BFLOAT16ND1√meanaclTensor*输入输入数据均值对应公式中的E(x)支持空 Tensor数据类型与input一致shape 长度与input的 channel 轴长度相等FLOAT32、FLOAT16、BFLOAT16ND1√invstdaclTensor*输入输入数据标准差倒数即sqrt(Var(x) eps)的倒数支持空 Tensor支持元素值均大于 0 的场景数据类型与input一致shape 长度与input的 channel 轴长度相等FLOAT32、FLOAT16、BFLOAT16ND1√epsdouble输入添加到方差中的值避免除以零对应公式中的eps-----outputaclTensor*输出最终输出结果对应公式中的y支持空 Tensor数据类型、shape 与input一致支持的 shape 与格式同inputFLOAT32、FLOAT16、BFLOAT16NC、NCL、NCHW、NCDHW、ND2-8√workspaceSizeuint64_t*输出返回用户需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----几点实操提示weight、bias是可选参数传入nullptr时实现内部会自动用 1weight和 0bias填充见 aclnn_batch_norm_elemt.cpp 中的FillScalar(dimC, 1, ...)/FillScalar(dimC, 0, ...)invstd语义是标准差倒数而非标准差本身且约束元素值均大于 0否则sqrt(Vareps)无意义六个 Tensorinput、weight、bias、mean、invstd、output在非空时数据类型必须保持一致数据格式非私有格式与 shape 必须匹配。五、返回值与常见报错两段接口均返回aclnnStatus状态码完整状态码语义参见 aclnn 返回码。其中第一段接口完成入参校验出现以下场景时返回对应错误返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的必选指针类型入参是空指针input、mean、invstd、output 均不可为空见源码中 CheckNotNull。ACLNN_ERR_PARAM_INVALID161002input、output 数据类型和数据格式不在支持范围内input/output shape 不在支持范围不支持 input C 轴为 0 的空 Tensormean/invstd 维度非一维或 shape 不等于 input C 轴长度weight/bias 非空时维度非一维或 shape 不等于 input C 轴长度input 和 output 数据格式不一致weight、bias 非空时各 Tensor 数据类型不一致input 和 output shape 不一致。这些校验逻辑与源码一一对应CheckDtypeValid负责数据类型校验L48-L69CheckFormat校验 input/output 存储格式一致且为非私有格式L71-L84CheckShape校验维度范围2~8 维、channel 非 0、各统计参数为 1 维且长度等于 channel 数L86-L115。UT 测试 test_aclnn_batchnormelemt.cpp 中对mean使用 INT32、mean维度为 2、input C 轴为 0、invstdshape 为 5 等非法输入均断言返回ACLNN_ERR_PARAM_INVALID。六、aclnnBatchNormElemt 参数说明第二段接口用于真正执行计算参数如下参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入workspace 大小由第一段接口aclnnBatchNormElemtGetWorkspaceSize获取。executor输入op 执行器包含算子计算流程。stream输入指定执行任务的 Stream。七、约束说明确定性计算Atlas A3 系列产品、Atlas A2 系列产品、Atlas 训练系列产品aclnnBatchNormElemt默认是非确定性实现支持通过aclrtCtxSetSysParamOpt开启确定性Ascend 950PR 950DT 系列产品默认即为确定性实现。八、完整调用示例可编译运行以下示例代码与仓库 examples/test_aclnn_batch_norm_elemt.cpp 保持一致完整走通环境初始化 → 构造 Tensor → 第一段接口 → 申请 workspace → 第二段接口 → 同步 → 取回结果 → 资源释放全流程#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_batch_norm_elemt.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根据自己的需要处理 CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 // 本示例3维输入 {2,4,2}C轴4weight/bias传nullptr由实现内部填充1/0 std::vectorint64_t inputShape {2, 4, 2}; std::vectorint64_t meanShape {4}; std::vectorint64_t invstdShape {4}; std::vectorint64_t outShape {2, 4, 2}; double eps 1e-2; void* inputDeviceAddr nullptr; void* meanDeviceAddr nullptr; void* invstdDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* input nullptr; aclTensor* mean nullptr; aclTensor* invstd nullptr; aclTensor* out nullptr; std::vectorfloat inputHostData {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; std::vectorfloat meanHostData {1, 2, 3, 4}; std::vectorfloat invstdHostData {5, 6, 7, 8}; std::vectorfloat outHostData(16, 0); // 创建input aclTensor ret CreateAclTensor(inputHostData, inputShape, inputDeviceAddr, aclDataType::ACL_FLOAT, input); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建mean aclTensor ret CreateAclTensor(meanHostData, meanShape, meanDeviceAddr, aclDataType::ACL_FLOAT, mean); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建invstd aclTensor ret CreateAclTensor(invstdHostData, invstdShape, invstdDeviceAddr, aclDataType::ACL_FLOAT, invstd); 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); uint64_t workspaceSize 0; aclOpExecutor* executor; // aclnnBatchNormElemt接口调用示例 // 3. 调用CANN算子库API需要修改为具体的API名称 // 调用aclnnBatchNormElemt第一段接口weight、bias传nullptr ret aclnnBatchNormElemtGetWorkspaceSize(input, nullptr, nullptr, mean, invstd, eps, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnBatchNormElemtGetWorkspaceSize 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); } // 调用aclnnBatchNormElemt第二段接口 ret aclnnBatchNormElemt(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnBatchNormElemt 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侧 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy( resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[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(result[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor aclDestroyTensor(input); aclDestroyTensor(mean); aclDestroyTensor(invstd); aclDestroyTensor(out); // 7. 释放Device资源 aclrtFree(inputDeviceAddr); aclrtFree(meanDeviceAddr); aclrtFree(invstdDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }九、编译与运行示例代码的编译与运行流程遵循仓库统一的 编译与运行样例 指南核心步骤如下将上述代码保存为test_aclnn_batch_norm_elemt.cpp并准备一份 CMakeLists.txtproject(ACLNN_EXAMPLE)add_executable(opapi_test test_aclnn_batch_norm_elemt.cpp)链接libascendcl.so、libnnopbase.so、libopapi_nn.so等库安装 CANN 软件后使用 CANN 运行用户登录环境执行source ${INSTALL_DIR}/set_env.sh生效环境变量${INSTALL_DIR}为 CANN 软件安装路径编译并运行mkdir -p build cd build cmake ../ -DCMAKE_CXX_COMPILERg -DCMAKE_SKIP_RPATHTRUE make cd bin ./opapi_test执行成功后程序会逐行打印result[i] is: ...形式的结果。若运行报错可通过aclGetRecentErrMsg接口获取具体异常信息示例aclnnBatchNormElemtGetWorkspaceSize failed. ERROR: 161001对应参数空指针错误。十、底层实现原理基于组合算子的元素级归一化从源码结构看aclnnBatchNormElemt并不是一个独立 Kernel 实现而是在 op_host/op_api/aclnn_batch_norm_elemt.cpp 中通过组合底层算子完成计算其核心流程PrepareAndComputeBatchNormL127-L194如下连续性处理对input、weight、bias、mean、invstd调用l0op::Contiguous转为连续存储这正是支持非连续 Tensor约束的来源高维折叠当输入维度大于 5 时先通过l0op::Reshape将形状折叠为 5 维{N, C, D, H, -1}并ReFormat为 NCDHW统一交给 BatchNorm 处理计算结束后再Reshape回原始 shape 并ReFormat回 ND最后用l0op::ViewCopy写回outputL214-L235可选参数填充weight、bias为空时分别用全 1、全 0 的 C 维张量填充由 invstd 反推方差对invstd做Mul(invstd, invstd)得到1/Var再用Div(1, 1/Var)还原出Var最后Sub(Var, eps)得到Var eps的误差补偿形式等价地构造出 BatchNorm 算子所需的 variance 输入L157-L173调用 BatchNorm 算子最终调用底层BatchNorm(input, weight, bias, mean, variance, false, 0.0, eps, bnOutput, ...)完成归一化计算L190-L191Workspace 返回整个组合流程的临时内存需求由uniqueExecutor-GetWorkspaceSize()汇总通过executor句柄传递给第二段接口第二段接口aclnnBatchNormElemt则统一通过CommonOpExecutorRun完成执行L243-L248。此外第一段接口在入参校验通过后还会处理空 Tensor 快速路径当input为空IsEmpty()时直接返回workspaceSize 0而不构建执行流L208-L212。十一、测试验证与参考实现仓库为本算子提供了两层测试保障UT 单测tests/ut/op_host/op_api/test_aclnn_batchnormelemt.cpp覆盖 FLOAT16/FLOAT32 在 2D/3D/4D/6D 下的TestGetWorkspaceSize调用、weight/bias传 nullptr 的缺省场景、非连续输入以及 dtype 非法mean 为 INT32、mean 维度非法、channel 为 0、invstd shape 非法等负向用例均断言返回ACLNN_ERR_PARAM_INVALIDST 用例atk_aclnnBatchNormElemt.json executor_aclnnBatchNormElemt.py以batch_norm_elemt_cpu类型注册其 CPU 参考实现直接调用 PyTorch 的torch.nn.functional.batch_norm(input, mean, invstd, weight, bias, False, 1, eps)生成基准结果输入按维度自动映射数据格式2 维 NC、3 维 NCL、4 维 NCHW、5 维 NCDHW、其余 ND覆盖从 2 维到 8 维、channel 数从 2 到 131073 的多种 shape 组合。阅读该 JSON 中的用例列表可以快速确认算子在实际验收中支持的 shape 与 dtype 覆盖范围。十二、小结aclnnBatchNormElemt是 CANN ops-nn 中一个典型的接口薄、组合深的元素级归一化算子对外提供简洁的两段式 aclnn 接口直接接收mean与invstd完成推理式 BatchNorm对内则通过 Contiguous、Reshape、ReFormat、Mul/Div/Sub 与底层 BatchNorm 算子的组合在 NPU 上高效执行。开发者在使用时重点把握三点六类 Tensor 的数据类型/格式/shape 一致性、invstd须取标准差倒数且元素大于 0、以及两段式接口的先查空间、再执行调用顺序。如需贡献该算子的 AscendC 内核实现可参考仓库根目录的 CONTRIBUTING.md 贡献流程。赞分享人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载相关推荐CANN ops-nn 算子 aclnnNonzeroV2 接口详解两段式调用、参数约束与完整示例CANN ops nn 算子 aclnnNonzeroV2 接口详解两段式调用、参数约束与完整示例 导读 aclnnNonzeroV2 是 CANN ops人工智能算子库深度学习CANNAscendCANN ops-nn 中 aclnnSwish 算子接口详解两段式调用、参数约束与源码级实现原理CANN ops nn 中 aclnnSwish 算子接口详解两段式调用、参数约束与源码级实现原理 本文以 CANN 神经网络算子库ops nn中 Swi人工智能算子库深度学习CANNAscendCANN ops-nn 算子 aclnnAddRelu / aclnnInplaceAddRelu 接口详解两段式调用、参数约束与源码实现CANN ops nn 算子 aclnnAddRelu / aclnnInplaceAddRelu 接口详解两段式调用、参数约束与源码实现 aclnnAddR人工智能算子库深度学习CANNAscend上一篇AutoDock-Vina终极指南如何在5分钟内完成分子对接下一篇GmSSL国密安全通信协议技术选型指南TLCP与TLS 1.3深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表