CANN ops-cv 算子深度解析:UpsampleBicubic2dAAGrad 双三次抗锯齿上采样反向传播算子使用指南
CANN ops-cv 算子深度解析UpsampleBicubic2dAAGrad 双三次抗锯齿上采样反向传播算子使用指南【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv导读UpsampleBicubic2dAAGrad是 CANN ops-cv 图像算子库中双三次抗锯齿Bicubic with Anti-Aliasing上采样算子UpsampleBicubic2dAA的反向传播Gradient算子用于在 NPU 上完成上采样梯度回传是训练场景下图像缩放链路的关键一环。本文以 image/upsample_bicubic2d_aa_grad/README.md 为主体结合仓库内 aclnn 接口文档、调用示例与算子源码完整讲解其计算公式、参数语义、约束条件、两段式 aclnn 调用流程以及 Host 侧 Tiling 与 Kernel 侧的底层实现。读完本文你将能够正确理解该算子的梯度传播原理并通过aclnnUpsampleBicubic2dAAGrad接口在 Ascend 平台上完成反向计算的调用与验证。一、产品支持情况根据 image/upsample_bicubic2d_aa_grad/README.md 与 aclnnUpsampleBicubic2dAAGrad 接口文档该算子在不同硬件平台上的支持情况如下产品是否支持Ascend 950PR / Ascend 950DT√Atlas A3 训练系列产品 / Atlas A3 推理系列产品√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×从源码的算子注册配置也可以印证这一点。在 upsample_bicubic2d_aa_grad_def.cpp 中通过AICore().AddConfig(ascend910b)、AddConfig(ascend910_93)对应 Atlas A2/A3 系列以及带DynamicCompileStaticFlag、DynamicRankSupportFlag、DynamicShapeSupportFlag的ascend950配置注册了 AICore 算子实现而 op_host/config 下也仅存在ascend910_93、ascend910b、ascend950三套编译配置目录与文档中三种产品支持、三种产品不支持的结论完全对应。需要特别说明的是与正向算子 UpsampleBicubic2dAA 相比该反向算子不支持 Kirin X90 / Kirin 9030 处理器系列。二、功能说明与计算原理2.1 算子定位双三次抗锯齿上采样的反向传播UpsampleBicubic2dAAGrad是 UpsampleBicubic2dAA仓库内路径为 image/upsample_bicubic2d_aa/README.md的反向传播算子。若输入张量gradOutput的 shape 为(N, C, H, W)则输出张量gradInput的 shape 为(N, C, inputSize[2], inputSize[3])即反向把 H、W 维度恢复为前向输入的空间尺寸。2.2 计算公式对于一个二维插值点(N, C, h, w)其反向梯度gradInput(N, C, h, w)的计算公式为$$ {gradInput(N, C, h, w)}\sum_{i0}^{3}\sum_{j0}^{3}{W(i, j)}*{f(h_i, w_j)} $$其中i和j是权重W(i, j)的索引变量f(h_i, w_j)是gradOutput在(h_i, w_j)处的像素值W(i, j)是双三次抗锯齿插值的权重函数。2.3 缩放比例 scaleH / scaleW正向与反向计算共享同一套缩放比例定义scaleH与scaleW由alignCorners与scalesH/scalesW联合决定$$ scaleH \begin{cases} (inputSize[2]-1) / (outputSize[0]-1) alignCornerstrue \ 1 / scalesH alignCornersfalse\ \ scalesH0\ inputSize[2] / outputSize[0] otherwise \end{cases} $$$$ scaleW \begin{cases} (inputSize[3]-1) / (outputSize[1]-1) alignCornerstrue \ 1 / scalesW alignCornersfalse\ \ scalesW0\ inputSize[3] / outputSize[1] otherwise \end{cases} $$这里需要注意两点alignCorners 的语义alignCorners为true表示输入和输出张量的角像素点对齐缩放比例基于尺寸减一计算为false表示输入和输出张量的边像素点对齐缩放比例基于原始尺寸计算。这一语义与 PyTorch 中F.interpolate的align_corners参数一致。反向算子的缩放比例是倒数关系在 aclnn_upsample_bicubic2d_aa_grad.cpp 中ComputeBicubic2dAABackwardScales函数实现了1.0 / scale当scale 0时的换算逻辑若scale未指定≤ 0则退化为input_size / output_size的尺寸比值。反向缩放比例越小意味着前向上采样倍数越大。2.4 权重函数 W(d)双三次抗锯齿插值的权重函数使用经典的a -0.5三次样条核$$ W(d) \begin{cases} (a2)|d|^3-(a3)|d|^21 |d|\leq1 \ a|d|^3-5a|d|^28a|d|-4a 1|d|2 \ 0 otherwise \end{cases} $$其中$a-0.5$抗锯齿场景下的标准取值对应三次样条核的 Catmull-Rom 变体$d |(h, w) - (h_i, w_j)|$即插值点与采样点之间的距离。a -0.5与正向算子 UpsampleBicubic2dAA README 中的权重定义完全一致确保了前反向计算的一致性。从权重公式可以看出每个输出像素的反向梯度由周围至多 4×4 邻域内的梯度像素加权累加得到这与正向计算中每个输出点对 4×4 邻域采样点加权求和是对偶的。三、参数说明以下参数表格来自 image/upsample_bicubic2d_aa_grad/README.md 的参数说明章节并结合 aclnnUpsampleBicubic2dAAGrad 接口文档 中的接口语义做了补充参数名输入/输出/属性描述数据类型数据格式grad_output输入表示反向计算的梯度 Tensor对应公式中的gradOutput。数据类型与输出grad_input一致FLOAT32、FLOAT16、BFLOAT16NDoutput_size属性表示输入grad_output在 H 和 W 维度上的空间大小对应公式中的outputSize。size 为 2且各元素均大于零INT64-input_size属性表示输出grad_input分别在 N、C、H 和 W 维度上的空间大小对应公式中的inputSize。size 为 4且各元素均大于零INT64-align_corners可选属性决定是否对齐角像素点对应公式中的alignCorners。为true时输入和输出张量的角像素点被对齐否则不对齐。默认值为falseBOOL-scales_h可选属性表示输出grad_input的 height 维度乘数对应公式中的scalesH。默认值为空FLOAT32-scales_w可选属性表示输出grad_input的 width 维度乘数对应公式中的scalesW。默认值为空FLOAT32-grad_input输出表示反向计算的输出张量对应公式中的gradInput。数据类型与输入grad_output一致FLOAT32、FLOAT16、BFLOAT16ND在 aclnn 接口层面上述属性会以入参形式出现在函数签名中outputSize与inputSize封装为aclIntArrayalignCorners为boolscalesH/scalesW为double详见 aclnn_upsample_bicubic2d_aa_grad.h。算子定义文件 upsample_bicubic2d_aa_grad_def.cpp 中的注册信息与文档一致grad_output/grad_input支持DT_FLOAT16、DT_FLOAT、DT_BF16三种数据类型格式为 NDoutput_size、input_size为必选ListInt属性align_corners为可选 BOOL 属性默认falsescales_h、scales_w为可选 Float 属性。数据格式说明当数据格式为 ND 时默认按照 NCHW 格式处理接口层面也接受显式的 NCHW 格式ACL_FORMAT_NCHW。四、约束说明4.1 算子级约束image/upsample_bicubic2d_aa_grad/README.md 中标注算子本身无额外约束但接口文档在更细的粒度上给出了如下限制这些约束在调用时同样必须满足shape 与内存约束适用于全部支持产品gradOutput、out每个维度的取值小于等于2^20参数out的 N 轴和 C 轴与gradOutput保持一致内存占用需满足$$ (gradOutput_H \times gradOutput_W out_H \times out_W gradOutput_H \times out_W) \times N \times C \times sizeof(dtype) 60 \times 1024 \times 1024 \times 1024 $$其中N、C为输入输出张量的 N、C 轴大小dtype为输入张量的数据类型额外要求 $N \times C \times gradOutput_H 2^{31}$。缩放比例一致性约束参数inputSize、outputSize、scalesH、scalesW需要满足$$ outputSize_H floor(inputSize_H \times scalesH) $$$$ outputSize_W floor(inputSize_W \times scalesW) $$Atlas A2/A3 系列缩小倍数约束反向接口的输入数据缩小倍数必须小于等于 50$$ outputSize_H / 输出shape的高度H \le 50 $$$$ outputSize_W / 输出shape的宽度W \le 50 $$这一约束与 Host 侧参数校验代码中MIN_SUPPORT_SCALE 0.02即最小支持缩放比例aclnn_upsample_bicubic2d_aa_grad.cpp的检查相互呼应反向缩放比例不能过小否则会触发ACLNN_ERR_PARAM_INVALID错误。4.2 确定性计算Atlas A3 / Atlas A2 系列aclnnUpsampleBicubic2dAAGrad默认使用确定性实现Ascend 950PR / Ascend 950DT默认使用非确定性实现支持通过aclrtCtxSetSysParamOpt开启确定性计算。关于确定性计算的更多背景可参考 docs/zh/context/determinism_compute.md。五、调用方式两段式 aclnn 接口5.1 两段式接口总览该算子遵循 CANN 算子库统一的两段式接口调用规范必须先调用aclnnUpsampleBicubic2dAAGradGetWorkspaceSize接口获取计算所需 workspace 大小以及包含算子计算流程的执行器再调用aclnnUpsampleBicubic2dAAGrad接口执行计算。第一段接口的函数原型aclnnStatus aclnnUpsampleBicubic2dAAGradGetWorkspaceSize( const aclTensor *gradOutput, const aclIntArray *outputSize, const aclIntArray *inputSize, bool alignCorners, double scalesH, double scalesW, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)第二段接口的函数原型aclnnStatus aclnnUpsampleBicubic2dAAGrad( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)两个接口均返回aclnnStatus状态码具体取值参见 aclnn 返回码。5.2 第一段接口参数详解参数名输入/输出描述使用说明数据类型数据格式shape非连续 TensorgradOutputaclTensor*输入反向计算的梯度 Tensor对应gradOutput不支持空 TensorND 格式下默认按 NCHW 处理FLOAT32、FLOAT16、BFLOAT16NCHW、ND4√outputSizeaclIntArray*输入gradOutput在 H、W 维的空间大小size 为 2各元素大于零INT64---inputSizeaclIntArray*输入输出out在 N、C、H、W 维的空间大小size 为 4各元素大于零INT64---alignCornersbool输入是否对齐角像素点true对齐否则不对齐----scalesHdouble输入输出out的 height 维度乘数不能传入负值----scalesWdouble输入输出out的 width 维度乘数不能传入负值----outaclTensor*输出反向计算的输出张量对应gradInput不支持空 Tensor数据类型与格式与gradOutput保持一致FLOAT32、FLOAT16、BFLOAT16NCHW、ND4√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----第一段接口的常见报错场景返回码与错误码参见 aclnn 返回码返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 gradOutput、inputSize 或 out 是空指针ACLNN_ERR_PARAM_INVALID161002gradOutput 或 out 的数据类型不在支持范围内gradOutput 与 out 数据类型不一致gradOutput 的 shape 不是 4 维inputSize 的 H 维或 W 维取值小于 1inputSize 的 size 不为 4outputSize 的 size 不为 2scalesH 或 scalesW 为负值这些校验逻辑都可以在源码 aclnn_upsample_bicubic2d_aa_grad.cpp 中逐一对号入座CheckNotNull负责空指针检查、CheckDtypeValid负责数据类型与一致性检查、CheckShape负责维度与 size 检查、CheckScalesValid负责负数检查、CheckInputElement负责元素非零与格式检查。5.3 第二段接口参数详解参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口获取executor输入op 执行器包含算子计算流程stream输入指定执行任务的 AscendCL Stream六、完整调用示例仓库在 examples/test_aclnn_upsample_bicubic2d_aa_grad.cpp 提供了完整的可直接参考的调用样例。完整代码框架如下编译与执行步骤请参考 编译与运行样例#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_upsample_bicubic2d_aa_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 shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } 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 CreateAclNchTensor(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_NCHW, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. 固定写法device/stream初始化参考acl API手册 int32_t deviceId 0; // 根据自己的实际device填写 aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t inputShape {1, 1, 4, 2}; // gradOutput shape: NCHW std::vectorint64_t outShape {1, 1, 8, 4}; // gradInput shape: NCHW void* inputDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* input nullptr; aclTensor* out nullptr; std::vectorfloat inputHostData {0, 1, 2, 3, 4, 5, 6, 7}; const size_t kSize32 32U; std::vectorfloat outHostData(kSize32, 0); const size_t kSize4 4U; std::vectorint64_t outputSize {kSize4, 2}; // gradOutput的H、W std::vectorint64_t inputSize {1, 1, 8, 4}; // gradInput的N、C、H、W bool alignCorners true; double scalesH 0.5; double scalesW 0.5; // 创建input aclTensor ret CreateAclNchTensor(inputHostData, inputShape, inputDeviceAddr, aclDataType::ACL_FLOAT, input); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建input aclIntArray auto outputSizeArray aclCreateIntArray(outputSize.data(), 2); auto inputSizeArray aclCreateIntArray(inputSize.data(), 4); // 创建out aclTensor ret CreateAclNchTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnUpsampleBicubic2dAAGrad第一段接口 ret aclnnUpsampleBicubic2dAAGradGetWorkspaceSize(input, outputSizeArray, inputSizeArray, alignCorners, scalesH, scalesW, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnUpsampleBicubic2dAAGradGetWorkspaceSize 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); } // 调用aclnnUpsampleBicubic2dAAGrad第二段接口 ret aclnnUpsampleBicubic2dAAGrad(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnUpsampleBicubic2dAAGrad 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侧 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), 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和aclIntArray aclDestroyTensor(input); aclDestroyTensor(out); // 7. 释放device资源 aclrtFree(inputDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }该示例的关键调用步骤可以归纳为 7 步与 CANN aclnn 接口的标准调用范式一致初始化aclInit→aclrtSetDevice→aclrtCreateStream构造张量通过aclrtMalloc申请 Device 内存、aclrtMemcpy拷贝 Host 数据再以aclCreateTensor创建aclTensor注意按连续 NCHW 布局计算 stridesoutputSize/inputSize用aclCreateIntArray封装第一段接口aclnnUpsampleBicubic2dAAGradGetWorkspaceSize完成参数校验并返回workspaceSize与executor申请 workspace当workspaceSize 0时用aclrtMalloc申请 Device 侧 workspace第二段接口aclnnUpsampleBicubic2dAAGrad在指定 stream 上执行计算同步与取数aclrtSynchronizeStream等待计算完成aclrtMemcpy将结果从 Device 拷贝回 Host 并打印资源释放aclDestroyTensor→aclrtFree→aclrtDestroyStream→aclrtResetDevice→aclFinalize。示例中的具体参数为gradOutputshape(1,1,4,2)FLOAT32outputSize [4, 2]inputSize [1,1,8,4]alignCorners truescalesH scalesW 0.5输出gradInputshape(1,1,8,4)即把4×2的梯度张量反向传播回8×4的输入空间。七、源码级实现剖析为了让读者对该算子有更深层的理解下面结合仓库源码从算子注册、Host 侧 Tiling 到 Kernel 执行梳理其完整实现链路。7.1 算子定义与注册upsample_bicubic2d_aa_grad_def.cpp 通过OP_ADD(UpsampleBicubic2dAAGrad)注册算子核心声明为Input(grad_output)必选支持 FLOAT16/FLOAT/BF16格式 NDOutput(grad_input)必选数据类型与输入一致Attr(output_size)必选 ListInt、Attr(input_size)必选 ListInt、Attr(align_corners)可选 BOOL默认 false、Attr(scales_h)/Attr(scales_w)可选 FloatAICore 配置ascend910b、ascend910_93使用基础配置ascend950使用开启动态编译、动态 Rank、动态 Shape 的regbaseConfig并指定 op 文件为upsample_bicubic2d_aa_grad_apt。7.2 Host 侧 Tiling数据切分与 workspace 规划Tiling 逻辑位于 upsample_bicubic2d_aa_grad_tiling.cpp 与 upsample_bicubic2d_aa_grad_tiling.h。从 Tiling 数据结构UpsampleBicubicAAGradTilingData可以看出该算子的实现策略记录scale_w/scale_h及其倒数invscale_w/invscale_h、支撑半径support_w/support_h、最大插值尺寸max_interp_size_w/h供 Kernel 重建插值权重维护slideStartList/slideEndList/tailSlideStartList/tailSlideEndList/tailRowStartList/tailRowEndList等 H/W 两个方向上的滑窗区间列表MAX_CORE_CONT 50对应文档中缩小倍数上限 50 的实现约束用于多核并行切分内嵌两个TCubeTilingmatmulTiling_w与matmulTiling_h从源码结构可以推断该算子在 Kernel 侧通过 Cube 矩阵乘单元Matmul分别对 W 与 H 方向的权重矩阵与梯度数据做矩阵乘累加把4×4 邻域加权求和转化为矩阵乘形式以提升计算效率。此外WORK_SPACE_SIZE 32 * 1024 * 102432MB等常量定义了 workspace 的规划基准Tiling 阶段按核数将中间矩阵大小写入 tilingData供 Kernel 侧通过GetUserWorkspace获取。7.3 Kernel 侧执行入口upsample_bicubic2d_aa_grad.cpp 是 Kernel 入口函数通过TILING_KEY_IS(1/2/3)分别派发到UpSampleBicubic2dAAGradNDhalf、UpSampleBicubic2dAAGradNDfloat、UpSampleBicubic2dAAGradNDbfloat16_t三个模板实例对应 FLOAT16、FLOAT32、BFLOAT16并通过REGIST_MATMUL_OBJ注册 W/H 两个方向的 Matmul 对象后执行Init与Process。arch35 目录下arch35存放了 Ascend 950 架构ascend950配置的 SIMT 与 DataCopy 相关实现体现了不同架构下的 Kernel 差异化适配。7.4 测试验证仓库为该算子提供了完整的测试覆盖Host 侧单测test_aclnn_upsample_bicubic2d_aa_grad.cppaclnn 接口层与 test_upsample_bicubic2d_aa_grad_tiling.cppTiling 层Kernel 侧单测test_upsample_bicubic2d_aa_grad.cpp配套 gen_data.py 与 compare_data.py 进行数据生成与结果比对ST 测试atk_aclnnUpsampleBicubic2dAAGrad.json 与 executor_aclnnUpsampleBicubic2dAAGrad.py 提供了基于 ATK 框架的端到端验证入口。八、使用建议与总结前反向配套使用UpsampleBicubic2dAAGrad与 UpsampleBicubic2dAA 共享同一套align_corners、scales_h/scales_w语义与a -0.5的权重定义训练场景中反向传播时应保持与前向一致的属性配置避免梯度数值偏差注意反向缩小倍数限制Atlas A2/A3 系列上反向输入缩小倍数须 ≤ 50等价于反向缩放比例 ≥ 0.02超出会触发参数校验错误充分利用非连续 Tensor 支持gradOutput与out均支持非连续 Tensor如切片、转置视图可直接接入更复杂的自动求导图无需手动contiguous相关背景可参考 docs/zh/context/non_contiguous_tensor.md确定性要求若训练过程需要严格的逐次可复现结果在 Atlas A2/A3 上默认即为确定性实现在 Ascend 950 上需通过aclrtCtxSetSysParamOpt显式开启。总之UpsampleBicubic2dAAGrad 是 CANN ops-cv 中实现抗锯齿双三次上采样梯度回传的标准算子理解其 4×4 邻域加权求和的计算本质、掌握align_corners与scales的参数语义再配合两段式 aclnn 接口的标准调用范式即可在 Atlas A2/A3 与 Ascend 950 系列平台上正确、高效地完成图像上采样训练链路的反向计算。【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考