十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

CANN ops-math aclnnInplaceNormal 算子使用指南:用正态分布随机数原位填充张量

CANN ops-math aclnnInplaceNormal 算子使用指南:用正态分布随机数原位填充张量 CANN ops-math aclnnInplaceNormal 算子使用指南用正态分布随机数原位填充张量【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math本篇技术指南以 CANN / ops-math 开源仓库中 aclnnInplaceNormal 算子文档 为主体系统讲解如何在 NPU 上通过两段式 aclnn 接口使用给定均值mean与标准差std的正态分布随机数原位填充in-place张量selfRef的完整流程。阅读完本文你将掌握aclnnInplaceNormalGetWorkspaceSize/aclnnInplaceNormal两个接口的入参约束、返回值与错误码语义、确定性计算约束并能基于仓库提供的示例代码与底层实现独立完成该算子 API 的编译、运行与精度验证。一、算子功能与产品支持情况功能说明aclnnInplaceNormal从给定的均值mean和标准差std的离散正态分布中抽取随机数用于填充selfRef张量。其中mean基础类型为float表示与每个输出元素相关的正态分布均值std基础类型为float表示与每个输出元素相关的正态分布标准差取值必须大于等于 0selfRef同时作为输入与输出in-place计算完成后随机数直接写回原张量。该接口对应 PyTorch 中torch.Tensor.normal_()的语义是深度学习中权重初始化、噪声注入等场景的基础算子。本仓库中该算子对应的实现位于 random/dsa_random_normal其中dsa_random_normal表示该目录承载的是DSADeterministic Sequence Algorithm确定性序列算法随机算子族即同一seedoffset组合在不同硬件上可复现出确定性的随机数序列。产品支持情况根据文档声明该算子在不同硬件平台上的支持情况如下硬件产品支持情况Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 训练系列产品Atlas 910支持Atlas 200I/500 A2 推理产品310B不支持Atlas 推理系列产品310P不支持需要注意的是支持列表因产品而异且不同产品对数据类型与offset取值还有额外约束详见约束说明章节使用前应结合目标硬件确认。二、两段式接口调用模型与 CANN 其他单算子 API 一致aclnnInplaceNormal采用两段式接口模型参见 两段式接口说明必须先调用第一段接口aclnnInplaceNormalGetWorkspaceSize获取计算所需的 workspace 大小以及封装了算子计算流程的执行器executor再调用第二段接口aclnnInplaceNormal执行计算。workspace除输入/输出外算子在 NPU 上完成计算所需的临时内存workspaceSize表示该临时内存的大小由第一段接口计算得出。第二段接口不可重复调用同一个 executor 只能执行一次计算重复调用会出现异常。若需多次计算必须重新走第一段接口 → 申请 workspace → 第二段接口的完整流程。两个接口的函数原型如下aclnnStatus aclnnInplaceNormalGetWorkspaceSize( const aclTensor* selfRef, float mean, float std, int64_t seed, int64_t offset, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnInplaceNormal( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)其中接口前缀aclnn表示算子接口类型InplaceNormal 表示执行 in-place 正态分布填充的算子。三、第一段接口 aclnnInplaceNormalGetWorkspaceSize 参数详解第一段接口完成入参校验、计算 workspace 大小并构建执行器其参数说明如下参数名输入/输出描述使用说明数据类型数据格式维度shape非连续 tensorselfRefaclTensor*输入/输出输入输出 tensor当 selfRef 为空 tensor 时直接返回成功不执行计算FLOAT32、INT32、INT64、BFLOAT16、FLOAT16、INT16、INT8、UINT8、BOOL、DOUBLEND支持 0-8 维√meanfloat输入表示随机均值-FLOAT32---stdfloat输入表示随机数的标准差需要大于等于 0-FLOAT32---seedint64_t输入设置随机数生成器的种子值-INT64---offsetint64_t输入表示随机数的偏移量-INT64---workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----其中selfRef的数据类型支持范围存在平台差异需要特别留意Atlas 训练系列产品、Atlas A2 训练系列产品/推理系列产品、Atlas A3 训练系列产品/推理系列产品不支持 INT32、INT64、INT16、INT8、UINT8、BOOL。即上述平台上selfRef仅支持 FLOAT32、FLOAT16、BFLOAT16、DOUBLE 等浮点类型整型与 BOOL 类型仅在 Ascend 950 系列上可用。这一限制可以从源码得到印证在 aclnn_normal.cpp 中DTYPE_SUPPORT_LIST包含全部 10 种类型而ASCEND910_DTYPE_SUPPORT_LIST去掉了 BF16同时CheckDtypeValid会针对不支持 BF16 的 SoC 版本直接拦截DT_BF16输入。返回值与错误码接口返回aclnnStatus状态码常见状态码定义见 aclnn 返回码说明。第一段接口完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 selfRef 为空指针ACLNN_ERR_PARAM_INVALID161002传入的 std 小于 0ACLNN_ERR_PARAM_INVALID161002selfRef 的数据类型不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002std、mean 的数据类型不符合接口入参要求ACLNN_ERR_PARAM_INVALID161002selfRef 的 shape 超过 8 维从源码看上述校验在 aclnn_normal.cpp 的CheckParams中按顺序执行先判空指针CheckNotNull对应 161001再检查std 0对应 161002随后用CheckDtypeValid校验数据类型、用CheckDimValid校验维度不超过 8 维MAX_DIM_LEN 8。四、第二段接口 aclnnInplaceNormal 参数详解第二段接口真正在 NPU 上执行计算参数说明如下参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnInplaceNormalGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Streamworkspace 大小必须为 0 时无需申请内存可直接传入空指针只有workspaceSize 0时才需要调用aclrtMalloc申请 Device 内存。第二段接口返回aclnnStatus状态码具体语义参见 aclnn 返回码说明。从源码实现看第二段接口内部通过CommonOpExecutorRun统一完成计算下发即把第一段构建好的算子计算图按 executor 描述在指定 stream 上执行。五、约束说明使用本接口前请重点关注以下约束确定性计算aclnnInplaceNormal默认即为确定性实现即相同seed、offset、shape 下生成的随机数序列可复现无需额外开启确定性模式确定性计算的更多背景可参见 确定性计算说明。offset 约束Ascend 950PR/Ascend 950DT在 Ascend 950 系列产品上offset必须为 4 的倍数。这与 DSA 路径中offset直接作为 counter 偏移参与随机数生成有关不满足该约束会导致非法参数错误。六、源码级实现原理从两段式接口到 NPU 计算该算子的 Host 侧实现集中在 aclnn_normal.cpp其整体调用流程可以概括为参数校验 → 预处理 → 分平台路径计算 → 后处理四步1. 预处理非连续 tensor 与类型提升selfRef支持非连续 tensor如转置、切片得到的视图。在 PrepareContiguousOutput 中先调用l0op::Contiguous将selfRef转为连续 tensor若selfRef数据类型不在浮点输出支持列表FLOAT16/FLOAT32/DOUBLE/BF16内会先l0op::Cast提升为 FLOAT32 参与计算避免整型中间精度损失。2. 空 tensor 快捷路径在 aclnnInplaceNormalGetWorkspaceSize 中若selfRef-IsEmpty()成立空 tensor直接返回ACLNN_SUCCESS且workspaceSize 0不执行任何计算这与文档中当 selfRef 为空 tensor 时直接返回成功的说明一致。3. 分平台计算路径源码根据当前 NPU 架构选择不同计算路径DSA 路径Ascend 950架构 DAV_2201直接调用l0op::DSARandomNormal定义见 dsa_random_normal.cpp以countshape 元素总数、seed、offset、mean、std为输入一次性完成确定性正态分布采样通用路径David 路径其他支持平台走normalDavidPath将seed转化为 key、offset转化为 counter调用l0op::StatelessRandomNormalV2生成标准正态随机数再通过Mul(std)Add(mean)完成缩放与平移DOUBLE 类型走normalDoublePath在 Atlas A2DAV_3510上还会根据 PyTorch 随机模式选择StatelessRandomNormalV3或StatelessNormal以保证与框架精度对齐。4. 后处理类型还原与原位写回PostProcessInplaceNormal 将计算结果l0op::Cast回selfRef的原始数据类型再通过l0op::ViewCopy写回原 tensor即使selfRef非连续也能正确落位最后通过executor-GetWorkspaceSize()得到真实 workspace 大小。七、完整调用示例以下代码摘自仓库文档及 示例工程可直接作为编写自己调用程序的模板。示例以 shape 为{2, 3}的 FLOAT32 张量为例mean 2.0、std 1.0、seed 1、offset 0#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_normal.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 selfShape {2, 3}; float mean 2.; float std 1.; int64_t seed 1; int64_t offset 0; void* selfDeviceAddr nullptr; aclTensor* selfRef nullptr; std::vectorfloat selfHostData {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, selfRef); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API需要修改为具体的API名称 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnInplaceNormal第一段接口 ret aclnnInplaceNormalGetWorkspaceSize(selfRef, mean, std, seed, offset, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceNormalGetWorkspaceSize 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); } // 调用aclnnInplaceNormal第二段接口 ret aclnnInplaceNormal(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceNormal 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(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和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(selfRef); // 7. 释放Device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }代码要点提示步骤 1、4 是固定写法初始化、同步步骤 2、5、6、7 需按实际 API 调整创建aclTensor时需同时给出 shape 与连续布局对应的 strides数据格式使用ACL_FORMAT_ND运行时需在环境变量已配置 CANN 环境的前提下执行source ${INSTALL_DIR}/set_env.sh若接口调用返回非 0 状态码可通过aclGetRecentErrMsg()获取详细错误信息用法示例见 编译与运行样例。八、编译与运行完整编译运行流程参考 编译与运行样例核心步骤概括如下准备 CMakeLists.txt将示例代码保存为test_aclnn_inplace_normal.cpp并按如下模板编写构建脚本链接库libascendcl.so、libnnopbase.so、libopapi_math.socmake_minimum_required(VERSION 3.14) project(ACLNN_EXAMPLE) add_compile_options(-stdc11) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin) add_executable(opapi_test test_aclnn_inplace_normal.cpp) if(NOT $ENV{ASCEND_CUSTOM_PATH} STREQUAL ) set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH /usr/local/Ascend/cann) endif() set(INCLUDE_BASE_DIR ${ASCEND_PATH}/include) include_directories(${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn) target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi_math.so)配置环境变量source ${INSTALL_DIR}/set_env.sh编译mkdir -p build cd build cmake ../ -DCMAKE_CXX_COMPILERg -DCMAKE_SKIP_RPATHTRUE make运行进入build/bin目录执行./opapi_test即可在终端看到形如result[0] is: 1.234567的随机数输出。由于正态分布采样的随机性每次seed不同结果会变化使用相同seed、offset则结果确定。九、正确性验证与测试仓库为aclnnInplaceNormal提供了完整的 ST系统级测试与单测可用于交叉验证实现正确性ST 测试测试入口位于 executor_aclnnInplaceNormal.py。其 golden 生成逻辑normal_golden使用 TensorFlow 的tf.raw_ops.StatelessRandomNormalV2生成标准正态随机数再做mul_data tf.multiply(normal_data, std)与add_data tf.add(mul_data, mean)——这与源码中通用路径StatelessRandomNormalV2 Mul(std) Add(mean)的计算链完全一致可视为对算子数学语义的权威参照。测试用例配置atk_aclnnInplaceNormal.json 中覆盖了 bf16/fp16/fp32/fp64 四种数据类型、从 1 维到 7 维的多种 shape含边界 shape 如 1、8、256、257 等以及std 0、mean 0等边界参数验证算子在不同 shape 与参数组合下的数值精度。UT 测试单元测试位于 test_aclnn_normal_l0.cpp 与 test_aclnn_normal_l2.cpp从 L0算子级与 L2接口级两个层次校验接口行为。十、延伸阅读若需要以 Device 侧 tensor 形式传入seed/offset例如希望动态更新随机数偏移量可参考同目录下的 aclnnInplaceNormalTensor 文档其接口aclnnInplaceNormalTensor将随机数偏移量扩展为offsetTensor值与标量offset之和适合在训练循环中灵活推进随机序列。关于两段式接口的通用说明两段式接口。关于 aclnn 返回码的完整定义aclnn 返回码。关于算子 API 调用的通用编译运行指引编译与运行样例。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表