ARTICLE DETAIL

资讯详情

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

CANN ops-nn 稀疏算子 SparseFillEmptyRows 详解:空行回补原理、参数约束与 GEIR 图模式调用实战

CANN ops-nn 稀疏算子 SparseFillEmptyRows 详解:空行回补原理、参数约束与 GEIR 图模式调用实战 CANN ops-nn 稀疏算子 SparseFillEmptyRows 详解空行回补原理、参数约束与 GEIR 图模式调用实战【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn导读本文基于 CANN ops-nn 开源算子库中 index/sparse_fill_empty_rows 目录的官方算子文档系统讲解SparseFillEmptyRows稀疏算子它专门用于解决稀疏张量Sparse Tensor中整行为空的问题——当稀疏表示中某一行没有任何非零元素时该行在稠密视角下会消失而本算子会在输出中为该空行补充一个取值为default_value的占位元素。读完本文你将掌握该算子的功能语义、全部 8 个输入输出的参数与数据类型约束、AI CPU 内核的 CSR 偏移填充算法原理以及如何通过 GEIR 图模式在 NPU 上完整调用并校验该算子。功能说明稀疏张量的空行补齐稀疏张量与空行问题稀疏张量Sparse Tensor用三个组件表示稠密张量Dense Tensor中少量非零元素indices[N, rank]的二维张量每一行是一个稀疏元素在稠密张量中的坐标values[N]的一维张量与indices逐行对应是各稀疏元素的值dense_shape[rank]的一维张量描述对应的稠密张量形状。由于稀疏表示只记录非零元素稠密张量中某一整行全部为零时该行在indices中不会出现任何条目这就是空行。空行会导致下游依赖逐行数据的算子如嵌入查找、按行规约、批次对齐等得到错误或缺失的行粒度结果。SparseFillEmptyRows 的核心语义按 README.md 的功能说明本算子对稀疏张量的**第 0 维行维度**逐行检查某行在输入中至少有一个稀疏元素 → 原样保留某行为空 → 在输出稀疏张量中为该行补充一个坐标为[row, 0, ..., 0]、取值为default_value的元素。补入的元素坐标除第 0 维为row外其余维度全部取 0。例如稠密形状[3, 4]中第 1 行为空则补入坐标[1, 0]。产品支持情况下表来自 README.md 的产品支持矩阵当前仓库实现所验证/支持的产品如下产品是否支持Ascend 950PR/Ascend 950DT√Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品√Atlas 训练系列产品√需要注意Atlas 200I/500 A2 推理产品当前不支持该算子实际部署前请以目标设备的支持矩阵为准。参数说明输入输出总览SparseFillEmptyRows共4 个输入、4 个输出与算子原型 sparse_fill_empty_rows_proto.h 中REG_OP(SparseFillEmptyRows)的声明一一对应参数名输入/输出/属性描述数据类型数据格式indices输入形状为[N, rank]的二维张量每一行表示一个稀疏元素在 dense tensor 中的坐标。INT64NDvalues输入形状为[N]的一维张量表示indices中每个稀疏元素对应的值。BOOL、COMPLEX128、COMPLEX64、DOUBLE、FLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT16、UINT32、UINT64、UINT8NDdense_shape输入形状为[rank]的一维张量表示稀疏张量对应 dense tensor 的形状dense_shape[0]表示行数。INT64NDdefault_value输入标量张量表示空行回补时写入y_values的默认值。BOOL、COMPLEX128、COMPLEX64、DOUBLE、FLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT16、UINT32、UINT64、UINT8NDy_indices输出回补空行后的稀疏索引形状为[N empty_row_count, rank]。INT64NDy_values输出回补空行后的稀疏值形状为[N empty_row_count]数据类型与values一致。BOOL、COMPLEX128、COMPLEX64、DOUBLE、FLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT16、UINT32、UINT64、UINT8NDempty_row_indicator输出形状为[dense_shape[0]]的一维布尔张量true表示对应行在输入稀疏张量中为空。BOOLNDreverse_index_map输出形状为[N]的一维张量reverse_index_map[i]表示输入第i个元素在输出中的位置。INT64ND关键参数深入解读indicesINT64唯一合法的行坐标类型是int64_t。其第一维N为稀疏元素个数第二维必须等于rank即dense_shape的元素数。注意稀疏表示中的值类型与坐标类型是分离的坐标一律为 INT64值类型则由values决定。values与default_value类型必须一致二者支持同一组数据类型BOOL、COMPLEX128、COMPLEX64、DOUBLE、FLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT16、UINT32、UINT64、UINT8覆盖了从 8 位整型到复数、半精度浮点的常见数值类型。default_value是标量零维而values是一维[N]。y_indices/y_values输出形状由输入N与空行数empty_row_count共同决定即N empty_row_count。y_values的数据类型与values保持一致。empty_row_indicatorBOOL长度为dense_shape[0]的一维布尔向量逐个标记每一行是否为空是分析稀疏度分布的重要中间结果。reverse_index_mapINT64长度为N建立输入元素 → 输出元素的映射。当有多个空行插入后输出中元素的下标与输入不再一一对应借助该映射可以反推每个原始稀疏元素在回补后的输出中的位置例如用于梯度回传时的 scatter 操作。约束说明以下约束来自 README.md 的约束说明并结合源码确认了其实现依据indices必须是二维张量第二维大小为rank。在 sparse_fill_empty_rows_infershape.cpp 中若indices最大形状的维度数不为 2shape 推导直接返回失败对应的 UT 用例InferShapeRangeFailedWhenIndicesAre1D/InferShapeRangeFailedWhenIndicesAre3D也验证了 1D、3D 输入均被拒绝。values的元素数必须与indices第一维N一致。dense_shape必须是一维张量元素数为rank且元素个数需在[1, gert::Shape::kMaxDimNum]范围内每个维度取值非负。indices[i, 0]必须满足0 indices[i, 0] dense_shape[0]。行坐标越界时AI CPU 内核会记录KERNEL_LOG_ERROR并返回KERNEL_STATUS_PARAM_INVALIDUT 用例row_index_out_of_range_fail覆盖了该场景。当dense_shape[0] 0时N必须为 0。内核中对此有显式校验dense_shape[0] 0 but indices.shape[0] Ndense_rows_zero_with_nonempty_indices_fail与dense_rows_zero_with_empty_indices_success两个用例分别覆盖失败与成功路径。values、default_value、y_values的数据类型需要保持一致。infershape 中对values与default_value的数据类型做了一致性校验不一致时报values and default_value must have the same dtype。本算子为 AI CPU 实现即计算在 Host 侧 CPU 完成而非 NPU 向量/矩阵核。底层实现原理AI CPU 内核的 CSR 偏移填充算法算子注册与入口算子的 AI CPU 实现位于 sparse_fill_empty_rows_aicpu.cpp类SparseFillEmptyRowsCpuKernel继承自CpuKernel通过REGISTER_CPU_KERNEL(SparseFillEmptyRows, ...)注册。入口Compute()首先做输入输出数量校验4 进 4 出随后依据values的数据类型走模板分发SPARSE_FILL_EMPTY_ROWS_DATA_TYPE_CASE宏最终调用模板函数ComputeSparseFillEmptyRowsT()。内核一次处理三个关键步骤第一步合法性检查与行计数。读取dense_shape[0]得到行数denseRows先拒绝负数行数若denseRows 0则要求N 0此时直接置输出形状为[0, rank]/[0]返回。随后遍历indices校验每个indices(i, 0)在[0, denseRows)内统计每行元素个数到csrOffset数组并顺带检测rowsAreOrdered输入行坐标是否按行号非递减有序。第二步生成空行指示与 CSR 前缀和。对每一行rowemptyRowIndicatorData[row] (csrOffset[row] 0)随后把每个计数至少钳到 1 并做前缀和得到每个输出元素应落到的位置区间——这正是 CSRCompressed Sparse Row格式的偏移思想也是补入空行后计算输出位置的关键。第三步填充输出。核心在FillValue()对每个原始元素i其输出位置 该行起始偏移 行内已填充计数随后拷贝该行的rank个坐标、拷贝值并写入reverseIndexMapData[i] outputI对每个空行row在起始偏移处写入坐标[row, 0, ..., 0]与defaultValue。快路径优化内核还内置了一个性能优化分支当allRowsFull rowsAreOrdered所有行都非空且输入行坐标有序时说明没有任何空行需要补入输出与输入完全一致此时直接copy_n批量拷贝indices、values并将reverse_index_map设为恒等映射[0, 1, ..., N-1]跳过逐元素填充显著降低常规场景无空行的开销。UT 用例all_rows_full_*系列覆盖全部 14 种数据类型专门验证了该快路径的输出不变性。数据类型推导与 shape 推导sparse_fill_empty_rows_graph_infer.cpp 完成图模式下输出数据类型推导y_indices固定 INT64、y_values继承values类型、empty_row_indicator固定 BOOL、reverse_index_map固定 INT64。sparse_fill_empty_rows_infershape.cpp 通过InferShapeRange推导输出 shape 范围y_indices/y_values的最大长度为dense_shape的总体元素数elementsNum即极端情况下每行都补入一个元素最小长度在elementsNum 0时为 0、否则为 1empty_row_indicator长度恒等于dense_shape[0]reverse_index_map长度范围与indices第一维范围一致。由于输出长度依赖dense_shape的实际数值推导声明了.InputsDataDependency({kDenseShapeIdx})即 shape 推导依赖第 2 个输入dense_shape的常量数据。调用方式GEIR 图模式调用与完整示例调用方式总览调用方式调用样例说明图模式调用test_geir_sparse_fill_empty_rows.cpp通过算子IR构图方式调用 SparseFillEmptyRows 算子。示例输入与预期输出官方示例 test_geir_sparse_fill_empty_rows.cpp 构造了如下输入indices [[0, 1], [2, 3]]N2, rank2values [10, 20]INT32dense_shape [3, 4]3 行 4 列default_value 1INT32 标量推理过程第 0 行有元素[0,1]第 1 行没有任何元素空行第 2 行有元素[2,3]。因此第 1 行需要回补补入坐标[1, 0]、值1输出排序后得到y_indices [0,1, 1,0, 2,3][N1, 2]y_values [10, 1, 20]第 1 行补入的1位于中间empty_row_indicator [false, true, false]reverse_index_map [0, 2]输入第 0 个元素 → 输出位置 0输入第 1 个元素 → 输出位置 2跳过被插入的空行元素这些期望值在示例的CheckOutput()中被逐一断言也在 test_sparse_fill_empty_rows.cpp 的geir_sample_success用例中复现。构图步骤拆解示例代码完整展示了 GEIR 图模式调用的标准流程核心步骤为构建输入 Tensor用BuildTensorint64_t/BuildScalarTensor构造indices、values、dense_shape与标量default_valueshape 分别为{2,2}、{2}、{2}、{}均指定ge::FORMAT_ND与ge::kPlacementHost。创建 Data 节点用ge::op::Data(name).set_attr_index(i)为每个输入创建图输入节点index从 0 到 3 依次对应indices、values、dense_shape、default_value。创建算子节点并接线ge::op::SparseFillEmptyRows(sparse_fill_empty_rows)后通过set_input_indices/set_input_values/set_input_dense_shape/set_input_default_value连接四个输入同时用update_output_desc_y_*预置 4 个输出的 TensorDesc{3,2}INT64、{3}INT32、{3}BOOL、{2}INT64。构图并运行graph.SetInputs(graphInputs).SetOutputs(graphOutputs)后通过ge::Session的AddGraph(0, graph)与RunGraph(0, inputs, outputs)在设备示例中ge.exec.deviceId0、ge.graphRunMode1在线推理模式上执行最后校验 4 个输出。该示例同时是图模式GEIR下调用本算子的最小可运行模板只需替换CreateGraph()中的输入数据即可复用到其他稀疏场景。测试与验证仓库为算子提供了两层单元测试内核级 UTtests/ut/op_kernel_aicpu/test_sparse_fill_empty_rows.cpp用NodeDefBuilder在 HOST 环境直接RUN_KERNEL覆盖三大类场景——fill_empty_row_*14 种数据类型的空行回补正确性、all_rows_full_*14 种数据类型的无空行快路径、geir_sample_success官方示例输入以及异常路径dense_rows_negative_fail负数行数、dense_rows_zero_with_nonempty_indices_fail0 行却有元素、row_index_out_of_range_fail行坐标越界、unsupported_dtype_fail不支持的数据类型均预期返回KERNEL_STATUS_PARAM_INVALID。infershape 级 UTtests/ut/op_host/test_sparse_fill_empty_rows_infershape.cpp验证 shape 推导的正确输出范围例如输入indices第一维范围[2,5]、dense_shape[3,4]时y_indices长度范围为[1, 12]、empty_row_indicator长度为 3以及 10 余个非法输入被拒绝的用例dtype 不符、indices非 2D、dense_shape空/非 1D/含负维度/超最大 rank/元素乘积溢出 int64 等同时验证dense_shape[0]0且N0时输出全为 0 维度的合法场景。参考资料算子官方文档index/sparse_fill_empty_rows/README.md算子原型GEIR IRop_graph/sparse_fill_empty_rows_proto.hAI CPU 内核实现op_kernel_aicpu/sparse_fill_empty_rows_aicpu.cppshape 推导op_host/sparse_fill_empty_rows_infershape.cpp数据类型推导op_graph/sparse_fill_empty_rows_graph_infer.cppGEIR 调用示例examples/test_geir_sparse_fill_empty_rows.cpp单元测试tests/ut/op_kernel_aicpu/test_sparse_fill_empty_rows.cpp、tests/ut/op_host/test_sparse_fill_empty_rows_infershape.cpp【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表