news 2026/9/18 9:38:42

CANN ops-cv ResizeBicubicV2Grad 算子深度解析:双三次插值反向传播的原理、接口与源码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-cv ResizeBicubicV2Grad 算子深度解析:双三次插值反向传播的原理、接口与源码实现

CANN ops-cv ResizeBicubicV2Grad 算子深度解析:双三次插值反向传播的原理、接口与源码实现

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

导读

ResizeBicubicV2Grad 是 CANN ops-cv 算子库中图像缩放类算子的反向传播实现,用于计算双三次(Bicubic)插值调整图像在反向传播过程中的输入梯度,是训练场景中ResizeBicubicV2前向算子不可或缺的配套算子。本文以 image/resize_bicubic_v2_grad/README.md 为骨架,结合算子原型、Host 侧算子定义、Shape 推导、Tiling 策略、Kernel 多分支实现与单测用例,完整讲解该算子的数学原理、参数语义、aclnn 两段式调用方式与图模式构图方式,帮助你快速完成集成与二次开发。

功能说明与数学原理

算子定位

ResizeBicubicV2Grad 计算输入图像在双三次插值基础下的梯度。用一句话概括:输入是正向插值后的梯度图,输出是插值前的原始图像的梯度。对应关系为:

  • grads:正向双三次插值调整后的图(对应公式中的 Y,即反向传播中来自上游的梯度)。
  • original_image:原图像(前向算子的输入)。
  • y:正向 Resize 的输入梯度(即本算子的输出)。

从算子原型注释(image/resize_bicubic_v2_grad/op_graph/resize_bicubic_v2_grad_proto.h)可见,其与 PyTorch 的upsample_bicubic2d_backward算子兼容,属于第三框架兼容算子。

双三次插值核函数

双三次插值使用 4×4 邻域像素加权求和得到目标像素值,其权重核 W(x) 定义如下:

$$ W(x) = \begin{cases} (a + 2)|x|^3 - (a + 3)|x|^2 + 1 & \text{for } |x|≤1 \ a|x|^3 -5a|x|^2 + 8a|x| - 4a & \text{for } 1<|x|<2 \ 0 & \text{otherwise} \ \end{cases} $$

其中系数a = -0.75(对应 PyTorch 双三次插值的默认a取值)。核函数仅在距离小于 2 的范围内非零,这是后续 Kernel 实现中每个输出像素只需累加 4×4 邻域贡献的数学依据。

梯度传播公式

对于原始图像中的像素 (i, j),其梯度由所有参与插值的目标像素 (i', j') 的梯度按权重累加得到:

$$ \frac{\partial L}{\partial X_{i,j}} = \sum_{i'} \sum_{j'} \frac{\partial L}{\partial Y_{i',j'}} \times W(i' - i) \times W(j' - j) $$

即:上游梯度grads中的每个像素 (i', j'),按其在 H 轴与 W 轴上的核权重 W 反向摊回原始图像对应位置,H 轴权重与 W 轴权重相乘即为该目标像素对原始像素的贡献。这与 aclnn 接口文档(image/upsample_bicubic2d_grad/docs/aclnnUpsampleBicubic2dBackward.md)中描述的正向插值公式互为对偶。

坐标映射与 align_corners

梯度计算中源坐标与目标坐标的映射关系由align_corners属性决定,坐标缩放因子 scaleH / scaleW 的计算方式为:

$$ scaleH =\begin{cases} (inputSize[2]-1) / (outputSize[0]-1) & alignCorners=true \ 1 / scalesH & alignCorners=false&scalesH>0\ inputSize[2] / outputSize[0] & otherwise \end{cases} $$

W 轴同理。其中align_corners=true表示输入输出张量的角像素点对齐(保留角像素值),false表示使用半像素中心进行插值。

产品支持情况

当前仓库中 ResizeBicubicV2Grad 算子的产品支持矩阵如下(以 README 为准):

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

从算子定义(image/resize_bicubic_v2_grad/op_host/resize_bicubic_v2_grad_def.cpp)中可以印证,AICore().AddConfig("ascend950", aicoreConfig)仅为 ascend950 平台注册了 AI Core 配置;op_host/config/ascend950/ 目录下的编译配置文件也仅针对该平台存在。需要注意:本仓库内 ResizeBicubicV2Grad 与 aclnnUpsampleBicubic2dBackward 接口的支持范围并不完全一致,后者在 Atlas A2/A3、Atlas 训练系列等产品上也支持,集成时请以目标平台实际可用的接口为准。

