news 2026/9/18 13:30:16

GridUnnormal 算子深度解析:CANN ops-cv 中 GridSample 链路的坐标反归一化实现与 GE 图模式调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GridUnnormal 算子深度解析:CANN ops-cv 中 GridSample 链路的坐标反归一化实现与 GE 图模式调用

GridUnnormal 算子深度解析:CANN ops-cv 中 GridSample 链路的坐标反归一化实现与 GE 图模式调用

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

导读

GridUnnormal 是 CANN ops-cv 开源算子库中服务于 GridSample 链路的坐标反归一化(unnormalize)算子:它以归一化采样坐标grid与同形状的尺寸辅助张量assist为输入,逐元素输出pos_base的小数偏移diff与整数采样位置position,为后续双线性/双三次采样的取点与插值提供基础。本文以 image/grid_unnormal/README.md 为骨架,结合算子原型、Infershape、Tiling、Kernel 与测试代码,系统讲解其计算公式、参数约束、产品支持范围、源码级实现原理以及 GE IR 图模式调用样例,帮助开发者理解并正确使用这一 GE 图内部算子。

产品支持情况

GridUnnormal 算子在不同硬件产品上的支持情况如下(以仓库 README 为准):

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

从源码结构看,算子 Kernel 与 Tiling 均位于arch35目录(op_kernel/arch35、op_host/arch35),对应 DAV_3510 架构(即 Ascend 950 系列)的寄存器编程实现,这与 README 中 Ascend 950 系列“√”的支持状态一致。

功能说明:GridSample 链路中的坐标反归一化

算子定位

GridUnnormal 是 GridSample 链路中的坐标反归一化算子,处理的是"把归一化采样坐标还原为输入图像上的真实浮点采样位置"这一环节。其输入包含两部分:

  • grid:归一化采样坐标,元素取值通常落在[-1, 1]区间;
  • assist:与grid同 shape 的尺寸辅助张量,每元素对应输入在某维上的尺寸值(例如图像的高或宽)。

算子输出两个结果:pos_base的小数部分diff,以及pos_base的下取整结果position(int32 整数采样位置),两者可直接作为后续插值采样的基础。

计算公式

对每个元素独立计算:

t = (grid + 1) * 0.5 pos_base = align_corners ? t * (assist - 1) : t * assist - 0.5 position = floor(pos_base) diff = pos_base - floor(pos_base)

其中floor向负无穷取整语义执行,不能用向零截断(trunc)替代。这一约束在 Kernel 实现中有直接体现:grid_unnormal.h 中定义了kCastF32ToI32Floor,其RoundMode显式指定为CAST_FLOOR,保证负数坐标按 floor 语义下取整。

两种align_corners模式分别对应 GridSample 语义中的两种坐标对齐约定:

  • align_corners = true:输入输出张量角点像素中心对齐,坐标映射为t * (assist - 1)
  • align_corners = false(默认):不做角点对齐,坐标映射为t * assist - 0.5

数值验证(golden 实现)

golden.py 提供了两套参考实现用于交叉验证:_grid_unnormal_golden_compute采用与 README 等价的代数变形(pos_base = ((grid + 1) * assist - 1) / 2),_grid_unnormal_third_party_compute则严格按 README 公式逐字实现(pos_base = normalized * assist - 0.5),并通过 torch 的torch.floor计算下取整结果。测试容差配置中,diff输出使用cross_check标准(float32/float16 均为 L1 级别),position输出使用binary_equal(整数逐位相等)标准。

参数说明

参数名输入/输出/属性描述数据类型数据格式
grid输入归一化采样坐标。支持 4D 静态 shape、动态 shape 和编译期未知 rank;rank 确定时必须为 4 且末维为 2;支持总元素数为 0 的空 Tensor。float16float32ND
assist输入每元素对应的输入尺寸辅助值。必须与gridshape、dtype 完全一致,不支持广播;rank 确定时必须为 4 且末维为 2。float16float32ND
diff输出pos_base的小数部分,shape 与grid一致,dtype 与grid一致。float16float32ND
position输出pos_base的下取整结果,shape 与grid一致。int32ND
align_corners属性可选属性,默认false。为true时按t * (assist - 1)计算;为false时按t * assist - 0.5计算。bool-

