ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

CANN ops-nn LpNormV3 算子全解析:Lp 范数计算与归一化在 NPU 上的实现及 aclnn 调用实践

CANN ops-nn LpNormV3 算子全解析:Lp 范数计算与归一化在 NPU 上的实现及 aclnn 调用实践 CANN ops-nn LpNormV3 算子全解析Lp 范数计算与归一化在 NPU 上的实现及 aclnn 调用实践【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nnLpNormV3 是 CANN ops-nn 开源算子库experimental/norm/lp_norm_v3中提供的 Lp 范数计算与归一化算子它按指定维度计算输入张量的 p 范数并用该范数对输入逐元素归一化输出。本文以该模块的 README.md 为主线结合算子定义、InferShape、Tiling、AscendC Kernel 与 aclnn 调用样例等源码系统讲解其数学原理、参数语义、NPU 多核实现机制并给出可复现的 FP32/FP16 调用与精度验证实践帮助开发者在 Atlas 训练/推理产品上快速完成 LpNormV3 的集成与验证。一、算子概述与产品支持情况1.1 功能定位LpNormV3 同时完成两项任务Lp 范数计算对输入张量按指定维度axis计算 p 范数归一化将输入张量的每个元素除以对应位置的范数输出与输入同形状的归一化结果。该能力在深度学习中常用于特征归一化如 L2 归一化、权重/梯度正则化以及数值稳定性处理等场景。按 README 的描述算子支持全局、按行、按列三种计算模式这一三种模式的划分与源码中三个 Tiling Key 一一对应详见下文。1.2 产品支持情况README 明确列出的产品支持范围如下产品是否支持Atlas A2 训练系列产品 / Atlas 800I A2 推理产品√该信息与算子定义文件 lp_norm_v3_def.cpp 中this-AICore().AddConfig(ascend910b)的配置相互印证——算子面向 910B 系列 AICore 构建。需要说明的是当前仓库代码仅覆盖上表所列产品其他产品型号的支持情况以 CANN 官方发布为准。二、数学原理与计算公式设输入张量为 (x)范数阶数为 (p)数值稳定项为 (\epsilon)范数的计算范围由axis参数决定Lp 范数计算[ \text{norm} \left( \sum |x_i|^p \epsilon \right)^{1/p} ]归一化结果[ y_i \frac{x_i}{\text{norm}} ]2.1 源码对公式的实现印证Kernel 中严格按上述两步流水实现两阶段处理流程与公式一一对应Reduce 阶段求 (\sum |x_i|^p)在 lp_norm_v3.h 的Reduce中先对元素取绝对值Abs再做 p 次幂Power最后按轴语义累加进局部和localSum开方阶段在SumAndSyncAll中核 0 对全局和加上 epsilon 后利用Ln → Muls(1/p) → Exp的等价变换计算 ((sum\epsilon)^{1/p})见 lp_norm_v3.h避免直接调用高开销的开方/幂函数Normalization 阶段逐元素用Div或标量除法除以对应范数槽位得到归一化输出见 lp_norm_v3.h。2.2 无穷范数L∞的特殊处理当p取正无穷或负无穷时公式退化为取绝对值后的最大值/最小值不再适用求和开方路径。Kernel 通过判断p的比特位0x7F800000/0xFF800000识别无穷情形见 lp_norm_v3.h在Process中分流到InfProcess/ReduceInf用逐元素 max/min 替代求和并以原子 max/min 完成跨核归约见 lp_norm_v3.h。调用样例中也预留了p std::numeric_limitsfloat::infinity()的写法见 test_aclnn_lp_norm_v3.cpp。2.3 数值稳定性README 将 (\epsilon) 定位为数值稳定补偿项用于避免范数过小时除法溢出。从源码看当前实现中 epsilon 是 Kernel 内硬编码常量1e-6见 lp_norm_v3.h并非运行时属性——算子定义仅注册了p与axis两个属性见下文参数说明这一点在对接时需与 README 参数表的口径区分开。三、参数说明README 给出的参数表如下参数名输入/输出/属性描述数据类型数据格式x输入待进行 LpNormV3 计算的输入张量FLOAT、FLOAT16NDp属性Lp 范数的阶数FLOAT-axis属性范数计算维度0全局1列2行INT-epsilon属性数值稳定补偿项FLOAT-y输出归一化后的输出张量FLOAT、FLOAT16ND3.1 参数在源码中的真实语义重要结合算子定义与 Tiling/Kernel 实现各参数在仓库代码中的实际约定如下与 README 表格存在两处需要开发者注意的差异axis取值算子定义中axis为可选 INT 属性默认值-1见 lp_norm_v3_def.cpp。Tiling 阶段依据axis生成三个 Tiling Key见 lp_norm_v3_tiling.cpp 与 lp_norm_v3_tiling_key.haxis 实际取值Tiling Key语义按源码workspace 槽位数-1LP_NORM_AXIS_NONE全局规约整个张量一个范数10LP_NORM_AXIS_0按行计算每行一个范数rows1LP_NORM_AXIS_1按列计算每列一个范数cols即源码实际使用-1 / 0 / 1三值与 README 表中0全局1列2行的文字描述并不一致调用样例 test_aclnn_lp_norm_v3.cpp 以DEFAULT_AXIS 0表示按行归一化同样印证了源码语义。开发者在写图或调用 aclnn 接口时应以源码为准全局传-1按行传0按列传1。epsilon属性README 参数表将其列为属性但算子定义只注册了p与axis见 lp_norm_v3_def.cppaclnn 接口签名aclnnLpNormV3GetWorkspaceSize(input, p, axis, output, ...)也没有 epsilon 入参。因此当前版本中 epsilon 为 Kernel 内部常量1e-6未向用户开放配置。p的默认值p为可选 FLOAT 属性默认2.0f即默认做 L2 范数归一化见 lp_norm_v3_def.cpp调用样例同样使用DEFAULT_P 2.0f。输出形状y与输入x同形状。InferShape 实现直接将输入各维度拷贝给输出见 lp_norm_v3_infershape.cpp这也与逐元素归一化、形状不变的语义一致。输入维度限制从 Tiling 源码看rows shape.dim(0)、cols shape.dim(1)见 lp_norm_v3_tiling.cpp可以推断当前实现主要面向二维张量按行/按列模式全局模式同样通过二维形状的rows*cols展开处理调用样例也明确限定Only support 2D input见 test_aclnn_lp_norm_v3.cpp。四、源码结构总览该算子模块按 CANN 算子标准布局组织各目录职责清晰experimental/norm/lp_norm_v3/ ├── README.md # 算子说明文档本文主体 ├── CMakeLists.txt # 模块构建入口遍历子目录 ├── examples/ │ └── test_aclnn_lp_norm_v3.cpp # aclnn 接口调用样例FP32/FP16 ├── op_host/ │ ├── CMakeLists.txt │ ├── lp_norm_v3_def.cpp # 算子原型定义输入/输出/属性/平台 │ ├── lp_norm_v3_infershape.cpp # InferShape输出形状推导 │ └── lp_norm_v3_tiling.cpp # Tiling分块、多核调度、workspace 计算 ├── op_kernel/ │ ├── lp_norm_v3.cpp # Kernel 入口模板实例化 │ ├── lp_norm_v3.h # Kernel 核心实现Reduce/Normalize │ ├── lp_norm_v3_tiling_data.h # Tiling 数据结构 │ └── lp_norm_v3_tiling_key.h # Tiling Key三种轴模式 └── tests/ └── ut/ # 单测目录当前为空占位模块 CMakeLists.txt 通过file(GLOB ...)枚举子目录并add_subdirectory且仅在ENABLE_TEST或BENCHMARK开启时才包含tests目录遵循仓库统一的算子模块组织方式。五、算子定义与 InferShape数据契约5.1 算子原型定义lp_norm_v3_def.cpp 通过OpDef注册算子元信息构成数据契约输入xREQUIRED必选支持DT_FLOAT、DT_FLOAT16格式为FORMAT_ND并对未确定 shape 的场景声明了UnknownShapeFormat同时调用AutoContiguous()保证输入内存连续化输出y同样必选数据类型与格式与输入对齐属性pOPTIONALFLOAT默认2.0f属性axisOPTIONALINT默认-1平台配置AICore().AddConfig(ascend910b)与 README 产品支持表呼应。OP_ADD(LpNormV3)将该算子注册进算子信息库供框架侧构图与下发使用。5.2 InferShapelp_norm_v3_infershape.cpp 中InferShapeLpNormV3的逻辑非常简洁读取输入x的形状将xShapeSize与各维尺寸原样写入输出y。由于归一化不改变张量形状这一推导是准确且高效的。六、Tiling 设计多核分块、workspace 与调度模式Tiling 在 lp_norm_v3_tiling.cpp 中完成是连接 Host 侧与 Device 侧 Kernel 的桥梁核心要点如下6.1 平台信息与 UB 容量GetPlatformInfo通过PlatformAscendC获取片上UBUnified Buffer大小与可用核数lp_norm_v3_tiling.cpp。单核单次搬运的数据量tileDataNum依据UB_SIZE / BUFFER_NUM / blockSize / 10计算lp_norm_v3_tiling.cpp其中BUFFER_NUM 2对应 Kernel 侧的双缓冲队列设计。6.2 多核负载均衡CalculateCoreBlockNums与LpNormV3TilingFunc共同决定核数及每个核处理的数据块若单 tile 即可容纳全部输入tileDataNum inputNum则退化为单核执行否则在可用核数与对齐后数据块数之间取较小值保证每个核至少分到 32B 数据lp_norm_v3_tiling.cpp为应对数据量不能被核数整除的情况Tiling 数据中同时输出smallCoreDataNum / bigCoreDataNum、smallTailDataNum / bigTailDataNum、finalSmallTileNum / finalBigTileNum等成对参数让前tailBlockNum个核处理大核数据、其余核处理小核数据实现负载均衡。这些字段的结构定义见 lp_norm_v3_tiling_data.h。6.3 Workspace 分配策略GetWorkspaceSize依据轴模式决定 workspace 大小lp_norm_v3_tiling.cpp全局模式1 个范数槽位按行模式rows个槽位按列模式cols个槽位。每个范数以 float 存储并做 64B 对齐SLOT_STRIDE 64 / sizeof(float) 16即每个槽位 16 个 float注释明确说明多核并发写全局缓存时若多个核在同一个 64B 内同时操作会导致随机覆写因此按范数数量 × 16 float分配lp_norm_v3_tiling.cpp。workspace 总大小为用户槽位 系统 workspaceGetLibApiWorkSpaceSize。6.4 调度模式与 Tiling Key由于 LpNormV3 需要跨核归约先求和、再广播范数Tiling 通过context-SetScheduleMode(1)声明为核间同步算子lp_norm_v3_tiling.cpp并依据axis设置LP_NORM_AXIS_NONE / LP_NORM_AXIS_0 / LP_NORM_AXIS_1三个编译模板参数Tiling Key见 lp_norm_v3_tiling_key.h从而在编译期展开不同的轴处理分支避免运行时分支开销。七、Kernel 实现原理两阶段流水 原子归约Kernel 入口 lp_norm_v3.cpp 以__global__ __aicore__模板函数实例化NsLpNormV3::LpNormV3DTYPE_X, schMode核心实现在 lp_norm_v3.h。7.1 整体流程Process根据p是否为无穷选择NormalProcess或InfProcess两者结构一致均分两个阶段规约阶段循环CopyIn → Reduce或ReduceInf将输入分 tile 搬入 UB计算|x|^p或 max/min累加进各核本地localSum同步与广播阶段SumAndSyncAll或SumAndSyncAllInf通过 workspace 原子操作完成跨核归约核 0 计算最终范数并写回 workspace随后SyncAll同步所有核归一化阶段各核再次CopyIn → Normalization → CopyOut按轴语义读取对应范数槽位逐元素相除得到输出。7.2 轴语义在 Kernel 中的映射Reduce与Normalization中通过全局线性索引反推行/列lp_norm_v3.h按行AXIS_0rowIdx globalIdx / cols行内元素累加到同一行范数按列AXIS_1colIdx globalIdx % cols列内元素累加到同一列范数全局AXIS_NONE所有元素累加为单一标量。归一化阶段反向使用同样的索引取范数并相除lp_norm_v3.h。7.3 跨核归约的可靠性设计原子加各核通过SetAtomicAddfloat()DataCopy将本地和累加到 workspace 槽位完成后SetAtomicNone()恢复lp_norm_v3.h无穷范数路径则使用SetAtomicMax/SetAtomicMin缓存一致性核 0 在写回范数后调用DataCacheCleanAndInvalid...(workGm[...])逐槽位刷缓存配合SyncAll与PipeBarrierPIPE_V保证其他核读到的是最新范数值lp_norm_v3.h数据类型适配FP16 输入在Reduce中先Cast到 float 再计算规避半精度累加误差lp_norm_v3.h。八、aclnn 调用实践从样例代码到精度验证README 指明算子通过 aclnn 接口调用对应样例为 test_aclnn_lp_norm_v3.cpp。该样例完整演示了 ACL 环境初始化 → 数据准备 → 张量创建 → workspace 申请 → 算子执行 → 结果回拷 → 精度验证的完整闭环可直接作为集成参考。8.1 环境初始化与张量创建// ACL 初始化aclInit - aclrtSetDevice - aclrtCreateStream int Init(int32_t deviceId, aclrtStream* stream); // 创建输入/输出张量申请 Device 内存 Host-Device 拷贝 aclCreateTensor template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto ret aclrtMalloc(deviceAddr, elemNum * sizeof(T), ACL_MEM_MALLOC_HUGE_FIRST); ret aclrtMemcpy(*deviceAddr, memSize, hostData.data(), memSize, ACL_MEMCPY_HOST_TO_DEVICE); // 计算连续张量 stridesshape 为 ND 格式 *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); }样例以固定随机种子12345在 [1, 5] 区间生成输入输入形状为{2, 3}规避范数为 0 的退化场景便于精度比对test_aclnn_lp_norm_v3.cpp。8.2 算子执行四步曲// 1. 获取 workspace 大小与 executor注意接口入参为 p 与 axis aclnnLpNormV3GetWorkspaceSize(inputTensor, DEFAULT_P /*2.0f*/, DEFAULT_AXIS /*0*/, outputTensor, workspaceSize, executor); // 2. 申请 workspace可能为 0 if (workspaceSize 0) { aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); } // 3. 执行算子 aclnnLpNormV3(workspaceAddr, workspaceSize, executor, stream); // 4. 流同步确保算子完成 aclrtSynchronizeStream(stream);执行完毕后通过aclrtMemcpy(..., ACL_MEMCPY_DEVICE_TO_HOST)将结果回拷 HostFP32 直接以 float 打印FP16 需经aclFloat16ToFloat转回 float 展示test_aclnn_lp_norm_v3.cpp。8.3 Golden 计算与精度验证思路样例的ComputeGoldenData在 Host 侧按公式手算范数与归一化结果作为基准golden支持有限 p 与无穷 p 两种分支test_aclnn_lp_norm_v3.cpp。VerifyResult逐元素比较算子输出与 golden统计Max Error / Avg Error阈值FP32 为1e-5FP16 为1e-3test_aclnn_lp_norm_v3.cpp超差时打印前 8 个失配元素的下标、输出值、golden 值与误差便于定位。主函数默认执行 FP32 用例FP16 用例通过ENABLE_FP16_TEST开关启用默认关闭见 test_aclnn_lp_norm_v3.cpp。FP16 路径的 golden 以 FP32 精度计算后与半精度输出比对从而把误差来源收敛到半精度表示与累加精度上。8.4 构建与运行该模块通过仓库顶层 CMake 统一构建experimental 目录下算子模块均以子目录方式挂接模块 CMakeLists.txt 负责遍历并add_subdirectory各子目录样例代码需在具备 CANN 工具链含 acl/acl.h 与生成的 aclnn_lp_norm_v3.h 接口头文件与对应 NPU 设备README 所列 Atlas A2 系列的环境下编译运行。测试目录tests/ut当前为占位状态可在开启ENABLE_TEST后按仓库 tests/ut 的既有范式补充单测。九、从 README 到源码的几点实践提示axis 语义以源码为准全局传-1、按行传0、按列传1与 README 参数表的文字描述存在差异构图或调用前务必确认epsilon 当前不可配置README 列为属性但实际为 Kernel 内部常量1e-6若业务需要其他补偿值需关注后续版本是否开放该属性输入维度当前实现按二维rows/cols组织归约高维张量场景需先确认算子是否支持或自行 reshape无穷范数支持p取 ±inf 时走独立的 max/min 路径样例中已预留该配置方式产品边界当前仅支持 Atlas A2 训练系列 / Atlas 800I A2 推理产品对应ascend910bAICore 配置。十、贡献信息按 README 记录LpNormV3 由个人开发者 shixiangyang 于 2025/11/8 适配贡献至开源仓贡献方个人开发者代码版权归哈尔滨工业大学 AISS GroupOpenBOAT 项目所有并遵循 CANN Open Software License Agreement Version 2.0 开源许可见仓库根目录 LICENSE。【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表