news 2026/9/18 12:35:31

ops-cv 三维最近邻精确上采样反向算子:aclnnUpsampleNearestExact3dBackward API 详解与 NPU 实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ops-cv 三维最近邻精确上采样反向算子:aclnnUpsampleNearestExact3dBackward API 详解与 NPU 实现解析

ops-cv 三维最近邻精确上采样反向算子:aclnnUpsampleNearestExact3dBackward API 详解与 NPU 实现解析

【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv

本文基于 ops-cv 仓库中的接口文档,系统讲解aclnnUpsampleNearestExact3dBackward这一 5D 张量三维最近邻"精确"上采样反向(梯度)接口的数学定义、两段式调用方式、全部参数与错误码、容量与缩放约束,并结合仓库内主机侧校验链路、L0 封装与 kernel 级 SIMT 实现,帮助你既能直接写出可运行的调用代码,又能理解其在 NPU 上的实际执行路径。

算子定位与产品支持情况

aclnnUpsampleNearestExact3dBackward是 aclnnUpsampleNearestExact3d(三维最近邻精确上采样正向算子)的反向计算接口,用于将上采样输出侧的梯度gradOut累加回输入侧的gradInput。仓库中该算子的实现与文档位于 image/upsample_nearest_exact3d_grad 目录,接口文档即 aclnnUpsampleNearestExact3dBackward.md。

按文档与 算子 README 的产品支持表,各产品的支持情况如下:

产品是否支持
Ascend 950PR/Ascend 950DT
Atlas A3 训练系列产品/Atlas A3 推理系列产品
Atlas A2 训练系列产品/Atlas A2 推理系列产品
Atlas 200I/500 A2 推理产品×
Atlas 推理系列产品×
Atlas 训练系列产品×

在 算子定义文件 中可以看到算子为ascend910bascend910_93ascend950三类平台分别注册了配置,与上表支持的产品线相对应;其中ascend950配置开启了动态编译、动态 Rank 与动态 Shape 支持(regbase 平台走动态编译静态标志,扩展文件指向upsample_nearest_exact3d_grad_aptkernel 源文件)。

数学原理:梯度累加窗口与 -0.5 偏移

理解这个"Backward"接口的关键,是它不是简单的逐点缩放,而是对输出侧梯度在一个三维窗口内做累加。对于输入gradOut(N, C, d, h, w),输出gradInput上任意一点(N, C, D, H, W)满足:

$$ gradInput(N, C, D, H, W) = \sum_{d = srcD}^{srcDUp - 1}\sum_{h = srcH}^{srcHUp - 1}\sum_{w = srcW}^{srcWUp - 1}gradOut(N, C, d, h, w) $$

其中缩放因子按如下规则确定:当scalesDscalesHscalesW三者均大于 0 时直接取用户传入值;否则用输出/输入空间尺寸之比回退计算:

$$ scaleD =\begin{cases} scalesD & scalesD>0&scalesH>0&scalesW>0\ outputSize[0]/inputSize[2] & Otherwise\ \end{cases} $$

$$ scaleH =\begin{cases} scalesH & scalesD>0&scalesH>0&scalesW>0\ outputSize[1]/inputSize[3] & Otherwise\ \end{cases} $$

$$ scaleW =\begin{cases} scalesW & scalesD>0&scalesH>0&scalesW>0\ outputSize[2]/inputSize[4] & Otherwise\ \end{cases} $$

输入点(D, H, W)对应的输出侧累加窗口边界为(outputSize[0/1/2]分别是输出 D、H、W 维长度):

$$ srcD = Min(ceil(scaleD * D - 0.5), outputSize[0]) $$

$$ srcDUp = Min(ceil(scaleD * (D + 1) - 0.5), outputSize[0]) $$

$$ srcH = Min(ceil(scaleH * H - 0.5), outputSize[1]) $$

$$ srcHUp = Min(ceil(scaleH * (H + 1) - 0.5), outputSize[1]) $$