参数说明

输入与输出

参数名输入/输出/属性描述数据类型数据格式
grads输入正向双三次插值调整后的图,对应公式 Y。FLOAT16、FLOAT32、BFLOAT16NCHW、NHWC
original_image输入原图像的高和宽。FLOAT16、FLOAT32、BFLOAT16NCHW、NHWC
y输出正向 Resize 的输入梯度。FLOAT16、FLOAT32、BFLOAT16NCHW、NHWC

属性参数

README 未单列属性,但算子原型(resize_bicubic_v2_grad_proto.h)与算子定义(resize_bicubic_v2_grad_def.cpp)明确声明了两个可选属性:

属性名类型默认值说明
align_cornersBoolfalse为 true 时,输入与输出张量的 4 个角像素中心对齐,保留角像素值;为 false 时,使用半像素中心计算插值。
scalesListFloat(2 个元素){0.0f, 0.0f}第一个元素表示 grads 中像素 H 轴下标与 y 中像素 H 轴下标的比值,第二个元素对应 W 轴。仅在 align_corners 为 true 且取值大于 0 时生效,否则按输入输出尺寸比值计算缩放。

关键约束(源码级确认)

  • 输入gradsoriginal_image与输出y均为 4D 张量,数据格式仅支持 NCHW、NHWC。
  • original_image的 N、C 维度必须与grads一致,输出y的 shape、format、dtype 与original_image保持一致。
  • 约束说明为“无”,但结合 resize_bicubic_v2_grad_infershape.cpp 的校验逻辑,运行期仍会强校验:grads格式必须为 NCHW/NHWC,original_image必须为 4D,且其 H、W 维(按格式区分索引:NCHW 下 hIdx=2,NHWC 下 hIdx=1)取值必须大于 0,否则返回GRAPH_FAILED

调用说明

README 提供了两种调用方式:

调用方式样例代码说明
aclnn 接口test_aclnn_resize_bicubic_v2_grad.cpp通过 aclnnUpsampleBicubic2dBackward 接口方式调用 ResizeBicubicV2Grad 算子。
图模式resize_bicubic_v2_grad_proto.h通过算子 IR 构图方式调用 ResizeBicubicV2Grad 算子。

aclnn 接口两段式调用

aclnn 接口遵循 CANN 统一的两段式调用范式(详见 docs/zh/context/two_phase_api.md):先调用GetWorkspaceSize接口完成入参校验并计算所需 workspace 大小,再调用执行接口真正下发计算。接口原型如下:

aclnnStatus aclnnUpsampleBicubic2dBackwardGetWorkspaceSize( const aclTensor* gradOut, const aclIntArray* outputSize, const aclIntArray* inputSize, const bool alignCorners, double scalesH, double scalesW, aclTensor* gradInput, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnUpsampleBicubic2dBackward( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)

各参数语义:

参数名输入/输出描述使用说明
gradOut输入反向计算的梯度 Tensor,对应公式中的gradOut(即算子输入grads)。不支持空 Tensor;数据类型与gradInput一致;ND 格式默认按 NCHW 处理。
outputSize输入gradOut在 H、W 维度上的空间大小。size 为 2,各元素大于 0。
inputSize输入输出gradInput在 N、C、H、W(或 N、H、W、C)维度上的空间大小。size 为 4,各元素大于 0。
alignCorners输入是否对齐角像素点。true 对齐角像素,false 不对齐。
scalesH / scalesW输入输出gradInput的 height / width 维度乘数。对应公式中的scalesHscalesW
gradInput输出反向计算的输出张量(即算子输出y)。数据类型、格式与gradOut一致,N、C 轴与gradOut一致。
workspaceSize输出需要在 Device 侧申请的 workspace 大小。由第一段接口计算返回。
executor输出op 执行器,封装了算子计算流程。由第一段接口返回,供第二段接口使用。

第一段接口的常见返回码:ACLNN_ERR_PARAM_NULLPTR(错误码 161001,入参空指针)与ACLNN_ERR_PARAM_INVALID(错误码 161002,覆盖数据类型/格式越界、维度不为 4、outputSize 长度不为 2、元素小于 1、N/C 轴不一致等十余种入参校验场景),完整返回码说明见 docs/zh/context/aclnn_return_code.md。

完整可运行示例

仓库提供了完整可编译的示例 image/resize_bicubic_v2_grad/examples/test_aclnn_resize_bicubic_v2_grad.cpp,其调用流程可归纳为 7 步:

  1. 环境初始化aclInitaclrtSetDeviceaclrtCreateStream
  2. 构造 Tensor:将 host 数据{1, 2, 3, 4.1}(shape{1,1,2,2},对应 2×2 的梯度图)经aclrtMalloc+aclrtMemcpy拷贝到 device 侧,并用aclCreateTensor创建grads对应的selfTensor;输出outshape 为{1,1,3,3}
  3. 构造尺寸参数outputSize = {2, 2}inputSize = {1, 1, 3, 3},分别通过aclCreateIntArray创建。
  4. 两段式调用:先调aclnnUpsampleBicubic2dBackwardGetWorkspaceSize(self, outputSize, inputSize, /*alignCorners=*/1, /*scalesH=*/1.1, /*scalesW=*/1.1, out, &workspaceSize, &executor),按返回值申请 workspace,再调aclnnUpsampleBicubic2dBackward(workspaceAddr, workspaceSize, executor, stream)
  5. 同步等待aclrtSynchronizeStream(stream)确保计算完成。
  6. 结果回拷aclrtMemcpy将 device 侧结果拷回 host 并逐元素打印。
  7. 资源释放aclDestroyTensoraclDestroyIntArrayaclrtFreeaclrtDestroyStreamaclrtResetDeviceaclFinalize

编译与运行方式可参考 docs/zh/context/compile_and_run_sample.md(需引入aclnnop/aclnn_upsample_bicubic_2d_backward.h头文件)。

图模式构图

图模式通过算子 IR 构图,直接引用REG_OP(ResizeBicubicV2Grad)注册的原型(resize_bicubic_v2_grad_proto.h)。其 IR 定义要点:

REG_OP(ResizeBicubicV2Grad) .INPUT(grads, TensorType({DT_FLOAT16, DT_FLOAT, DT_BF16})) .INPUT(original_image, TensorType({DT_FLOAT16, DT_FLOAT, DT_BF16})) .OUTPUT(y, TensorType({DT_FLOAT16, DT_FLOAT, DT_BF16})) .ATTR(align_corners, Bool, false) .ATTR(scales, ListFloat, {0.0f, 0.0f}) .OP_END_FACTORY_REG(ResizeBicubicV2Grad)

源码级实现剖析

算子定义与 Shape/Dtype 推导

  • 算子定义(resize_bicubic_v2_grad_def.cpp):声明两个必选输入gradsoriginal_image与一个必选输出y,数据类型覆盖DT_FLOAT16 / DT_FLOAT / DT_BF16,格式覆盖NCHW / NHWC;同时开启动态编译、动态 Rank 与动态 Shape 支持。
  • Shape 推导(resize_bicubic_v2_grad_infershape.cpp):输出y的 shape 直接继承original_image;对于未知 Rank 的输入则设置未知 Shape。Dtype 推导将输出类型设置为grads的类型(三者必须同为 FLOAT/FLOAT16/BF16 之一)。

Host 侧 Tiling 策略

Tiling 逻辑分布在 op_host/arch35/ 下的 4 个文件中,通过不同的 TilingKey 选择 Kernel 分支。从单测 test_resize_bicubic_v2_grad_tiling.cpp 可以清晰看到 TilingKey 的划分规律:

TilingKey含义单测验证场景
10000 / 10001SIMT 常规路径(32 位 / 64 位索引)-
20000 / 20001SIMT 确定性路径(32 位 / 64 位索引)225×32 → 113×32,align_corners=true
20002 / 20003SIMT 确定性路径 + SplitK(32 位 / 64 位索引)大 shape 场景(如 32×2048×4096×32)
30000纯拷贝路径(AllCopy)输入输出尺寸完全一致(如 32×32 → 32×32)

单测还揭示了两个实现细节:一是当gradsoriginal_image尺寸相同时,算子退化为纯内存拷贝(TilingKey=30000);二是确定性路径的 TilingData 中新增了splitK / coresPerOutput / segsPerOutput三个字段,非 SplitK 场景取默认值。非 split-K 路径的 workspace 即系统预留区GetLibApiWorkSpaceSize(),在 ascend950 UT 环境(测试 faker 平台描述符)下为 UINT32_MAX(4294967295),与同仓 col2im 用例一致。

Kernel 多分支实现

Kernel 入口 resize_bicubic_v2_grad.cpp 依据 TilingKey 分发到 arch35/ 下的三类模板实现:

  • ResizeBicubicV2GradAllCopy:纯拷贝分支,直接搬移数据,无需插值计算。
  • ResizeBicubicV2GradSimt:SIMT 常规实现,模板参数覆盖索引位宽(uint32_t/int32_tuint64_t/int64_t)、数据格式(NCHW/NHWC)与 align_corners 开关(true/false),共计 8 种模板组合。
  • ResizeBicubicV2GradSimtDetermine:确定性实现,支持Process()ProcessSplitK()两种执行路径,后者用于超大 shape 下按 K 维切分、多核协作累加的场景,配合 workspace 使用。

Kernel 通过KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_MIX_AIV_1_0)声明任务类型,接收grads / originalImage / y / workspace / tiling五个 GM 地址参数,从 tiling 内存中恢复 TilingData 后按 key 分发执行。

