CANN ops-nn 中 GaussianNllLossGrad 算子的梯度计算原理与 ACLNN 调用实战
2026/9/20 23:46:17 网站建设 项目流程
  • 人工智能
  • 算子库
  • 深度学习
  • CANN
  • Ascend

【免费下载链接】ops-nn

本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。

项目地址:https://gitcode.com/cann/ops-nn
点击查看免费下载

GaussianNllLossGrad 是 CANN ops-nn 神经网络算子库中 GaussianNLLLoss 损失函数的反向算子,用于在 NPU(Atlas A2 训练系列产品 / Atlas 800I A2 推理产品)上同时计算损失对均值预测input与方差var的梯度。本文以 关联文档 为骨架,结合仓库内的算子定义、tiling 与 kernel 源码及测试用例,深入讲解其数学公式、广播与规约语义、ACLNN 两段式调用方式、约束条件与本地编译验证方法,帮助读者既能在业务中正确调用该算子,也能从源码层面理解其实现机制。

产品支持情况与定位

GaussianNllLossGrad 属于实验目录(experimental/loss)下的反向算子,产品支持情况如下:

产品是否支持
Atlas A2 训练系列产品/Atlas 800I A2 推理产品

从仓库结构看,该算子位于 experimental/loss/gaussian_nll_loss_grad 目录,与同目录下其他算子一致,采用标准的四段式工程结构:docs(接口文档)、examples(ACLNN 调用样例)、op_host(算子定义、shape 推导与 tiling)、op_kernel(AscendC 核函数实现)、tests(UT 测试)。算子注册使用的 AICore 配置为ascend910b(见 gaussian_nll_loss_grad_def.cpp),与文档所述 Atlas A2 系列平台对应。

功能说明:梯度数学原理

GaussianNLLLoss 的前向损失为负对数高斯似然,其反向算子 GaussianNllLossGrad 计算损失对input(均值预测)和var(方差)的梯度,不计算对target的梯度。设d = input - targetv = max(var, eps),则每个逻辑元素的梯度为:

gradInput = gradOutput * d / v gradVar = gradOutput * 0.5 * (1 / v - d² / v²)

几个关键语义点:

  • reduction="mean"时两项梯度额外乘以1/N,其中Ninput的逻辑元素数;
  • reductiongradOutput的关系"sum""mean"接收单元素标量gradOutput(上游已聚合的标量梯度),"none"接收与input同 shape 的逐元素梯度;
  • var广播时的归约:当var以广播形式参与计算时,gradVar需要把每个广播维度上的贡献求和,归约回原始varshape 输出;
  • full仅保持前后向接口一致:该属性对应前向中“是否包含完整高斯常数项”的开关,但不影响梯度计算结果;
  • 低精度转 FLOAT 计算:FLOAT16 与 BFLOAT16 输入会先转换为 FLOAT 参与运算,结果再转换回输入 dtype 输出,以保证数值精度。

上述公式在 op_kernel/gaussian_nll_loss_grad.h 中可直接验证:ComputeGradInput(第 193–208 行)依次执行Sub(input, target)得到dMaxs(var, eps)得到vDiv(d, v)Mul(gradOutput),与gradInput = gradOutput * d / v完全对应;ComputeGradVar(第 210–235 行)则先计算d² / v²1 / v的差,再乘gradOutput0.5,与gradVar公式一致。

参数说明

算子的完整参数定义见 gaussian_nll_loss_grad_def.cpp,其中 4 个输入、3 个属性与 2 个输出的数据类型、格式与 shape 规格如下:

参数名输入/输出/属性描述数据类型数据格式Shape 规格
gradOutput输入上游梯度。FLOAT、FLOAT16、BFLOAT16NDnone时与input相同;sum/mean时为单元素标量。
input输入Gaussian 分布均值预测。gradOutput相同ND任意维静态 shape。
target输入目标值。gradOutput相同NDinput相同,或同 rank 且恰有一个广播维为 1。
var输入方差。gradOutput相同NDinput相同、最后一维为 1、缺少最后一维,或单元素标量。
full属性是否包含完整高斯常数项;不影响梯度。默认falseBOOL--
eps属性方差下限。默认1e-6FLOAT-必须大于 0。
reduction属性规约方式,默认"mean"STRING-"none""mean""sum"
gradInput输出input的梯度。gradOutput相同NDinput相同。
gradVar输出var的梯度;广播贡献已归约。gradOutput相同NDvar相同。