上述参数定义在算子原型 grid_unnormal_proto.h 中通过REG_OP(GridUnnormal)声明,与 grid_unnormal_def.cpp 中的 OpDef 注册完全一致:输入输出均为REQUIREDalign_cornersOPTIONAL且默认false。dtype 组合由 2 行 dtype 表描述:grid/assist/diff = {fp16, fp32}position = {int32, int32},即 diff 跟随 grid 的 dtype,position 固定为 int32。

约束说明

  • gridassist的 shape、dtype 必须一致,不支持广播;
  • rank 确定时,gridassist必须为 4D Tensor,shape 为[batch, height, width, 2](末维 2 表示坐标分量);
  • 仅支持 ND 格式;非连续 Tensor 作为用户可见接口不涉及;
  • diff的 dtype 跟随gridposition固定为 int32;
  • 中间计算使用 fp32;fp16 输入会提升到 fp32 计算,diff再回写为 fp16;
  • 总元素数为 0 的空 Tensor 支持空进空出,设备侧不访问数据;
  • 非有限输入(NaN/Inf)以及floor(pos_base)超出 int32 表示范围不属于本算子支持域;
  • 本算子为 GE 图内部算子,不提供 aclnn、torch、TensorFlow、ONNX、Caffe 对外接口。

这些约束在 Infershape 与 Tiling 代码中均有对应检查:grid_unnormal_infershape.cpp 会校验 rank 必须为 4、末维必须为 2(未知维度除外)、grid 与 assist 的已知维度必须逐维相等;grid_unnormal_tiling_arch35.cpp 在编译期进一步校验存储 shape 完全一致且两个输入 dtype 必须相等(L150-L156)。golden.py 的注释也明确指出:该 OpDef 为 aclnn_exclude,未交付 torch_npu 绑定,也未交付 TensorFlow/ONNX 解析器与融合 pass。

动态 shape 支持细节

  • 编译期未知 rank 通过-2标记表示:Infershape 中IsUnknownRank检查shape->GetDimNum() == 1 && shape->GetDim(0) == -2时直接放行;
  • 动态 shape 场景下,末维-1UNKNOWN_DIM)被允许(只要不等于 2 之外的其他已知值),Tiling 阶段再基于实际存储 shape 计算总元素数;
  • 算子声明中DynamicRankSupportFlag(true)DynamicShapeSupportFlag(true)DynamicCompileStaticFlag(true)(grid_unnormal_def.cpp)确认了上述能力的编译期开启状态。

调用说明:GE IR 图模式调用

GridUnnormal 作为 GE 图内部算子,通过构图方式在图中使用。README 给出的调用方式为:

调用方式调用样例说明
图模式调用test_geir_grid_unnormal通过本目录的算子原型构图方式调用 GridUnnormal 算子。

调用样例核心流程

test_geir_grid_unnormal.cpp 演示了完整的 GE IR 图模式调用流程,关键步骤包括:

  1. 创建算子节点op::GridUnnormal("gridUnnormal_1"),基于 grid_unnormal_proto.h 生成的op::GridUnnormal类;
  2. 构造输入占位节点op::Data("grid").set_attr_index(0)op::Data("assist").set_attr_index(1),通过update_input_desc_x声明 FORMAT_ND、DT_FLOAT 的 4D 描述;
  3. 绑定输入gridUnnormal.set_input_grid(grid)gridUnnormal.set_input_assist(assist)
  4. 设置属性gridUnnormal.set_attr_align_corners(false)
  5. 声明输出描述update_output_desc_diff(DT_FLOAT)与update_output_desc_position(DT_INT32);
  6. 构图执行graph.SetInputs(inputs).SetOutputs(outputs)后,经Session->AddGraph(graphId, graph)Session->RunGraph(graphId, input, output)完成执行,其中全局配置ge.exec.deviceId=0ge.graphRunMode=1