$$ srcW = Min(ceil(scaleW * W - 0.5), outputSize[2]) $$

$$ srcWUp = Min(ceil(scaleW * (W + 1) - 0.5), outputSize[2]) $$

这里-0.5的偏移正是"Exact"(精确)模式的语义所在:它对应 PyTorchupsample_nearest_exact3d的反向定义,与普通的upsample_nearest3d(无 -0.5 偏移)形成对照。这一细节可以在 kernel 共享实现中得到直接印证:upsample_nearest3d_grad_simt_base.h 中ComputeOrig函数按isExtra模板开关选择ceil(idx * scale - 0.5)ceil(idx * scale),本算子固定以isExtra = true实例化(见 upsample_nearest_exact3d_grad_apt.cpp 中Nearest3dGradSimt<DTYPE_GRAD_OUTPUT, uint32_t, true, schId>的第三个模板参数)。

两段式接口与函数原型

按 CANN 的两段式接口规范,该算子分为两个接口:必须先调用第一段接口完成入参校验并计算所需 workspace 大小、拿到aclOpExecutor,再调用第二段接口在指定流上执行计算。

aclnnStatus aclnnUpsampleNearestExact3dBackwardGetWorkspaceSize( const aclTensor *gradOut, const aclIntArray *outputSize, const aclIntArray *inputSize, double scalesD, double scalesH, double scalesW, aclTensor *gradInput, uint64_t *workspaceSize, aclOpExecutor **executor)
aclnnStatus aclnnUpsampleNearestExact3dBackward( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)

第一段接口 aclnnUpsampleNearestExact3dBackwardGetWorkspaceSize 参数说明

参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensor
gradOut(aclTensor*)输入表示反向计算的梯度Tensor,对应公式中的gradOut不支持空Tensor;gradOut的所有维度取值均小于等于(2^31-1)。FLOAT32、FLOAT16、BFLOAT16NCDHW、NDHWC5
outputSize(aclIntArray*)输入表示输入gradOut在D、H和W维度上的空间大小,对应公式中的outputSizesize为3,且各元素均大于零。INT64---
inputSize(aclIntArray*)输入表示输出gradInput分别在N、C、D、H和W维度上的空间大小,对应公式中的inputSizesize为5,且各元素均大于零。INT64---
scalesD(double)输入表示输出gradInput的depth维度乘数,对应公式中的scalesD取值小于等于50。----
scalesH(double)输入表示输出gradInput的height维度乘数,对应公式中的scalesH取值小于等于50。----
scalesW(double)输入表示输出gradInput的width维度乘数,对应公式中的scalesW取值小于等于50。----
gradInput(aclTensor*)输出表示反向计算的输出张量,对应公式中的输出gradInput不支持空Tensor;数据类型、数据格式、shape与入参gradOut保持一致(指 N、C 维度一致);gradInput的所有维度取值均小于等于(2^31-1)。FLOAT32、FLOAT16、BFLOAT16NCDHW、NDHWC5
workspaceSize(uint64_t*)输出返回需要在Device侧申请的workspace大小。-----
executor(aclOpExecutor**)输出返回op执行器,包含了算子计算流程。-----

返回值aclnnStatus状态码,取值参见 aclnn 返回码说明。第一段接口完成入参校验,出现以下场景时报错:

返回码错误码描述
ACLNN_ERR_PARAM_NULLPTR161001传入的gradOut、outputSize、inputSize或gradInput是空指针。
ACLNN_ERR_PARAM_INVALID161002gradOut的数据类型不在支持的范围内。
ACLNN_ERR_PARAM_INVALID161002gradOut和gradInput的数据类型不一致。
ACLNN_ERR_PARAM_INVALID161002gradOut的维度不为5维。
ACLNN_ERR_PARAM_INVALID161002outputSize的size不等于3。
ACLNN_ERR_PARAM_INVALID161002outputSize的某个元素值不大于0。
ACLNN_ERR_PARAM_INVALID161002inputSize的size不等于5。
ACLNN_ERR_PARAM_INVALID161002gradOut与inputSize在N、C维度上的size不同。
ACLNN_ERR_PARAM_INVALID161002gradOut在D、H、W维度上的size与outputSize[0]、outputSize[1]、outputSize[2]不一致。
ACLNN_ERR_PARAM_INVALID161002gradInput在N、C维度的size与inputSize[0]、inputSize[1]不一致。
ACLNN_ERR_PARAM_INVALID161002gradInput在D、H、W维度上的size与inputSize[2]、inputSize[3]、inputSize[4]不一致。
ACLNN_ERR_PARAM_INVALID161002scalesD、scalesH、scalesW的取值不满足约束要求。