在算子定义源码中,四个输入与两个输出均声明为REQUIRED,数据类型限定为{ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16},格式限定为FORMAT_ND;三个属性full(默认false)、eps(默认1e-6)、reduction(默认"mean")均为 OPTIONAL。所有输入输出均标记了AutoContiguous(),保证 NPU 侧按连续内存访问。

广播语义的源码级分类

targetvar的广播形式在 tiling 阶段被精确分类并编码进 tiling 数据,见 gaussian_nll_loss_grad_tiling.cpp:

  • target分类(ClassifyTarget,第 73–102 行):仅允许两种形式——与input完全同 shape(BROADCAST_NONE),或同 rank 且恰有一个维度为 1(BROADCAST_TARGET_AXIS,记录广播轴大小targetAxisSize与该轴之后的内侧步长targetInnerStride);
  • var分类(ClassifyVar,第 117–151 行):支持四种形式——与input同 shape(VAR_SAME)、最后一维为 1(VAR_LAST_DIM_ONE)、缺少最后一维即前缀匹配(VAR_MISSING_LAST_DIM)、单元素标量(VAR_SCALAR),并记录每个var元素对应的归约规模varReduceSize

对应地,tiling 数据结构 gaussian_nll_loss_grad_tiling_data.h 中保存了targetBroadcastModetargetBroadcastAxisSizetargetInnerStridevarBroadcastModevarReduceSize等字段。该分类逻辑在 UT test_gaussian_nll_loss_grad_tiling.cpp 中通过 4 组典型 case(同 shape、target 单维广播、var 缺末维、var 标量)逐一断言验证。

约束说明

结合 README.md 与 aclnnGaussianNllLossGrad.md 的约束章节,使用该算子需满足:

  • 仅支持 Atlas A2 平台、ND 数据格式,以及 FLOAT、FLOAT16、BFLOAT16 三种数据类型;
  • 所有输入与输出的 dtype 必须一致;实现不创建 dtype-only tiling key(即同一 tiling 逻辑复用于三种 dtype,计算前统一转 FLOAT);
  • target仅允许同 shape,或在一个维度上从 1 广播到input
  • var仅允许同 shape、最后一维为 1、缺少最后一维或单元素标量;
  • eps必须大于 0;var的值约束为非负;
  • clamp(v = max(var, eps))对梯度透明:即使var < epsgradVar仍按 clamp 后的v参与公式计算;
  • full不改变gradInputgradVar的任何结果;
  • input不产生gradInput元素;存在单元素var时(广播到空 input 之外),gradVar为 0;
  • "none"gradOutput必须与input同 shape,"sum"/"mean"gradOutput必须为单元素标量;
  • 输出gradInputgradVar分别严格匹配inputvar的 shape;动态未知维在 tiling 前必须具体化。

这些校验在 tiling 阶段全部落地:ValidateDtypes(tiling 源码)检查所有输入输出 dtype 一致且属于受支持集合;ClassifyTarget/ClassifyVar校验广播合法性;eps <= 0reduction非法字符串、gradOutput shape 与 reduction 不匹配等均返回GRAPH_FAILED并记录错误日志(第 216–242 行)。

调用说明

当前仓库对该算子提供ACLNN 调用(两段式接口),暂不提供 GE 与 PyTorch 直接调用方式。

调用方式调用样例说明
ACLNN 调用接口文档、样例两段式接口。
GE-暂不提供。
PyTorch-暂不提供。

两段式接口与函数原型

ACLNN 采用与 CANN 其他算子一致的两段式调用模式(详见 两段式接口说明):先调用aclnnGaussianNllLossGradGetWorkspaceSize完成算子准备并获取 workspace 大小与执行器,再调用aclnnGaussianNllLossGrad真正执行计算。