测试与验证

算子配套的 UT 覆盖两层:

  • Tiling 单测(test_resize_bicubic_v2_grad_tiling.cpp):使用tiling_context_fakertiling_case_executor构造运行上下文,校验不同 shape 组合(同尺寸拷贝、等比缩放、超大 shape)下生成的 TilingKey、TilingData 二进制内容与 workspace 大小是否符合预期。
  • Infershape 单测(test_resize_bicubic_v2_grad_infershape.cpp):校验输出 shape 继承逻辑与非法输入(非 4D、H/W 为 0、格式不支持)的报错路径。

总结

ResizeBicubicV2Grad 是 CANN ops-cv 中一个典型的“动态 Shape + 多 Tiling 策略 + SIMT 多分支”算子:数学上严格遵循双三次核函数与梯度摊回公式,工程上通过 AllCopy / SIMT / SIMT-Determine(+SplitK) 三类路径在正确性、确定性与大 shape 性能之间取得平衡。无论你是通过aclnnUpsampleBicubic2dBackward两段式接口进行单算子调用,还是通过ResizeBicubicV2Grad算子 IR 构图接入训练图,均可从本文的参数语义与源码脉络出发,快速完成开发与问题定位。

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

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

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

Redux Store 完全指南:创建、配置、中间件与 DevTools 调试

Redux Store 完全指南&#xff1a;创建、配置、中间件与 DevTools 调试 【免费下载链接】redux A JS library for predictable global state management 项目地址: https://gitcode.com/gh_mirrors/re/redux 本篇技术指南聚焦 Redux 应用的核心枢纽——Store&#xff0c…

作者头像 李华
网站建设 2026/9/18 9:38:06

非遗数字化保护:地理空间技术与三维建模的创新应用

1. 非遗数字化的时代挑战与机遇在当代文化保护领域&#xff0c;我们正面临一个关键转折点&#xff1a;如何让沉睡在档案库里的非物质文化遗产真正"活"起来&#xff1f;传统非遗保护往往停留在拍照、录像、建档的初级阶段&#xff0c;这种单向记录模式就像把蝴蝶钉在标…

作者头像 李华
网站建设 2026/9/18 9:34:38

dlt 数据伪匿名化实战:使用 add_map 与加盐哈希隐藏 PII 列

dlt 数据伪匿名化实战&#xff1a;使用 add_map 与加盐哈希隐藏 PII 列 【免费下载链接】dlt data load tool (dlt) is an open source Python library that makes data loading easy &#x1f6e0;️ 项目地址: https://gitcode.com/GitHub_Trending/dl/dlt 伪匿名化&…

作者头像 李华
网站建设 2026/9/18 9:34:27

CS2自用命令指南:从autoexec到手感性能网络配置全解析

1. 从 CSGO 迁到 CS2&#xff1a;我为什么重新规整了一套自用命令CS2自用命令这个说法&#xff0c;听起来像是一份“自己用着顺手”的配置表&#xff0c;但它其实是我和游戏之间的一套固定对话方式。CS2正式上线后&#xff0c;我第一件事就是把用了很多年的 autoexec.cfg 从 CS…

作者头像 李华