样例数值推演

样例配置为align_corners = falsegridassist均为DT_FLOAT、shape[1, 6, 5, 2],grid 每元素填0.3f、assist 每元素填5.0f,期望输出:

t = (0.3 + 1) * 0.5 = 0.65 pos_base = 0.65 * 5 - 0.5 = 2.75 position = floor(2.75) = 2 (int32) diff = 2.75 - 2 = 0.75

diff输出全为 0.75(float),position输出全为 2(int32)。这个手算推演与 golden.py 的 torch 参考实现可以相互印证,可直接作为自测基准。

源码实现原理

Kernel 实现:RegBase 寄存器编程范式

grid_unnormal.cpp 是 kernel 入口,采用 ops-cv 非模板extern "C"约定:dtype 由DTYPE_GRID编译期实例化(fp16/fp32 各一份),align_corners走 tilingdata 运行时分支。入口通过REGISTER_TILING_DEFAULT注册默认 Tiling,随后调用NsGridUnnormal::GridUnnormalKernel<DTYPE_GRID>完成初始化与计算。

grid_unnormal.h 中GridUnnormalKernel<T>的实现要点:

  • 数据搬运:GM↔UB 使用TQue+DataCopyPadCopyIn/CopyOut),每个核心按coreStart_/coreLen_处理属于自己的元素区间,双缓冲(kBufNum = 2)隐藏搬运与计算延迟;
  • 向量计算:在__VEC_SCOPE__内用 MicroAPI 寄存器算子(RegTensor/MaskReg/Adds/Muls/Mul/Sub/Cast)完成逐元素计算。t = (grid + 1) * 0.5通过Adds加 1 再Muls乘 0.5 实现;align_corners为 true 时先Adds(aReg, -1)Mul,为 false 时先MulAdds(-0.5)(模板参数AlignCorners编译期展开);
  • 统一 fp32 中间计算:fp16 输入由LoadOneTensorForDtypeT载入即升为 fp32,StoreOneTensorForDtypeT存回时再降为 fp16——与 README"中间计算使用 fp32"的约束对应;
  • floor 语义保证position使用Cast<int32_t, float, kCastF32ToI32Floor>(RoundMode 为CAST_FLOOR)保证向负无穷下取整,随后通过Cast<float, int32_t>还原浮点 floor 值做Sub得到diff
  • 寄存器位宽:向量寄存器 256B,fp32 通道每拍 64 元素(kFp32PerLoop = 64),repeatTimes按 64 元素对齐循环。

Tiling 实现:扁平化分核 + UB 切分

grid_unnormal_tiling_arch35.cpp 实现 host 侧 Tiling,策略为"纯 elementwise,按总元素数扁平分核":

  1. 通过PlatformAscendC获取 AIV 核数coreNum与 UB 大小ubSize(GetPlatformInfo);
  2. 校验存储 shape 与 dtype 后计算总元素数total(GetInputInfo);
  3. 依据 UB 容量与每元素字节数(BytesPerElem:两个输入 + diff 输出按 dtype 字节计、position 按 int32 计,均乘双缓冲数 2)计算ubFactor,并向下对齐到 64 元素整数倍(CalcUbFactor),预留 8KB UB 余量;
  4. perCore = ceil(total / coreNum)向上取整均分到各核,usedCores收窄为实际需要的核数并设置SetBlockDim(FillNormalTiling);
  5. 空 Tensor(total <= 0)走 FillEmptyTiling:totalNum=0blockDim=1、workspace 为 0,实现"空进空出、设备侧不访问数据"。

TilingData 结构定义在 grid_unnormal_tiling_data.h:totalNum(总元素数)、perCoreNum(每核元素数)、ubFactor(单次 UB tile 元素数)、alignCorners(0/1 分支标志)。

算子配置与编译产物

op_host/config/ascend950/grid_unnormal_binary.json 描述了 ascend950 平台预编译产物的规格:为 fp16 与 fp32 两种 dtype 各生成一份二进制(GridUnnormal_1d9cf9915315ae9f8e867b978473f984GridUnnormal_f7372211154d6f1cad01274230b0fdf9),输入输出 shape 均为-2(编译期未知 rank,动态 shape 入口),align_corners属性在编译产物中值为 null(运行时决定)。同目录下的grid_unnormal_simplified_key.ini提供简化 key 配置。