这些校验项与源码中的检查函数一一对应:aclnn_upsample_nearest_exact3d_backward.cpp 中的CheckParams按固定顺序执行CheckNotNull(空指针检查)、CheckDtypeValid(类型白名单且输入输出类型一致)、CheckShape(5 维检查、outputSize 长度为 3、inputSize 长度为 5)、CheckInputElement(各元素大于 0、shape 与 inputSize/outputSize 的逐维一致性、scale 上限)以及CheckUplimit(各维度不超过 INT32_MAX)。其中类型白名单DTYPE_SUPPORT_LIST仅包含DT_FLOATDT_FLOAT16DT_BF16(同文件 L33-L34),缩放上限常量MAX_SUPPORT_SCALE = 50(L37)。

第二段接口 aclnnUpsampleNearestExact3dBackward 参数说明

参数名输入/输出描述
workspace输入在Device侧申请的workspace内存地址。
workspaceSize输入在Device侧申请的workspace大小,由第一段接口aclnnUpsampleNearestExact3dBackwardGetWorkspaceSize获取。
executor输入op执行器,包含了算子计算流程。
stream输入指定执行任务的Stream。

返回值aclnnStatus,参见 aclnn 返回码说明。

约束说明

文档给出的 shape 与容量约束如下,调用前必须逐一核对:

  • 参数gradOutgradInput的 shape 约束:
    • 每个维度的取值小于等于 2^20。
    • 参数gradInput的 N 轴和 C 轴与gradOut保持一致。
    • 内存占用需小于 60GB,计算公式:

$$ N * C * (gradOut_D * gradOut_H * gradOut_W + gradInput_D * gradInput_H * gradInput_W + gradOut_D * gradOut_H * gradInput_W + gradOut_D * gradInput_H * gradInput_W) * sizeof(dtype) < 60 * 1024 * 1024 * 1024 $$

其中 N 为输入和输出的 N 轴,C 为输入和输出的 C 轴,dtype 为输入张量的数据类型。
  • N * C * gradOut_D * gradOut_H < 2^31
  • gradInput_W * gradInput_H < 2^31
  • 参数gradOutgradInput的数据格式不为 NCDHW 或 NDHWC 时,输入的其他数据格式默认按 NCDHW 处理。
  • 反向接口的输入数据缩小倍数必须小于等于 50,即:

$$ outputSize_D / 输出shape的深度D <= 50 $$

$$ outputSize_H / 输出shape的高度H <= 50 $$

$$ outputSize_W / 输出shape的宽度W <= 50 $$

  • scalesDscalesHscalesW的取值均大于 0 时,参数inputSizeoutputSizescalesDscalesHscalesW需满足如下约束:

$$ outputSize_D = floor(inputSize_D * scalesD) $$

$$ outputSize_H = floor(inputSize_H * scalesH) $$

$$ outputSize_W = floor(inputSize_W * scalesW) $$

  • 确定性计算:aclnnUpsampleNearestExact3dBackward默认确定性实现。

从源码结构看,"缩小倍数不超过 50"的限制对应主机侧CheckInputElement中对回退 scale(ComputeNearestExact3dGradScales,即 scale ≤ 0 时用output_size / input_size反推)的三重校验,见 aclnn_upsample_nearest_exact3d_backward.cpp L128-L135;这也解释了为什么文档把"scales 取值不满足约束"与 shape 校验统一归入 161002 错误码。