aclnnStatus aclnnGaussianNllLossGradGetWorkspaceSize( const aclTensor* gradOutput, const aclTensor* input, const aclTensor* target, const aclTensor* var, bool full, float eps, const char* reduction, aclTensor* gradInput, aclTensor* gradVar, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnGaussianNllLossGrad( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)

第一段接口各参数的详细说明(来自 aclnnGaussianNllLossGrad.md):

参数名输入/输出描述使用说明数据类型数据格式维度(shape)
gradOutput(aclTensor*)输入上游梯度。非空;dtype 与其他张量一致。FLOAT、FLOAT16、BFLOAT16NDnone 时与 input 相同;sum/mean 时单元素。
input(aclTensor*)输入均值预测。非空指针。FLOAT、FLOAT16、BFLOAT16ND任意维静态 shape。
target(aclTensor*)输入目标值。非空;可按一个 size-1 维广播。FLOAT、FLOAT16、BFLOAT16ND同 input,或同 rank 且一个维度为 1。
var(aclTensor*)输入非负方差。非空;支持限定广播。FLOAT、FLOAT16、BFLOAT16ND同 input、最后一维为 1、缺少最后一维或单元素。
full(bool)输入保留的前向一致性属性。不影响梯度;默认 false。BOOL--
eps(float)输入方差下限。必须大于 0;默认 1e-6。FLOAT--
reduction(char*)输入规约模式。none、sum 或 mean;默认 mean。STRING--
gradInput(aclTensor*)输出input 梯度。非空;dtype 与 gradOutput 一致。FLOAT、FLOAT16、BFLOAT16ND与 input 相同。
gradVar(aclTensor*)输出var 梯度。非空;dtype 与 gradOutput 一致。FLOAT、FLOAT16、BFLOAT16ND与 var 相同。
workspaceSize(uint64_t*)输出返回 Device workspace 大小。----
executor(aclOpExecutor**)输出返回 op 执行器。----

第二段接口参数:workspace(Device workspace 地址,输入)、workspaceSize(由第一段接口获取,输入)、executor(第一段接口返回的执行器,输入)、stream(执行任务的 Stream,输入)。

返回值aclnnStatus状态码,具体含义参见 aclnn 返回码说明。输入为空指针、dtype 不一致、shape/广播不合法、eps <= 0reduction非法时,第一段接口返回错误。

需要特别说明的是:虽然该算子当前实现不申请用户 workspace(tiling 中workspace[0] = 0,见 gaussian_nll_loss_grad_tiling.cpp),但框架仍按两段式接口返回实际 workspace 大小,调用方应统一按workspaceSize分配并传入(样例代码对workspaceSize > 0才执行aclrtMalloc)。

最小调用示例

仓库提供的完整可运行样例见 test_aclnn_gaussian_nll_loss_grad.cpp,其核心调用片段如下(full=trueeps=1e-6reduction="none"):

uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; aclnnStatus ret = aclnnGaussianNllLossGradGetWorkspaceSize( gradOutput, input, target, var, false, 1e-6f, "mean", gradInput, gradVar, &workspaceSize, &executor); if (ret == ACL_SUCCESS) { ret = aclnnGaussianNllLossGrad(workspace, workspaceSize, executor, stream); }

样例的运行流程可以归纳为五步,同时也是验证算子正确性的完整闭环:

  1. 环境初始化aclInitaclrtSetDevice(0)aclrtCreateStream
  2. 张量构造:通过CreateTensor在 Device 上aclrtMallocaclrtMemcpy数据,再用aclCreateTensor按 ND 格式创建aclTensor。样例采用inputShape={2,3}targetShape={2,1}varShape={2,1},即targetvar各在第二维上从 1 广播到 3 的典型场景;
  3. 两段式调用:先GetWorkspaceSize获取 workspace 与 executor,必要时分配 workspace,再执行aclnnGaussianNllLossGrad,最后aclrtSynchronizeStream同步;
  4. 结果回拷:将gradInputgradVar通过ACL_MEMCPY_DEVICE_TO_HOST拷回 Host 并打印;
  5. Golden 对比验证:样例在 Host 侧按公式d = input - targetv = max(var, eps)手工计算goldenInputgoldenVar,其中gradVar按行累加广播贡献,再与 NPU 输出逐元素比较最大误差,阈值1e-5内判定PASS,最终按passed ? 0 : 1作为进程退出码。

值得注意的是样例中gradOutputHost使用reduction="none"时与input同 shape 的 6 个元素,且 Golden 计算中goldenVar[row] += ...的累加逻辑与 kernel 中ProcessBroadcastGradVar的归约行为一致,可作为理解广播归约语义的直观参照。

本地编译运行 UT

该算子已纳入 CANN 的 build.sh 单算子构建体系,可分别构建 host(tiling/infershape)与 kernel 两部分。先 source 匹配的 CANN 环境(仓库 README 以 cann-9.0.0 为例),再执行:

source /usr/local/Ascend/cann-9.0.0/set_env.sh bash build.sh -u --ophost --ops=gaussian_nll_loss_grad --soc=ascend910b --experimental bash build.sh -u --opkernel --ops=gaussian_nll_loss_grad --soc=ascend910b --experimental

命令要点:

  • --ops=gaussian_nll_loss_grad指定单算子构建范围,避免全量编译;
  • --soc=ascend910b与算子定义中AddConfig("ascend910b")一致;
  • --experimental表示该算子位于 experimental 实验目录;
  • -u表示构建并运行 UT(单元测试)。

仓库内的 UT 覆盖情况:Host 侧包含 test_gaussian_nll_loss_grad_infershape.cpp(验证输出 shape 继承input/var、dtype 继承gradOutput,对应 infershape 实现)与 test_gaussian_nll_loss_grad_tiling.cpp(覆盖 4 种广播组合、reduction 校验、workspace=0、tilingKey=0);Kernel 侧 test_gaussian_nll_loss_grad.cpp 通过 tikicpulib 的ICPU_RUN_KF在 CPU 上仿真运行核函数,并对 FLOAT/Half/BF16 三种 dtype 分别设置容差(如 Half 容差2e-2),配合 gen_data.py 与 compare_data.py 进行数据生成与结果比对。

本地自测 UT 覆盖率

若普通 UT 已通过且本机安装了lcov,可附加--cov生成代码覆盖率报告:

bash build.sh -u --ophost --ops=gaussian_nll_loss_grad --soc=ascend910b --experimental --cov bash build.sh -u --opkernel --ops=gaussian_nll_loss_grad --soc=ascend910b --experimental --cov

该参数对 host 与 kernel 两个构建目标分别统计行覆盖率,便于在新增用例时评估测试充分度。

eager 调用前置条件

如需在 eager 模式下运行样例(如带 Python 前端或自定义 vendor 的集成场景),前置条件为:

  1. source匹配的 CANN 环境;
  2. 使用--experimental构建并安装最新 custom package;
  3. cust及匹配的vendor_name=custom运行样例。

也就是说,样例默认基于随 CANN 分发的算子包运行;要加载本仓库实验目录下新编译的算子实现,必须走 custom package 安装流程并显式指定custvendor,否则无法命中本仓库内的算子二进制。

参考资源

  • 算子 README:experimental/loss/gaussian_nll_loss_grad/README.md
  • ACLNN 接口文档:experimental/loss/gaussian_nll_loss_grad/docs/aclnnGaussianNllLossGrad.md
  • 两段式接口通用说明:docs/zh/context/two_phase_api.md
  • aclnn 返回码:docs/zh/context/aclnn_return_code.md
  • 编译与运行样例指南:docs/zh/context/compile_and_run_sample.md
  • 前向损失语义参考:PyTorch GaussianNLLLoss(GaussianNLLLossGrad 为其反向对应实现)
  • 人工智能
  • 算子库
  • 深度学习
  • CANN
  • Ascend

【免费下载链接】ops-nn

本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。

项目地址:https://gitcode.com/cann/ops-nn
点击查看免费下载

相关推荐

上一篇:如何利用LikeC4进行架构模拟:系统行为预测的可视化分析指南
下一篇:Chrome DevTools App进阶技巧:开启远程调试模式的完整教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询