测试验证

仓库为 GridUnnormal 提供了 host 侧与 kernel 侧两层单元测试:

  • Infershape/Tiling 单测:test_grid_unnormal_infershape.cpp 验证形状与 dtype 推导逻辑;test_grid_unnormal_tiling.cpp 验证 Tiling 数据与分核结果;
  • Kernel 单测:test_grid_unnormal.cpp 配合 gen_data.py 生成输入数据、compare_data.py 与 golden 结果比对,验证设备侧实际计算结果;
  • golden 参考:golden.py 提供 torch 参考实现,float32/float16 输出按 L1 交叉校验、int32 输出按逐位相等校验。

总结

GridUnnormal 作为 GridSample 链路的坐标反归一化算子,以"两输入(grid/assist)、两输出(diff/position)、一个可选属性(align_corners)"的简洁接口,在 Ascend 950(arch35)平台上以统一 fp32 中间精度、CAST_FLOOR下取整语义、扁平化分核 Tiling 与 RegBase 寄存器编程实现了高效且行为可预期的坐标还原。理解其计算公式与约束(4D、末维为 2、shape/dtype 一致、ND 格式、floor 语义)是正确使用的前提;如需在图中集成该算子,可参考 test_geir_grid_unnormal.cpp 的 GE IR 构图方式,并通过 golden.py 的参考实现做数值验证。

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

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

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

MoveIt 运动规划:CHOMP 轨迹优化与避障调参实战

一、先搞清楚 CHOMP 在 MoveIt 里到底是"谁"我见过太多人第一次翻到moveit_config/config/目录下的chomp_planning.yaml&#xff0c;第一反应是"哦&#xff0c;又一个 Planner&#xff0c;跟 OMPL 里的 RRTConnect、BiTRRT 是一类东西&#xff0c;换一个名字而已…

作者头像 李华
网站建设 2026/9/18 13:26:46

RHEL/CentOS 7 最小化安装后必做的30项基础配置

简介&#xff1a;本资源是一份面向Linux系统运维工程师与CentOS/RHEL初学者的实战配置指南&#xff0c;聚焦最小化安装后的30项关键初始化操作&#xff0c;覆盖生产环境部署必备技能。文档以清晰条目形式组织&#xff0c;涵盖红帽订阅注册、静态IP与主机名配置、系统更新、基础…

作者头像 李华
网站建设 2026/9/18 13:25:59

Docker 容器化 MySQL 8.0 GTID 主从复制实战与排错

1. 我为什么坚持用 Docker 跑 MySQL 主从而不是装两台虚拟机前阵子帮同事在测试环境里搭一套 MySQL 主从复制&#xff0c;他原本的计划是开两台虚拟机&#xff0c;各自装一遍 MySQL 8.0&#xff0c;再手动改配置文件、开防火墙端口、配账号。我看了眼他那台 16G 内存的开发机&a…

作者头像 李华
网站建设 2026/9/18 13:24:24

Download gradle超时

Android Studio经常会出现一直在Download gradle&#xff0c;可能是无法找到资源&#xff0c;可以按照如下方法离线下载 进入https://mirrors.cloud.tencent.com/gradle/下载gradle-wrapper.properties文件中所需要版本压缩包复制到C:\Users\用户名.gradle\wrapper\dists\gradl…

作者头像 李华
网站建设 2026/9/18 13:23:46

基于Unity与C#的瓯绣3D虚拟展馆漫游系统实现

前几年接手过几个地方非遗文化数字化的活儿&#xff0c;说实话&#xff0c;一开始我是拒绝的。因为这类项目十有八九最后做成了一个"能点的电子画册"——几张高清图加一段文字说明&#xff0c;再配点背景音乐&#xff0c;交差完事。但瓯绣这个题材不太一样&#xff0…

作者头像 李华