源码实现链路解析

第一段接口的内部编排:Contiguous → Transpose → 计算 → ViewCopy

阅读 GetWorkspaceSize 实现 可以看到该接口并非单一 kernel 调用,而是一条 L0 算子编排链:

  1. 空 Tensor 短路gradOut为空时直接返回workspaceSize = 0
  2. scales 归一化:当scalesD/H/W均大于 0 时透传用户值、并把outputSize置为空数组;否则向 kernel 传{0, 0, 0}的 scales,由 kernel 侧依据outputSize/inputSize反推(L222-L234)。这与"功能说明"中 scale 的分段定义严格一致。注意:文档参数表中gradOutgradInput标记"不支持空Tensor",此处短路分支可理解为框架层的防御性处理。
  3. 非连续 Tensor 拉直:对gradOut调用l0op::Contiguous
  4. 数据格式归一:若gradOut存储格式为 NDHWC,则先用l0op::Transpose(permute 为{0, 4, 1, 2, 3})转为 NCDHW 再进入 kernel;计算结果再按{0, 2, 3, 4, 1}转回 NDHWC(L236-L256)。从源码结构看,kernel 本体只处理 NCDHW 布局,NDHWC 通过前后两次转置支持,这正是参数表同时列出 NCDHW、NDHWC 两种格式的原因。
  5. 核心计算:调用l0op::UpsampleNearestExact3dGradNcdhw生成gradInput中间结果;
  6. 结果写回l0op::ViewCopy把结果拷贝到可能非连续的gradInput输出张量上;最后由GetWorkspaceSize()汇总整条链路的 workspace 需求。

L0 封装 UpsampleNearestExact3dGradNcdhw 还负责输出 shape 构造(以inputSize的 D、H、W 覆盖gradOut的对应维度)以及低精度适配:在非 regbase 平台上,BFLOAT16/FLOAT16 输入会先Cast为 FLOAT 参与计算,结果再Cast回原类型(L58-L73),保证半精度下的累加精度行为一致。

Kernel 级实现:SIMT 三级切分与累加窗口

device 侧入口 upsample_nearest_exact3d_grad_apt.cpp 按调度模板schId分支:schId == 0时退化为纯拷贝(输出完全等于输入);否则实例化Nearest3dGradSimt<DTYPE_GRAD_OUTPUT, uint32_t/uint64_t, true, schId>执行真正的梯度累加。该实现复用了 upsample_nearest3d_grad 的 arch35 共享代码。

从 upsample_nearest3d_grad_simt_base.h 可以读出与文档公式逐式对应的三层设计:

  • 窗口边界计算ComputeOrig<T2, isExtra>(D, lenSrcD, scaleD, origD)依次求出origD/origH/origW(即 srcD/srcH/srcW)与D+1/H+1/W+1对应的上界(srcDUp/srcHUp/srcWUp),并用min(orig, limit)钳位到输出尺寸——对应公式中Min(ceil(scale*(D+1)-0.5), outputSize[0])的形式;
  • 累加实现ComputeForSplitNcdhw(L45-L60)对d ∈ [origD, origDUp)h ∈ [origH, origHUp)w ∈ [origW, origWUp)三重循环求和gradOut,累加过程使用float中间量,最后写回outputGm[yGmIdx]——这正是文档中三重求和公式的逐行实现;
  • 数据并行切分SimtCompute(L104-L154)将输出体展平索引按schId分为三类任务:SCH_ID_1固定 N、C 只对 D/H/W 切分,SCH_ID_2固定 N 按 C 切分,SCH_ID_3对 N 切分;每个任务块内部再以魔数除法(GetUintDivMagicAndShift)快速还原(D, H, W)坐标。线程数按索引位宽选择:uint32 索引 2048 线程、uint64 索引 1024 线程(upsample_nearest3d_grad_simt.h L81-L91)。

