CANN ops-math GroupedBiasAddGrad 算子深入解析:分组偏置梯度归约的原理、约束与双模式调用实践
算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载GroupedBiasAddGrad 是 CANN ops-math 数学算子库仓库根目录math/下提供的分组偏置加法GroupedBiasAdd反向计算算子其核心任务是对反向传播梯度gradY按分组通道做归约求和得到 bias 的梯度。本文以 math/grouped_bias_add_grad/README.md 为主体结合算子定义、Infershape、Tiling、Kernel 与示例源码系统讲解该算子的功能公式、参数语义、约束条件、产品支持情况并给出 aclnn 单算子调用与图模式GEIR调用两种完整可运行的工程实践帮助你在 Ascend 平台上正确、高效地使用该算子完成分组 bias 反向梯度计算。一、算子定位与功能概述GroupedBiasAddGrad 是分组偏置加法GroupedBiasAdd的反向传播算子。在前向过程中不同输入通道共享一个分组 biasgrouped bias反向传播时则需要把上层回传的梯度按分组边界累加归约还原出每个分组对应的 bias 梯度供 bias 参数更新使用。算子的核心行为可以概括为一句话对分组通道的偏置梯度进行归约求和。其输入输出约束为当提供了可选输入groupIdxOptional即分组信息时gradY必须为 2 维张量输出out为 2 维张量当不提供groupIdxOptional时gradY必须为 3 维张量输出out降为 2 维。从仓库源码看该算子的 IR 定义proto位于 op_graph/grouped_bias_add_grad_proto.h注册名为GroupedBiasAddGrad输入为grad_y类型 T可选输入group_idxINT32/INT64输出grad_bias与grad_y同类型 T属性group_idx_type默认值为 0其中 T 支持DT_FLOAT16、DT_BF16、DT_FLOATREG_OP(GroupedBiasAddGrad) .INPUT(grad_y, T) .OPTIONAL_INPUT(group_idx, TensorType({DT_INT32, DT_INT64})) .OUTPUT(grad_bias, T) .ATTR(group_idx_type, Int, 0) .DATATYPE(T, TensorType({DT_FLOAT16, DT_BF16, DT_FLOAT})) .OP_END_FACTORY_REG(GroupedBiasAddGrad)宿主侧算子定义见 op_host/grouped_bias_add_grad_def.cpp它同时注册了ascend910b、ascend910_93、ascend950、ascend350等多个 AICore 配置并声明了动态 Rank、动态 Shape、动态编译静态化等能力。二、产品支持情况根据算子 README 与 aclnn 接口文档GroupedBiasAddGrad 在以下产品上可用产品是否支持Ascend 950PR / Ascend 950DT√Atlas A3 训练系列产品 / Atlas A3 推理系列产品√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×也就是说该算子面向的是 A2/A3/950 系列训练与推理产品对应ascend910b、ascend910_93、ascend950、ascend350等平台配置较老或轻量级的推理产品暂不支持。三、功能说明与计算公式gradY按分组边界沿第 0 维累加最终输出out的 shape 为(G, H)其中H为gradY最后一维大小。具体分三种场景3.1 有可选输入 groupIdxOptional且 groupIdxType 为 0结束索引模式groupIdxOptional(j)直接表示第j组的结束位置索引$$ out(G,H) \begin{cases} \displaystyle \sum_{i \mathrm{groupIdxOptional}(j-1)}^{\mathrm{groupIdxOptional}(j)} \mathrm{gradY}(i, H), 1 \leq j \leq G-1 \[8pt] \displaystyle \sum_{i 0}^{\mathrm{groupIdxOptional}(j)} \mathrm{gradY}(i, H), j 0 \end{cases} $$其中G表示groupIdxOptional第 0 维的大小即该张量共有G个数。3.2 有可选输入 groupIdxOptional且 groupIdxType 为 1分组大小模式groupIdxOptional(j)表示第j组的大小需要先做前缀和得到每个分组的实际结束位置$$ groupIdx(i) \sum_{i0}^{j} groupIdxOptional(j), \quad j0...G $$$$ out(G,H) \left{ \begin{aligned} \sum_{i,,\mathrm{groupIdx}(j-1)}^{\mathrm{groupIdx}(j)} \mathrm{gradY}(i, H), 1 \leq j \leq G-1 \ \sum_{i,,0}^{\mathrm{groupIdx}(j)} \mathrm{gradY}(i, H), j 0 \end{aligned} \right. $$3.3 无可选输入 groupIdxOptional等分模式此时gradY为 3 维(G, C, H)即隐含地把第 0 维G视为分组数、第 1 维C视为每组行数每组大小均等$$ out(G, H) \sum_{i0}^{C} gradY(G, i, H) $$3.4 计算示例接口文档给出了三组直观示例详见 docs/aclnnGroupedBiasAddGradV2.mdgroupIdxType 为 0gradYshape 为(1000, 30)groupIdxOptional为(400, 600, 1000)将gradY分为 3 组每组累加行数依次为 400、200、400输出outshape 为(3, 30)groupIdxType 为 1gradYshape 为(1000, 30)groupIdxOptional为(400, 210, 390)每组累加行数依次为 400、210、390输出outshape 为(3, 30)无 groupIdxOptionalgradYshape 为(10, 100, 30)等分为 10 组、每组 100 行输出outshape 为(10, 30)。四、参数说明4.1 算子级参数对应 README 参数表参数名输入/输出/属性描述数据类型数据格式gradY输入反向传播梯度公式中的输入 gradY支持非连续的 TensorFLOAT16、BFLOAT16、FLOATNDgroupIdxOptional可选输入每个分组结束位置公式中的输入 groupIdxOptional最多支持 2048 个组支持非连续的 TensorINT32、INT64NDout输出bias 的梯度公式中的 outFLOAT16、BFLOAT16、FLOATNDgroupIdxType可选属性表示 groupIdx 的类型0 表示 groupIdxOptional 中的值为每个 group 的结束索引1 表示 groupIdxOptional 中的值为每个 group 的大小Int-4.2 aclnn 接口参数GetWorkspaceSize 阶段在 docs/aclnnGroupedBiasAddGrad.md 中第一段接口的参数语义被进一步细化参数名输入/输出描述使用说明数据类型数据格式shape非连续 TensorgradY输入反向传播梯度有 groupIdxOptional 时 shape 仅支持 2 维无 groupIdxOptional 时 shape 仅支持 3 维FLOAT、FLOAT16、BFLOAT16ND2-3√groupIdxOptional输入每个分组结束位置只支持 1 维INT32、INT64ND1√out输出bias 的梯度数据类型必须与 gradY 一致shape 仅支持 2 维输入为三维(G, C, H)时输出为(G, H)输入为二维(GB, H)、group_idx 为(G)时输出为(G, H)FLOAT、FLOAT16、BFLOAT16ND2√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----4.3 V2 接口新增的 groupIdxType 参数aclnnGroupedBiasAddGradV2 是基础接口的功能扩展新增了groupIdxType属性int64_t类型以显式指定 groupIdx 的语义0groupIdxOptional中的值为每个 group 的结束索引与基础接口默认行为一致1groupIdxOptional中的值为每个 group 的大小需在算子内部做前缀和换算。这一点在宿主侧代码中也有体现属性group_idx_type在 grouped_bias_add_grad_def.cpp 中被声明为Attr(group_idx_type).AttrType(OPTIONAL).Int(0)aclnn 入口在 op_api/aclnn_grouped_bias_add_grad.cpp 中基础接口aclnnGroupedBiasAddGradGetWorkspaceSize内部直接以固定值0调用公共执行函数而 V2 接口则透传调用方的groupIdxType。五、约束说明使用前必读结合 README 与接口文档使用该算子必须满足以下约束存在输入 group_idx 时gradY仅支持 2 维形状张量数值必须非负且不超过 INT32 最大值当groupIdxType为 0 时groupIdxOptional数据必须按升序排列且最后一个数值等于gradY第 0 维的大小当groupIdxType为 1 时groupIdxOptional数值的总和必须等于gradY第 0 维的大小不存在输入 group_idx 时gradY仅支持 3 维形状组数上限groupIdxOptional最多支持 2048 个组常量MAX_GROUP_NUM 2048在 op_host/grouped_bias_add_grad_infershape.cpp 与 aclnn 校验逻辑中均有定义确定性计算aclnnGroupedBiasAddGrad 与 V2 接口默认均为确定性实现多次运行结果一致输出 shape 关系三维输入(G, C, H)输出(G, H)二维输入(GB, H)配合 group_idx(G)输出(G, H)。这些约束在 aclnn 入参校验中会触发对应的返回码空指针返回ACLNN_ERR_PARAM_NULLPTR错误码 161001数据类型/维度不在支持范围、维度关系不匹配、组数超过 2048、groupIdxType取值非法非 0 或 1时返回ACLNN_ERR_PARAM_INVALID错误码 161002。校验逻辑可在 op_api/aclnn_grouped_bias_add_grad.cpp 中查看例如CheckShapeValid会逐一比对gradY、groupIdxOptional、out的维数、组数groupNum与H维大小。六、调用方式与完整示例README 给出了两种调用路径调用方式调用样例说明aclnn 调用test_aclnn_grouped_bias_add_grad.cpp通过 aclnnGroupedBiasAddGrad 接口方式调用图模式调用test_geir_grouped_bias_add_grad.cpp通过算子 IR 构图方式调用6.1 aclnn 两段式接口调用aclnn 算子采用两段式接口Two-Phase API先调用aclnnGroupedBiasAddGradGetWorkspaceSize完成入参校验、构图并返回 workspace 大小与执行器再调用aclnnGroupedBiasAddGrad真正执行计算。两个接口的原型如下aclnnStatus aclnnGroupedBiasAddGradGetWorkspaceSize( const aclTensor* gradY, const aclTensor* groupIdxOptional, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnGroupedBiasAddGrad( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)V2 接口仅在第一段多一个int64_t groupIdxType参数aclnnStatus aclnnGroupedBiasAddGradV2GetWorkspaceSize( const aclTensor *gradY, const aclTensor *groupIdxOptional, int64_t groupIdxType, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)从底层实现看op_api/aclnn_grouped_bias_add_grad.cpp第一段接口会依次完成参数检查 → 创建OpExecutor→ 空 Tensor 快速返回 → 对非连续输入gradY、groupIdxOptional调用l0op::Contiguous转连续 → 调用l0op::GroupedBiasAddGrad构造计算图 → 对非连续输出out用l0op::ViewCopy回写 → 返回 workspace 大小。第二段接口则统一通过CommonOpExecutorRun在指定stream上执行。下面给出一个基于 examples/test_aclnn_grouped_bias_add_grad.cpp 的可运行示例gradY为(40, 10)的 FLOAT 张量groupIdxOptional {5, 15, 30, 40}即 groupIdxType 为 0 的结束索引模式#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_grouped_bias_add_grad.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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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初始化根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出 std::vectorint64_t gradYShape {40, 10}; std::vectorint64_t groupIdxShape {4}; std::vectorint64_t outShape {4, 10}; void* gradYDeviceAddr nullptr; void* groupIdxDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* gradY nullptr; aclTensor* groupIdx nullptr; aclTensor* out nullptr; std::vectorfloat gradYHostData(400, 1.0); std::vectorint32_t groupIdxHostData {5, 15, 30, 40}; // groupIdxType0 结束索引模式 std::vectorfloat outHostData(40, 0.0); ret CreateAclTensor(gradYHostData, gradYShape, gradYDeviceAddr, aclDataType::ACL_FLOAT, gradY); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(groupIdxHostData, groupIdxShape, groupIdxDeviceAddr, aclDataType::ACL_INT32, groupIdx); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用第一段接口获取workspace大小与执行器 uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnGroupedBiasAddGradGetWorkspaceSize(gradY, groupIdx, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(GetWorkspaceSize 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); } // 4. 调用第二段接口执行计算 ret aclnnGroupedBiasAddGrad(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnGroupedBiasAddGrad failed. ERROR: %d\n, ret); return ret); // 5. 同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 6. 将device侧结果拷贝回host并打印 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result 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]); } // 7. 释放资源 aclDestroyTensor(gradY); aclDestroyTensor(groupIdx); aclDestroyTensor(out); aclrtFree(groupIdxDeviceAddr); aclrtFree(outDeviceAddr); aclrtFree(gradYDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }需要说明的是上述示例中groupIdxHostData {5, 15, 30, 40}为结束索引模式即 4 组分别累加 gradY 的第 0~4 行、5~14 行、15~29 行、30~39 行若改用 V2 接口并设置groupIdxType 1则groupIdxHostData应传分组大小如{5, 15, 10, 10}其总和必须等于gradY第 0 维大小 40可参见 docs/aclnnGroupedBiasAddGradV2.md 中的 V2 调用示例完整的编译与运行流程CMake 配置、链接 aclnnop 库等请参考编译与运行样例aclnn 返回码的详细定义见aclnn 返回码。6.2 图模式GEIR调用除 aclnn 单算子调用外还可以通过 GE 图接口以构图方式调用。样例见 examples/test_geir_grouped_bias_add_grad.cpp核心构图逻辑如下// 自定义代码添加单算子定义到图中 auto add1 op::GroupedBiasAddGrad(add1); std::vectorint64_t gradYShape {100, 256}; std::vectorint64_t groupIdxShape {3}; // 添加 grad_y 输入FLOAT 类型2 维 ADD_INPUT(1, grad_y, inDtype, gradYShape); // 添加 group_idx 可选输入INT32 类型1 维 ADD_INT_INPUT(2, group_idx, DT_INT32, groupIdxShape); // 设置 group_idx_type 属性 ADD_INPUT_ATTR(group_idx_type, 0); outputs.push_back(add1);随后通过ge::Graph、ge::Session完成图的构建与运行GEInitialize初始化 GE 全局环境 →CreateOppInGraph构图 →session-AddGraph加入计算图 →session-RunGraph执行并回读输入/输出数据。与 aclnn 路径相比图模式适合在已有 GE 计算图例如网络整图下沉中嵌入该算子。七、从源码看算子的实现路径7.1 Infershape输出形状的推导规则op_host/grouped_bias_add_grad_infershape.cpp 实现了形状推导gradY为 3 维且无group_idx时输出为(G, H)其中H gradY.shape[2]gradY为 2 维且有group_idx时输出为(G, H)其中G group_idx.shape[0]、H gradY.shape[1]当group_idx.shape[0]已知且大于 2048 时推导直接失败并报错。7.2 Tiling核间与核内数据切分Tiling 数据结构的定义见 op_host/arch22/grouped_bias_add_grad_tiling_def.h关键字段包括usedCoreNum/normalCoreNum/tailCoreProcessNum使用的核数、整核个数与尾核处理量normalCoreProcessNum/tailCoreProcessNum整核与尾核分别处理的 GH 大小dimG/dimC/dimH/dimGB输入张量各维大小baseH/baseC单次 UB 处理的 H、C 方向个数loopCNum每个核的 UB 循环次数wsUnitNumworkspace 上每个核存放 UB 累加结果的个数groupIdxType透传的分组语义属性。不同架构arch22 / arch35下 Tiling 策略有专门实现例如 op_host/arch35/grouped_bias_add_grad_RA_tiling_arch35.cpp、op_host/arch35/grouped_bias_add_grad_ARA_tiling_arch35.cppREADME 与本文不再展开细节。7.3 Kernel模板分发与多场景 Kernel 实现Kernel 入口为 op_kernel/grouped_bias_add_grad.cpp入口函数grouped_bias_add_grad通过 TilingKey 宏进行场景分发从源码结构看主要区分了三类实现等分模式EqualCTHREE_DIMS_*系列对应无groupIdxOptional的 3 维输入场景调用 arch22/grouped_bias_add_grad_equal_c.h 中的GroupedBiasAddGradEqualC不等分模式UnequalCTWO_DIMS_*系列对应有groupIdxOptional的 2 维输入场景调用 arch22/grouped_bias_add_grad_unequal_c.h 中的GroupedBiasAddGradUnequalC性能优化路径Perf*_PERF系列调用 arch22/grouped_bias_add_grad_unequal_c_perf.h 中的GroupedBiasAddGradUnequalCPerf。TilingKey 编码还区分了数据类型FLOAT16 / FLOAT / BFLOAT16、是否使用 UBUSE_UB或 workspaceUSE_WS以及 group_idx 的元素类型INT32 / INT64arch35 平台则提供了 grouped_bias_add_grad_cut_gh.h、grouped_bias_add_grad_cut_h.h 等更细粒度的切分策略。Kernel 内部先通过SetSysWorkspace设置系统 workspace再从 tiling 数据中取出用户 workspaceGetUserWorkspace用于跨核累加结果的暂存。7.4 测试与验证仓库为该算子提供了完整的测试体系可据此验证理解与实际运行结果单算子功能测试tests/ut/op_kernel/test_grouped_bias_add_grad.cpp配套数据生成脚本 tests/ut/op_kernel/grouped_bias_add_grad_data/gen_data.py 与 golden 脚本 tests/assets/golden.pyInfershape / Tiling 单测tests/ut/op_host/arch22/test_grouped_bias_add_grad_infershape.cpp、tests/ut/op_host/arch22/test_grouped_bias_add_grad_tiling.cpp 等aclnn 接口单测tests/ut/op_api/test_aclnn_grouped_bias_add_grad.cppST 用例tests/st/aclnnGroupedBiasAddGrad/atk_aclnnGroupedBiasAddGrad.json 与执行脚本 tests/st/aclnnGroupedBiasAddGrad/executor_aclnnGroupedBiasAddGrad.py。八、总结与使用建议GroupedBiasAddGrad 是 CANN ops-math 中面向分组偏置反向传播的高效归约算子其核心是把梯度沿分组边界累加选择接口需要显式指定分组语义结束索引或分组大小时用 aclnnGroupedBiasAddGradV2否则用 aclnnGroupedBiasAddGrad核对约束注意 2 维/3 维输入与groupIdxOptional的搭配、组数不超过 2048、groupIdxOptional数值非负且不超过 INT32 上限并按groupIdxType保证升序类型 0或总和正确类型 1按需验证在接入业务前可先运行 tests/ut 下的单元测试与 examples 下的两个样例确认目标产品Ascend 950、Atlas A2/A3 系列上的行为符合预期。把握住分组归约求和这一核心语义与上述约束即可在 NPU 训练/推理场景中正确使用该算子完成 grouped bias 的反向梯度计算。赞分享算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载相关推荐BatchToSpaceND 算子深度解析原理、约束与 NPU 图模式调用实践CANN ops-mathBatchToSpaceND 算子深度解析原理、约束与 NPU 图模式调用实践CANN ops math BatchToSpaceND 是 CANN op算子库人工智能CANNCANN ops-math AsinGrad 算子详解反正弦梯度算子的实现原理、约束与图模式调用CANN ops math AsinGrad 算子详解反正弦梯度算子的实现原理、约束与图模式调用 导读 AsinGrad 是 CANN ops math 数学算子库人工智能CANNCANN ops-math BiasAddGrad 算子深度解析偏置梯度计算的原理、参数与图模式调用实践CANN ops math BiasAddGrad 算子深度解析偏置梯度计算的原理、参数与图模式调用实践 本文以 CANN 开源数学算子库 ops math算子库人工智能CANN创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考