
人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载aclnnMaxUnpool3d 是 CANN ops-nn 神经网络算子库中用于 3D 最大反池化Max Unpooling的上采样算子功能上是 aclnnMaxPool 在 3D 场景下的逆运算依据 indices 索引把输入 self 的元素放回由 outputSize 决定尺寸的输出张量 outRef 的对应位置其余位置置 0。本文基于 index/scatter_elements/docs/aclnnMaxUnpool3d.md 并结合仓库源码完整介绍其产品支持情况、计算公式、两段式接口原型、全部参数与错误码并深入剖析其基于 ScatterElements 的底层实现与测试验证帮助开发者快速完成调用、规避入参陷阱。产品支持情况aclnnMaxUnpool3d 在不同产品形态上的支持情况如下产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品不支持Atlas 训练系列产品不支持可以看到该算子仅面向 Ascend 950 与 A2/A3 系列产品开放使用前需先确认目标设备的算力平台归属。功能说明算子功能aclnnMaxUnpool3d 是 aclnnMaxPool 在 3D 场景下的逆运算由outputSize决定outRef的 D、H、W 轴大小根据indices索引在outRef中填入self的元素值其余位置全部设置为 0。也就是说MaxPool3d 在做下采样时记录每个最大值的位置indices而 MaxUnpool3d 则利用这些索引把数值放回原位得到一个更大尺寸但大部分位置为 0 的稀疏上采样结果。计算公式输入为 4 维各维度依次为 N、D、H、WNBatch为批量大小、DDepth为特征图深度、HHeight为特征图高度、WWidth为特征图宽度$$ outRef[N][indices[N][i]] self[N][i] $$输入为 5 维各维度依次为 N、C、D、H、WCChannels为特征图通道数$$ outRefN][C][indices[N][C][i]] self[N][C][i] $$其中outRef、indices和self是最后两轴4 维场景或最后三轴5 维场景合为一轴后 reshape 得到的i ∈ [0, D×H×W)。从源码看这一步 reshape 正是算子在设备侧实际执行的第一步aclnn_max_unpool3d.cpp 将输入与输出分别压缩为(N, C, D*H*W)与(N, C, outD*outH*outW)的形状随后在最后一个维度上按索引做散射写入。两段式接口与函数原型aclnnMaxUnpool3d 遵循 CANN 算子库通用的两段式接口设计先调用aclnnMaxUnpool3dGetWorkspaceSize获取计算所需 workspace 大小以及包含了算子计算流程的执行器executor再调用aclnnMaxUnpool3d执行计算。第一段接口原型aclnnStatus aclnnMaxUnpool3dGetWorkspaceSize( const aclTensor* self, const aclTensor* indices, const aclIntArray* outputSize, const aclIntArray* stride, const aclIntArray* padding, aclTensor* outRef, uint64_t* workspaceSize, aclOpExecutor** executor)第二段接口原型aclnnStatus aclnnMaxUnpool3d( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)对应的头文件声明位于 index/scatter_elements/op_api/aclnn_max_unpool3d.h两段接口均以ACLNN_API导出供上层以 C 语言 ABI 方式调用。aclnnMaxUnpool3dGetWorkspaceSize 参数说明第一段接口完成入参校验并计算 workspace 大小其参数说明如下参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 TensorselfaclTensor*输入公式中的 self表示待转换的目标张量数据类型与 outRef 一致shape 与 indices 保持一致4 维时各维度依次为 N、D、H、W5 维时依次为 N、C、D、H、WFLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT8、DOUBLEND4-5√indicesaclTensor*输入公式中的 indices表示输入 self 的元素在输出结果中的索引位置shape 与 self 保持一致4 维时各维度依次为 N、D、H、W5 维时依次为 N、C、D、H、WINT64、INT32ND4-5√outputSizeaclIntArray*输入输出结果在 D、H、W 维度上的空间大小size 大小为 3三个元素的乘积需大于等于 self 在 D、H、W 维度上的 size 乘积----strideaclIntArray*输入最大池化窗口在 D、H、W 维度上的步长预留参数当前版本不参与计算需传入 size 为 3、值大于 0 的 Host 侧 aclIntArray----paddingaclIntArray*输入最大池化窗口在 D、H、W 维度上的填充值预留参数当前版本不参与计算需传入 size 为 3 的 Host 侧 aclIntArray----outRefaclTensor*输出公式中的 outRef即上采样结果数据类型与 self 一致4 维时各维度依次为 N、D、H、W5 维时依次为 N、C、D、H、WFLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT8、DOUBLEND4-5√workspaceSizeuint64_t*输出需要在 Device 侧申请的 workspace 大小由第一段接口计算返回随后按该值调用 aclrtMalloc 申请----executoraclOpExecutor**输出op 执行器包含算子计算流程第二段接口直接使用----关键约束解读dtype 白名单self/outRef 支持 FLOAT、FLOAT16、INT16、INT32、INT64、INT8、UINT8、DOUBLEindices 仅支持 INT64、INT32。这与源码中定义的 DTYPE_SUPPORT_LIST 和 INDEX_DTYPE_SUPPORT_LIST 完全一致。预留参数stride 与 padding 虽然出现在函数签名中但当前版本不参与计算属于为与 MaxPool 参数语义对齐而保留的占位参数仍需传入符合 size 约束的合法值。outRef 必须是连续张量源码中的 CheckOutContiguous 明确校验outRef必须为连续contiguous张量否则直接返回ACLNN_ERR_PARAM_INVALID。返回值与错误码两段接口均返回aclnnStatus状态码具体定义参见 aclnn返回码。第一段接口完成入参校验以下场景会报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001self、indices、outputSize、stride、padding 或 outRef 是空指针ACLNN_ERR_PARAM_INVALID161002self 和 indices 的数据类型不在支持范围之内ACLNN_ERR_PARAM_INVALID161002self 和 outRef 的数据类型不一致ACLNN_ERR_PARAM_INVALID161002self 的维度不为 4 维或 5 维ACLNN_ERR_PARAM_INVALID161002self 和 indices 的 shape 不一致ACLNN_ERR_PARAM_INVALID161002outputSize 的 size 大小不等于 3ACLNN_ERR_PARAM_INVALID161002outputSize 的三个元素乘积小于 self 在 D、H、W 维度上的 size 乘积ACLNN_ERR_PARAM_INVALID161002stride 的 size 大小不等于 3ACLNN_ERR_PARAM_INVALID161002padding 的 size 大小不等于 3ACLNN_ERR_PARAM_INVALID161002self 在 C、D、H、W 维度上的 size 不大于 0ACLNN_ERR_PARAM_INVALID161002stride 的元素值不大于 0ACLNN_ERR_PARAM_INVALID161002outRef 在 N、C 维度上的 size 与 self 不完全相同ACLNN_ERR_PARAM_INVALID161002outRef 在 D、H、W 维度上的 size 与 outputSize 中的三个元素值不相等这些校验逻辑与源码中 CheckParams 的执行顺序一一对应空指针检查 → outRef 连续性检查 → dtype 检查 → self/indices shape 检查 → self 元素值合法性检查 → outputSize/stride/padding 的 size 与取值检查 → outRef shape 与 outputSize 的一致性检查。aclnnMaxUnpool3d 参数说明第二段接口在获取到 workspace 与 executor 后执行实际计算参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnMaxUnpool3dGetWorkspaceSize 获取executor输入op 执行器包含算子计算流程stream输入指定执行任务的 Stream从源码实现看第二段接口 aclnnMaxUnpool3d 只是将参数透传给通用的CommonOpExecutorRun完成异步下发真正的计算逻辑全部封装在第一段接口构造的执行器之中。源码级实现原理ScatterElements 路由与 reshape 流程该算子虽然在index/scatter_elements目录下提供 API但它的计算本质是按索引散射写入仓库 README 明确指出aclnnMaxUnpool3d 通过调用 ScatterElements 算子的 L0 接口实现在 Ascend 950 上实际路由到 ScatterElementsV2 算子。结合 aclnn_max_unpool3d.cpp第一段接口内部构造的计算图为Contiguous 归一化对 self、indices、outRef 分别调用l0op::Contiguous将非连续张量转换为连续布局这也是参数表中非连续 Tensor标记为 √ 的原因Reshape 压缩self 与 indices 统一 reshape 为(N, C, D*H*W)outRef reshape 为(N, C, outD*outH*outW)把空间维度合并为单一轴ZerosLike 初始化以 reshape 后的 outRef 为模板生成全零张量保证非索引位置为 0ScatterElements 散射在 axis2即合并后的空间轴上以reductionnone模式执行ScatterElements(zeroOut, indicesReshape, selfReshape)将 self 的每个元素写入 indices 指向的位置Reshape 还原 ViewCopy把散射结果恢复为 outRef 的原始 shape再通过 ViewCopy 拷贝到调用方提供的 outRef 中。这一设计意味着 aclnnMaxUnpool3d 并不需要单独的 kernel 实现而是复用了成熟的 ScatterElements 底层算子因此天然具备确定性见下文约束说明且能够覆盖 4 维与 5 维两种输入形态。另外IsEmpty 快速路径 表明当 self 为空张量时第一段接口直接返回workspaceSize 0并成功结束不会执行后续构图。约束说明确定性计算aclnnMaxUnpool3d 默认为确定性实现即在相同输入与运行环境下多次执行结果完全一致便于结果比对与问题复现。调用示例示例代码如下完整编译与运行流程请参考编译与运行样例。示例以 4 维输入演示self 形状为(1, 1, 2, 2)输出形状为(1, 1, 4, 4)即把 2×2 的输入按索引上采样到 4×4 的输出。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_max_unpool3d.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 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初始化参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {1, 1, 2, 2}; std::vectorint64_t outShape {1, 1, 4, 4}; void* selfDeviceAddr nullptr; void* indicesDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* out nullptr; aclTensor* indices nullptr; std::vectorfloat selfHostData {1, 2, 3, 4}; std::vectorfloat outHostData {0, 0, 0, 0.0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0, 0, 0}; std::vectorint64_t indicesHostData {3, 8, 11, 13}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建indices aclTensor ret CreateAclTensor(indicesHostData, selfShape, indicesDeviceAddr, aclDataType::ACL_INT64, indices); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); std::vectorint64_t arraySize1 {1, 4, 4}; const aclIntArray *outputSize aclCreateIntArray(arraySize1.data(), arraySize1.size()); CHECK_RET(outputSize ! nullptr, return ACL_ERROR_INTERNAL_ERROR); std::vectorint64_t arraySize2 {1, 2, 3}; const aclIntArray *stride aclCreateIntArray(arraySize2.data(), arraySize2.size()); CHECK_RET(stride ! nullptr, return ACL_ERROR_INTERNAL_ERROR); const aclIntArray *padding aclCreateIntArray(arraySize2.data(), arraySize2.size()); CHECK_RET(padding ! nullptr, return ACL_ERROR_INTERNAL_ERROR); // 3. 调用CANN算子库API需要修改为具体的API名称 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnMaxUnpool3d第一段接口 ret aclnnMaxUnpool3dGetWorkspaceSize(self, indices, outputSize, stride, padding, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnMaxUnpool3dGetWorkspaceSize 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); } // 调用aclnnMaxUnpool3d第二段接口 ret aclnnMaxUnpool3d(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnMaxUnpool3d 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(outShape); std::vectorfloat outData(size, 0); ret aclrtMemcpy(outData.data(), outData.size() * sizeof(outData[0]), outDeviceAddr, size * sizeof(outData[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(out[%ld] is: %f\n, i, outData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(out); aclDestroyTensor(indices); aclDestroyIntArray(outputSize); aclDestroyIntArray(stride); aclDestroyIntArray(padding); // 7. 释放device资源 aclrtFree(selfDeviceAddr); aclrtFree(indicesDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }该示例的运行逻辑self 的 4 个元素{1, 2, 3, 4}分别被写入 4×4 输出扁平化后的位置 3、8、11、13其余 12 个位置保持为 0。可以直观验证outRef[N][indices[N][i]] self[N][i]的计算公式。示例要点提示indices 的值域示例中 indices 取{3, 8, 11, 13}均落在[0, 4*4)区间内即合并后的输出空间轴索引对应上采样后每个原始元素应放置的扁平位置。stride/padding 传参示例中二者均传{1, 2, 3}size 为 3、值大于 0满足预留参数的最低校验要求。输出初始值outHostData 全部初始化为 0即便不初始化算子内部也会通过 ZerosLike 清零非索引位置这里显式给出便于结果比对。测试与验证仓库为 aclnnMaxUnpool3d 提供了完整的两级测试单元测试index/scatter_elements/tests/ut/op_host/test_aclnn_max_unpool3d.cpp 覆盖了大量入参校验场景与上文错误码表一一对应空指针场景case_self_nullptr、case_indices_nullptr、case_out_nullptr均断言返回ACLNN_ERR_PARAM_NULLPTR非法 dtypecase_bfloat16、case_bool、case_complex64、case_complex128均断言返回ACLNN_ERR_PARAM_INVALID证明 BF16、BOOL、复数类型不在支持白名单内非法 shapecase_self_shape2self 为 2 维、case_indices_shape3indices 与 self 维度不一致、case_neg1_tensor含 -1 动态维度均返回ACLNN_ERR_PARAM_INVALID非法 IntArraycase_output_size3outputSize 仅 2 个元素、case_stride_size4、case_padding_size4size 不等于 3、case_neg1_output_sizeoutputSize 含负值均返回ACLNN_ERR_PARAM_INVALID空张量case_0_tensor断言返回ACL_SUCCESS验证了源码中的空张量快速路径。ST 测试index/scatter_elements/tests/st/aclnnMaxUnpool3d/executor_aclnnMaxUnpool3d.py 用 PyTorch 的Tensor.scatter_在 CPU/NPU 上构造参考实现先将张量 reshape 为(N, C, -1)扁平形态再在最后一维做 scatter用于与算子输出做数值比对。该参考实现与算子内部reshape ScatterElements的计算流程完全同构进一步印证了前文对实现原理的分析。总结aclnnMaxUnpool3d 是 CANN ops-nn 中实现 3D 最大反池化的标准接口功能上作为 aclnnMaxPool 的逆运算通过outputSize决定输出空间尺寸、以indices还原最大值位置、其余位置补零接口上采用两段式设计先经aclnnMaxUnpool3dGetWorkspaceSize完成校验并申请 workspace再经aclnnMaxUnpool3d在指定 Stream 上异步执行。理解其Contiguous → Reshape → ZerosLike → ScatterElements → ViewCopy的底层计算图有助于开发者正确构造 self/indices/outputSize 参数、预判常见错误码并快速定位基于该接口实现的各类上采样场景问题。赞分享人工智能算子库深度学习CANNAscend【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-nn点击查看免费下载相关推荐CANN ops-nn 算子开发指南aclnnHardShrink 两段式接口详解与 NPU 实现原理CANN ops nn 算子开发指南aclnnHardShrink 两段式接口详解与 NPU 实现原理 HardShrink硬收缩是一种逐元素稀疏化激活函人工智能算子库深度学习CANNAscendCANN ops-nn 算子详解aclnnSquaredRelu 两段式接口使用指南与实现原理CANN ops nn 算子详解aclnnSquaredRelu 两段式接口使用指南与实现原理 本文以 CANN ops nn 仓库中 aclnnSquare人工智能算子库深度学习CANNAscendCANN ops-nn ForeachLog2 算子详解aclnnForeachLog2 两段式接口使用与实现原理CANN ops nn ForeachLog2 算子详解aclnnForeachLog2 两段式接口使用与实现原理 本文以 CANN ops nn 算子库中的人工智能算子库深度学习CANNAscend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考