ARTICLE DETAIL

资讯详情

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

CANN ops-math 算子库 ReduceStdV2 深度解析:标准差与均值联合归约的接口、约束与 Kernel 实现

CANN ops-math 算子库 ReduceStdV2 深度解析:标准差与均值联合归约的接口、约束与 Kernel 实现 CANN ops-math 算子库 ReduceStdV2 深度解析标准差与均值联合归约的接口、约束与 Kernel 实现【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-mathReduceStdV2 是 CANN ops-math 数学基础算子库中负责在指定维度上同时计算标准差std与均值mean的归约算子广泛应用于神经网络归一化、数值统计与分布计算场景。本文以 experimental/math/reduce_std_v2/README.md 为核心结合仓库内算子定义、shape 推导、Tiling、Kernel 与单元测试源码系统讲解其数学原理、参数语义、产品支持范围、边界行为以及 aclnnStd 调用方式帮助读者在 NPU 上正确、高效地使用该算子。功能说明一次归约同时产出标准差与均值ReduceStdV2 计算指定维度dim上的标准差和均值其中dim可以是单个维度、维度列表或 None空列表表示对所有维度归约。假设dim为维度iN为该维度的 shape 大小取该维度上的数据 $x_{i}$先求出平均值 $\bar{x_{i}}$再按下式计算标准差$$ std \sqrt{\frac{1}{max(0, N - \delta N)}\sum_{j0}^{N-1}(x_{ij}-\bar{x_{i}})^2} $$其中 $\delta N$ 即属性correction修正值对应样本标准差中的 Bessel 修正当correction1时即为统计学中常用的样本标准差分母为N-1。公式中的max(0, N - δN)保证了自由度不为负也为后文介绍的 NaN/Inf 边界行为埋下伏笔。当keepdim true时reduce 后保留被归约的维度且输出 shape 中该维度值为 1当keepdim false时被归约的维度被删除。产品支持情况产品是否支持Ascend 950PR / Ascend 950DT√Atlas A3 训练系列产品 / Atlas A3 推理系列产品√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品√Atlas 训练系列产品√需要特别说明的是Atlas 训练系列产品、Atlas 推理系列产品不支持 BFLOAT16 数据类型。从源码看算子当前的 AICore 配置也集中在最新架构上reduce_std_v2_def.cpp 中通过this-AICore().AddConfig(ascend950, aicoreConfig)注册了 ascend950 的 AICore 配置并同时开启了动态编译、动态 Rank 与动态 Shape 支持OpAICoreConfig aicoreConfig; aicoreConfig.DynamicCompileStaticFlag(true) .DynamicRankSupportFlag(true) .DynamicShapeSupportFlag(true) .ExtendCfgInfo(opFile.value, reduce_std_v2_apt); this-AICore().AddConfig(ascend950, aicoreConfig);参数说明下表完整列出 ReduceStdV2 算子的输入、属性与输出参数名输入/输出/属性描述数据类型数据格式x输入待进行 ReduceStdV2 计算的输入 Tensor即公式中的 xFLOAT、FLOAT16、BFLOAT16NDdim属性参与 Reduce 计算的维度即公式中的 i。默认值为空列表表示对所有维度做 ReduceLIST_INT-correction属性修正值即公式中的 deltaINT64-keepdim属性是否在输出张量中保留输入张量的维度BOOL-is_mean_out属性是否输出均值。true 表示输出 meanfalse 表示不输出 meanBOOL-std输出ReduceStdV2 计算的标准差出参即公式中的 stdFLOAT、FLOAT16、BFLOAT16NDmean输出ReduceStdV2 计算的均值出参即公式中的平均值FLOAT、FLOAT16、BFLOAT16ND这些参数与算子定义注册中的描述一一对应。在 reduce_std_v2_def.cpp 中可以看到输入x、输出std、mean均注册为REQUIRED支持的数据类型为{DT_FLOAT16, DT_FLOAT, DT_BF16}格式为FORMAT_ND属性dim为可选的ListInt默认空列表属性correction为可选的Int属性keepdim为可选的Bool默认值false属性is_mean_out为可选的Bool默认值true。其中is_mean_out属性由 l0op 层接口透传见 reduce_std_v2.h 中ReduceStdV2(self, dim, correction, keepdim, isMeanOut, executor)的签名。在 aclnnStd 高层接口中该值被固定为false只输出 std均值作为中间量而图算子的std、mean双输出则始终推导 shape。输出 shape 的推导规则std和mean的 shape 由dim和keepdim共同推导keepdim true保留被 Reduce 的维度且该维度值为 1keepdim false删除被 Reduce 的维度。该规则在 reduce_std_v2_infershape.cpp 中有完整实现。关键逻辑包括dim 为空列表时GetReduceAxes会将[0, inputDimNum)全部作为归约轴等价于对所有维度做 Reduce负数维度归一化NormalizeReduceAxes校验每个轴必须落在[-rank(x), rank(x))范围内负数轴统一加上inputDimNum转为正索引否则返回GRAPH_FAILED并报错dim value %ld is out of rangekeepdim 分支分别调用ReduceDimsWithKeepDims与ReduceDimsWithoutKeepDims完成输出 shape 推导双输出同步推导由于 GE 没有可选输出的概念会给每个输出都申请内存因此mean的 shape 必须与std保持一致源码注释明确说明即*meanShape *varShape。在 dtype 推导方面reduce_std_v2_graph_infer.cpp 的InferDtype4ReduceStdV2将输入x的数据类型直接透传给输出std与mean保证图算子侧三者类型一致。约束说明与合法性校验使用 ReduceStdV2 时需遵守以下约束dim中的每个维度值必须在[-rank(x), rank(x))范围内支持负数维度同一维度归一化后不能重复。当dim为空列表时对x的所有维度进行 Reduce。std和mean的 shape 由dim和keepdim推导keepdim true时保留被 Reduce 维度且维度值为 1keepdim false时删除被 Reduce 维度。aclnnStd 接口中out的 shape 必须与dim和keepdim推导结果一致。aclnnStd 接口中x和out的数据类型均需在支持列表内且x的数据类型需支持转换为out的数据类型ReduceStdV2 图算子的std、mean与x支持相同的数据类型范围。空 Tensor 场景返回 NaN当被 Reduce 维度的元素数shapeProd为 1 且shapeProd correction时返回 NaN当correction 1且shapeProd correction时返回 Inf。这些约束在 aclnnStd 的 aclnn_std.cpp 参数校验阶段被严格执行dtype 校验CheckDtypeValidx与out都必须在支持列表内且x的类型必须能转换为out的类型否则返回参数非法错误dim 校验CheckDimValid使用 64 位dimMask位图检查维度越界与重复负数维度先加selfDimNum归一化后再查重越界或重复均报Provided dim %ld must be in the range of [%ld, %ld]或Dim %ld appears multiple times in the list of dimsshape 校验CheckShape通过StdInferShape按dim与keepdim推导出期望的 reduce 后 shape再与out的实际 shape 比较不一致则校验失败。边界场景NaN 与 Inf 的语义ReduceStdV2 对三类无法正常计算的边界场景给出了明确语义且这一语义在 aclnnStd 的 GetWorkspaceSize 阶段就会提前填充输出避免真正进入 Kernel 计算场景输出空 TensorselfReshape-IsEmpty()填充 NaN单元素归约且shapeProd correction自由度 ≤ 0如shapeProd1, correction1填充 NaN多元素归约且correction 1且shapeProd correctioncorrection 覆盖了全部元素数填充 Inf对应 aclnn_std.cpp 中的三段分支代码if (selfReshape-IsEmpty()) { // 空tensor填充NAN ret CheckFillScalarShapeStdAndVar(out, NAN, uniqueExecutor.get()); ... } if ((shapeProd 1) (shapeProd correction)) { // 单元素规约且自由度小于等于0时返回NAN ret CheckFillScalarShapeStdAndVar(out, NAN, uniqueExecutor.get()); ... } if ((shapeProd 1) (correction 1) (shapeProd correction)) { // 多元素规约且correction覆盖规约元素个数时返回INF ret CheckFillScalarShapeStdAndVar(out, INFINITY, uniqueExecutor.get()); ... }其中shapeProd由CalcShapeProdStdAndVarMean计算表示被归约维度上的元素总数。这一行为与公式中的max(0, N - δN)完全自洽当自由度归零或为负时标准差在数学上无定义因此显式输出 NaN/Inf 而不是静默产生未定义结果。调用说明通过 aclnnStd 接口调用ReduceStdV2 在 aclnn 层的对外入口是aclnnStd采用 CANN 标准的两段式异步调用模式调用方式调用样例说明aclnn 调用examples/test_aclnn_std.cpp通过 aclnnStd 接口方式调用 ReduceStdV2 算子两段式接口定义在 op_api/aclnn_std.h// 第一段计算 workspace 大小并创建执行器 aclnnStatus aclnnStdGetWorkspaceSize(const aclTensor* self, const aclIntArray* dim, const int64_t correction, bool keepdim, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor); // 第二段执行计算 aclnnStatus aclnnStd(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream);完整调用示例解读以 examples/test_aclnn_std.cpp 为例其核心流程如下输入self为 shape{4, 2}的 float 张量dim {0}keepdim falsecorrection 1输出 shape 为{2}1. 初始化 ACL 环境aclInit→aclrtSetDevice(deviceId)→aclrtCreateStream(stream)2. 构造输入输出 Tensor调用aclrtMalloc申请 device 侧内存aclrtMemcpy将 host 数据拷贝到 device再用aclCreateTensor按 ND 格式、连续 strides 创建aclTensordim通过aclCreateIntArray(dimData.data(), 1)创建3. 第一段接口调用aclnnStdGetWorkspaceSize(self, dim, correction, keepdim, out, workspaceSize, executor)获取 workspace 大小与执行器随后按workspaceSize申请 device 内存4. 第二段接口调用aclnnStd(workspaceAddr, workspaceSize, executor, stream)异步下发计算5. 同步与取数aclrtSynchronizeStream(stream)等待执行完成再用aclrtMemcpy将 device 结果拷回 host 打印result[i]6. 资源释放依次销毁aclTensor、aclIntArray释放 device 内存与 stream最后aclFinalize。接口内部的计算链路从 aclnn_std.cpp 源码看aclnnStdGetWorkspaceSize内部先做参数校验与空指针/边界分支处理然后构造如下算子融合计算图self │ ├─ Reshape0维tensor转成shape[1] ├─ dim 归一化空指针/空列表 → 全维度负数 → 加 rank 转正 ├─ Contiguous将非连续输入转连续 ├─ ReduceMeankeepdimtrue先求均值 ├─ ReduceStdWithMean结合均值与 correction 求标准差 ├─ Cast必要时将结果转为 out 的 dtype └─ ViewCopy拷贝到 out兼容非连续输出其中ReduceMean使用keepdimtrue是为了让其中间输出能被ReduceStdWithMean直接消费避免为广播额外注册通用 Expand L0 算子。这一设计在头文件 aclnn_std.h 的 mermaid 调用链注释中也有完整描述。在IsRegBase()平台分支下则会走l0op::ReduceStdV2对应is_mean_outfalse直接完成计算再经Cast与ViewCopy输出。Kernel 实现原理两遍法与三种模板分支在 Kernel 侧入口为 op_kernel/reduce_std_v2_apt.cpp它根据 Tiling 阶段生成的PatternID / loopARCount / loopInnerARCount在编译期选择三种实现ReduceVarEmpty空归约场景直接以static_castDTYPE_X(NAN)作为初值填充输出对应 README 中空 Tensor 返回 NaN的语义ReduceVarPureMove纯搬移场景当PatternID / CONST10 CONST10时归约维度退化仅做数据搬移即可得到结果ReduceVarSch常规场景使用模板化的 Reduce 调度框架配合PromoteType由GetPromoteTypeDTYPE_X推导低精度类型提升到 float 参与累加以保证精度完成均值-标准差计算。两遍法Two-Pass核心算法常规分支的核心算法在 op_kernel/arch35/reduce_var_twopass.h 中采用经典的两遍法第一遍求均值第二遍求平方偏差和。关键数值技巧包括均值缩放meanScale 1.0 / nextR、meanCorrection nextR / RNum其中nextR FindNextPower2(RNum)。先用 2 的幂倒数缩放求和再乘以修正系数还原既避免了直接大数累加溢出又保持了精度防平方溢出第二遍计算(x - mean)²时先乘以varScale再平方Muls(y1Scale, y1, varScale)→Mul(y1Pow, y1Scale, y1)防止大数值平方溢出最后再统一还原二分累加DichotomyAdd对长维度归约采用二分法逐级合并部分和提高数值稳定性并减少累加误差isStdtrue时末尾开方Sqrt(var, var, pregMerge)将方差转为标准差输出isStdfalse时直接输出方差pad 清零在VFMeanVarTwoPassARPad*系列中对非 VL 对齐的尾部数据先清零再参与方差累加避免脏数据污染结果。Tiling 与单测验证Tiling 实现位于 op_host/arch35/reduce_std_v2_tiling_arch35.cpp复用math/reduce_var的ReduceVarTiling框架运行后通过GEN_REDUCE_TILING_KEY生成patternID / loopARCount / loopInnerARCount组合的 Tiling Key 并下发。仓库自带的单元测试 tests/ut/op_host/arch35/test_std_tiling.cpp 覆盖了多种典型场景可作为理解参数语义的活教材测试用例输入/属性验证要点ReduceStdV2_test_tiling_001shape{64,10240}、dtype FP16、dim{1}、correction8、keepdimtrue期望 Tiling Key 5143、workspace 16777216并逐字节比对 Tiling 数据ReduceStdV2_test_tiling_float32_dim1shape{32,128}、dtype FP32、dim{1}、correction1、keepdimtrueFP32 单维归约冒烟用例ReduceStdV2_test_tiling_keepdim_falseshape{8,16,32}、dim{1}、keepdimfalse输出 shape{8,32}验证不保留维度ReduceStdV2_test_tiling_multi_dim_correction_not_oneshape{4,8,16}、dim{1,2}、correction2、keepdimfalse多维归约且 correction ≠ 1此外 tests/ut/op_host/test_std_infershape.cpp 验证 shape 推导逻辑tests/ut/op_api/test_aclnn_std.cpp 验证 aclnn 调用链路。算子二进制发布配置在发布侧op_host/config/ascend950/reduce_std_v2_binary.json 按 dtype 组合声明了三份 Kernel 二进制ReduceStdV2_bfloat16_bfloat16、ReduceStdV2_float16_float16、ReduceStdV2_float32_float32三者均为 ND 格式、动态 shapeshape: [-2]属性dim / correction / keepdim / is_mean_out全部支持。这也印证了 README 中输入与输出均支持 FLOAT、FLOAT16、BFLOAT16Atlas 训练/推理系列除外的约束三份二进制恰好对应三种数据类型组合运行时按实际 dtype 匹配加载。小结ReduceStdV2 是 CANN ops-math 中一个典型的双输出归约算子在数学语义上通过correction同时兼容总体标准差与样本标准差在接口层通过 aclnnStd 两段式异步调用对外暴露并内置了完整的参数校验与 NaN/Inf 边界处理在实现层则借助ReduceMean ReduceStdWithMean的算子融合链路和 Kernel 侧的两遍法数值技巧在保证精度的同时兼顾了 NPU 的向量化执行效率。开发者可依据 examples/test_aclnn_std.cpp 的完整样例快速上手并参考 tests/ut/op_host/arch35/test_std_tiling.cpp 中的组合覆盖思路设计自己的验证用例。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表