结合文档的 60GB 内存公式可以推断,公式中额外的两项gradOut_D*gradOut_H*gradInput_WgradOut_D*gradInput_H*gradInput_W即为主机侧为 NDHWC 前后转置与中间结果准备的临时缓冲预算上限。

图模式(IR)调用与属性默认值

除 aclnn 接口外,该算子也可通过算子 IR 以图模式构图调用。算子名为UpsampleNearestExact3dGrad,其输入输出与属性定义为:

参数名类型描述数据类型
grad_output输入反向计算的梯度 Tensor,对应公式中的gradOutputFLOAT32、FLOAT16、BFLOAT16
input_size属性(必填)输出分别在 N、C、D、H、W 维度上的空间大小,size 为 5 且各元素大于零;必须满足input_size[0] == grad_output 的 Ninput_size[1] == grad_output 的 CLISTINT
output_size属性(可选)输入grad_output在 D、H、W 维度上的空间大小,size 为 3 且各元素大于零;默认值{0, 0, 0}LISTINT
scales属性(可选)沿每个维度的缩放数组,3 个元素分别对应scalesDscalesHscalesW;默认值{0.0, 0.0, 0.0}LISTFLOAT
y输出反向计算的输出张量,对应公式中的gradInput,类型与格式与grad_output一致,shape 由input_size决定。FLOAT32、FLOAT16、BFLOAT16

可见 aclnn 接口中scalesD/H/W为 0 且使用outputSize的用法,等价于图模式下只填input_sizescales取默认值)的场景,两条路径语义一致。

调用示例

以下示例代码继承自接口文档,可直接作为接入模板;具体编译与执行流程请参考编译与运行样例。示例选取了一个 2 倍下采样反向前向的场景:gradOut形状为{2, 2, 2, 2, 2}(NCDHW),gradInput形状为{2, 2, 1, 1, 1}scales全 0 走outputSize反推路径,期望每个输入点累加对应 2×2×2=8 个输出梯度。

#include <iostream> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_upsample_nearest_exact3d_backward.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::vector<int64_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::vector<T>& hostData, const std::vector<int64_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::vector<int64_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_NCDHW, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. (固定写法)device/stream初始化,参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, &stream); // check根据自己的需要处理 CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); // 2. 构造输入与输出,需要根据API的接口自定义构造 std::vector<int64_t> gradOutShape = {2, 2, 2, 2, 2}; std::vector<int64_t> gradInputShape = {2, 2, 1, 1, 1}; void* gradOutDeviceAddr = nullptr; void* gradInputDeviceAddr = nullptr; aclTensor* gradOut = nullptr; aclTensor* gradInput = nullptr; std::vector<float> gradOutHostData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; std::vector<float> gradInputHostData = {2.0, 2, 2, 2}; std::vector<int64_t> outputSizeData = {2, 2, 2}; std::vector<int64_t> inputSizeData = {2, 2, 1, 1, 1}; double scalesD = 0.0; double scalesH = 0.0; double scalesW = 0.0; // 创建gradOut aclTensor ret = CreateAclTensor(gradOutHostData, gradOutShape, &gradOutDeviceAddr, aclDataType::ACL_FLOAT, &gradOut); CHECK_RET(ret == ACL_SUCCESS, return ret); // 创建gradInput aclTensor ret = CreateAclTensor(gradInputHostData, gradInputShape, &gradInputDeviceAddr, aclDataType::ACL_FLOAT, &gradInput); CHECK_RET(ret == ACL_SUCCESS, return ret); const aclIntArray* outputSize = aclCreateIntArray(outputSizeData.data(), outputSizeData.size()); CHECK_RET(outputSize != nullptr, return ACL_ERROR_INTERNAL_ERROR); const aclIntArray* inputSize = aclCreateIntArray(inputSizeData.data(), inputSizeData.size()); CHECK_RET(inputSize != nullptr, return ACL_ERROR_INTERNAL_ERROR); // 3. 调用CANN算子库API,需要修改为具体的API uint64_t workspaceSize = 0; aclOpExecutor* executor; // 调用aclnnUpsampleNearestExact3dBackward第一段接口 ret = aclnnUpsampleNearestExact3dBackwardGetWorkspaceSize(gradOut, outputSize, inputSize, scalesD, scalesH, scalesW, gradInput, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnUpsampleNearestExact3dBackwardGetWorkspaceSize 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); } // 调用aclnnUpsampleNearestExact3dBackward第二段接口 ret = aclnnUpsampleNearestExact3dBackward(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnUpsampleNearestExact3dBackward failed. ERROR: %d\n", ret); return ret); // 4. (固定写法)同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 auto size = GetShapeSize(gradInputShape); std::vector<float> resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), gradInputDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host 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]); } // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改 aclDestroyTensor(gradOut); aclDestroyTensor(gradInput); // 7. 释放device资源,需要根据具体API的接口定义修改 aclrtFree(gradOutDeviceAddr); aclrtFree(gradInputDeviceAddr); if (workspaceSize > 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }

该示例的完整可编译版本(含资源释放与错误处理细节)见仓库样例 test_aclnn_upsample_nearest_exact3d_grad.cpp。

测试体系与正确性验证

仓库围绕该算子提供了分层测试,可作为行为验证的依据:

  • 主机侧接口 UT:test_aclnn_upsample_nearest_exact3d_backward.cpp 覆盖第一段接口的参数校验路径;
  • Kernel UT:test_upsample_nearest_exact3d_grad.cpp 配合数据生成与对比脚本(gen_data.py、compare_data.py)对 kernel 数值进行比对;
  • 系统级(ST)标杆测试:executor_aclnnUpsampleNearestExact3dBackward.py 以 PyTorch 的upsample_nearest_exact3d_backward(torch_npu 后端)作为标杆正向计算,与算子输出做一致性对比,进一步印证该算子与框架语义(含 -0.5 偏移)对齐。

小结

  • aclnnUpsampleNearestExact3dBackward是 5D 张量(NCDHW/NDHWC)最近邻精确上采样的梯度接口,数学本质是对输出侧梯度在srcD..srcDUp等三维窗口内求和,-0.5偏移是其"Exact"语义的来源;
  • 调用遵循两段式接口:第一段完成入参校验(161001/161002 错误码体系)、workspace 计算与 L0 算子链编排,第二段在指定 stream 上执行;
  • 实现上支持 FLOAT32/FLOAT16/BF16 三种类型与 NCDHW/NDHWC 两种格式(NDHWC 经前后转置支持),scales 与 outputSize 两种指定方式语义等价,缩小倍数上限 50,shape 与 60GB 内存约束需在调用前自查;
  • 深入阅读入口:主机侧校验与编排在 op_host/op_api/aclnn_upsample_nearest_exact3d_backward.cpp,L0 封装在 op_host/op_api/upsample_nearest_exact3d_grad.cpp,kernel 计算核心在 arch35/upsample_nearest3d_grad_simt_base.h。

【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 12:31:22

盲派命理口诀实操:十干十神与四柱宫位断命法

简介&#xff1a;这是一份北派盲人命理金口诀整理资料&#xff0c;以Word文档形式提供&#xff0c;适合命理爱好者、传统文化研究者以及希望深入了解八字命理体系的读者。文档从十干断易篇入手&#xff0c;逐一解析十天干对应性格与处世特点&#xff0c;随后展开十断财官体系&a…

作者头像 李华
网站建设 2026/9/18 12:29:57

一键微信公众号 RSS 订阅:wewe-rss 私有化部署完整指南

一键微信公众号 RSS 订阅&#xff1a;wewe-rss 私有化部署完整指南 【免费下载链接】wewe-rss &#x1f917;更优雅的微信公众号订阅方式&#xff0c;支持私有化部署、微信公众号RSS生成&#xff08;基于微信读书&#xff09; 项目地址: https://gitcode.com/GitHub_Trending…

作者头像 李华