Kornia 修复 create_meshgrid 半精度归一化坐标在 eager 与 torch.compile 下的不一致
发布时间:2026/11/2 15:15:22 锦皓数字建站

计算机视觉深度学习图像处理【免费下载链接】kornia Geometric Computer Vision Library for Spatial AI项目地址https://gitcode.com/GitHub_Trending/ko/kornia点击查看免费下载导读本文深入解析 Kornia 一次针对数值一致性的重要修复issue #4030PR #4231create_meshgrid与create_meshgrid3d生成的float16/bfloat16半精度归一化坐标网格在 eager 执行与torch.compile编译执行下原本会得到不同的数值导致模型一旦被编译包裹采样网格就悄然发生漂移。文章将说明问题的根源多次舍入 vs 单次舍入、窄化后拓宽被编译器折叠、修复方案统一在float32中构建与归一化、仅做一次窄化、对HomographyWarper等下游算子的影响以及剩余的 CUDA 平台边界。读完你将理解半精度网格坐标的舍入机制并能在自己的训练/推理代码中规避同类陷阱。一、问题背景归一化坐标网格的两种生成路径create_meshgrid是 Kornia 几何变换的核心工具位于 kornia/geometry/grid.py它生成形状为(1, H, W, 2)的坐标网格。当normalized_coordinatesTrue时网格被归一化到[-1, 1]区间与 PyTorchtorch.nn.functional.grid_sample的约定保持一致。归一化公式为x_norm (xs / (size - 1) - 0.5) * 2其中xs是由torch.arange生成的像素索引 ramp0, 1, 2, ..., size-1。在本次修复之前create_meshgrid内部存在两条不同的执行路径eager 路径(xs / (size - 1) - 0.5) * 2被当作三次独立的半精度运算即除法、减法、乘法各自在float16/bfloat16下执行每步都做一次舍入graph-capture 路径torch.compile/torch.jit.trace捕获的图编译器Inductor把整条表达式链提升到float32计算、只在结果存储时舍入一次。与此同时代码在进入 capture 分支前会先把半精度 ramp 物化出来capture 分支再将其拓宽为工作精度——这种先窄化再拓宽narrow-then-widen的往返恰好被编译器视为冗余而折叠掉Inductor 直接保留了精确的整数索引而 eager 路径则是用已经舍入过的半精度分子去做除法。这两点叠加的后果是当网格宽度超过2**pp 为有效数位数bfloat16为 8 位、float16为 11 位时两条路径除法的分子已经不同——即 ramp 整数在超过2**p后无法被半精度精确表示eager 的半精度分子带上了舍入误差而编译路径的精确分子没有。二、量化影响模型被 torch.compile 包裹后网格悄然漂移由于上述机制一个半精度网格在模型被torch.compile包裹的瞬间就会改变数值。修复说明中给出的实测量级bfloat16[-1, 1]归一化范围网格宽度新旧路径最大差异300超过 bfloat16 的2**8 256最多0.01176000最多0.01560.0117 恰好是 bfloat16 在[-1, 1]区间上约 1 个 ulp2**-7 ≈ 0.0078至2**-6 ≈ 0.0156之间的数值宽度 6000 时偏差约 0.0156接近 2 个 ulp。对精度敏感的采样任务如特征点对齐、光流、深度图 warp而言这种因是否编译而产生的不可复现差异是难以排查的。值得强调的是差异并非编译器的 bug而是两条路径对同一数学表达式的舍入策略不同eager半精度逐步运算逐步舍入3 次舍入compiledfloat32整链运算仅存储时舍入1 次舍入eager 的窄化-拓宽往返制造了带误差的分子编译路径将其折叠并保留精确索引。三、修复方案float32 工作精度 单次窄化修复的核心思路在 kornia/geometry/grid.py 中清晰可见实现为三步ramp_dtype dtype if dtype is not None else torch.empty(0, devicedevice).dtype # 只有半精度且需要归一化时才拓宽 widened normalized_coordinates and ramp_dtype in (torch.float16, torch.bfloat16) work_dtype torch.float32 if widened else ramp_dtype xs torch.arange(width, devicedevice, dtypework_dtype) ys torch.arange(height, devicedevice, dtypework_dtype)统一工作精度当输入 dtype 是float16/bfloat16且normalized_coordinatesTrue时把work_dtype提升为float32ramp 直接以float32生成——eager 与 capture 分支看到的是同一份分子统一归一化算术无论 eager 还是 capture 分支都在float32下完成(xs / (size - 1) - 0.5) * 2或align_cornersFalse的半像素映射(2 * xs 1) / width - 1算术过程在所有路径上完全一致只窄化一次归一化完成后才在末尾做单次窄化回目标 dtypeif widened: xs xs.to(ramp_dtype) ys ys.to(ramp_dtype)这样eager、traced 与 compiled 三种执行模式得到完全一致的网格且 eager 数值移动到了正确舍入的一侧——这正是本变更属于breaking change的原因eager 路径下半精度网格的数值也发生了改变向正确舍入值靠拢。create_meshgrid3d采用了完全相同的策略见 kornia/geometry/grid.py。3.1 配套修改arange 取代 linspace 生成 ramp本次修复还顺带把 ramp 的生成从linspace改为arangekornia/geometry/grid.py 的注释说明了理由linspace会先把端点舍入到坐标 dtype再从舍入后的端点反向填充 ramp 上半部分因此只有当size - 1是可精确表示的整数size 2**p 1bfloat16 为 257、float16 为 2049时步长才精确为 1超出后每个索引会偏离正确舍入的列索引最多 1 ulp而 Inductor 将同一个调用降低为逐索引计算、正确舍入导致 eager 与 compiled 的 ramp 相差最多 1 ulparange同时能在导出export场景下保留动态输出长度linspace会对符号steps做特化。此外默认 dtype 通过torch.empty(0, devicedevice).dtype读取而不是torch.get_default_dtype()因为 TorchScript 没有后者的内建支持且对无输入的工厂调用TorchScript 优化执行器会在首次运行后做常量折叠若直接读取默认 dtype 会把 ramp 冻结在首次调用时的默认值上。带上device的empty张量使读取依赖输入参数从而避免折叠。3.2 位级不变性保证由于float32/float64和整数网格本来就不会被拓宽本次修复对它们位级完全不变bit-for-bit unchangedfloat32、float64网格widened恒为Falsework_dtype ramp_dtype执行路径与修复前一致整数网格与像素 rampnormalized_coordinatesFalse不做任何归一化运算不受影响align_corners两种约定True的像素中心映射与False的半像素边缘映射的公式在float32中都是精确执行的只有最终一次窄化落在目标 dtype 上。四、HomographyWarper预计算网格与即时网格的一致性HomographyWarperkornia/geometry/transform/homography_warper.py是网格问题的直接下游受益者。它内部存在两条采样网格来源预计算网格__init__中通过create_meshgrid(height, width, normalized_coordinates..., align_corners...)构建基础网格homography_warper.py随后precompute_warp_grid(src_homo_dst)调用warp_gridkornia/geometry/transform/imgwarp.py用transform_points变换得到缓存的_warped_grid即时网格forward收到src_homo_dst时走函数式homography_warpkornia/geometry/transform/imgwarp.py在调用时按输入 dtype 现算网格。修复前HomographyWarper的预计算网格在float32下构建、而即时路径的网格按输入 dtype半精度构建两条路径的半精度数值不同导致同一个 warp 在预计算与直接传入 homography两种用法下结果不一致。修复后两者共享create_meshgrid的 float32 工作精度逻辑预计算与即时路径在半精度下完全一致顺带修复了test_homography_warper在float16与bfloat16两个 dtype 下均通过test_translation[shape1]在float16下通过。五、残余边界与测试基线变化修复并非声称半精度在所有平台上达到位级一致文档明确指出了剩余边界CUDA float16最多仍有 1 个 ulp坐标 dtype 的 ulp差异因为 ATen 与 Triton 的float32除法并非位级相同除以size - 1时产生不同的舍入CUDA bfloat16bfloat16 有效位数少能吸收这一 ulp 级差异CPU 内核eager 与编译路径完全一致bit-exactLinux CPU 上的半精度基线测试随之更新。5.1 测试容忍度分析test_translation 的 atol1e-4测试 tests/geometry/transform/test_homography_warper.py 的test_translation以atol1e-4断言平移后的像素对齐。修复后该测试在bfloat16下新增了两个待记录xfail条目此前已有[shape1]、[shape3]在bfloat16下无法达到atol1e-4现在[shape0]、[shape2]也加入其中即bfloat16 下四种 shape 全部无法满足atol1e-4。修复说明给出了一个反直觉的原因旧网格之所以有两个 shape 碰巧通过并不是因为旧网格更精确而是因为旧网格更大的误差在经grid_sample的 bfloat16 反归一化unnormalize后恰好往返为一个精确的像素索引——即错误的网格与错误的采样舍入相互抵消。修复后网格数值移到正确舍入一侧这种巧合消失测试反而暴露了 bfloat16 本身的精度天花板在[-1, 1]区间上 bfloat16 的 ulp 约为 0.0078远超atol1e-4因此对 bfloat16 的整像素平移断言本就不可达。这些条目以 known-failure / xfail 方式记录在 testing/known_failure_xfails 与 testing/half_precision_xfails 中属于测试预期管理而非回归。六、迁移与工程启示对本变更的使用者而言需要关注以下几点eager 半精度网格数值会变化这是 breaking change 的核心——修复让 eager 结果向正确舍入一侧移动如果你的模型在半精度下对网格有紧耦合的数值依赖例如保存了依赖旧网格数值的权重或校准参数需要重新验证编译与非编译不再分叉torch.compile包裹模型前后的网格行为从此一致消除了此前编译后结果变化的隐性问题调试编译图时不再需要怀疑网格来源半精度的硬件边界仍在CUDA 下 float16 网格仍允许最多 1 ulp 的平台差异ATen 与 Triton 除法不同跨后端严格对比时应使用 bfloat16 或提升到 float32 工作精度断言精度要与 dtype 匹配如test_translation所示对 bfloat16 数据设置atol1e-4是不可达的半精度测试的容忍度应放宽到 dtype 的 ulp 量级。作为参考is_compiling()kornia/core/utils.py是 Kornia 判断是否处于torch.compile/torch.export捕获期的统一入口create_meshgrid的 capture 分支grid.py正是依赖它与torch.jit.is_tracing()来区分执行模式——理解这一守卫逻辑有助于把握 Kornia 中大量eager 与编译路径分叉代码的设计范式。赞分享计算机视觉深度学习图像处理【免费下载链接】kornia Geometric Computer Vision Library for Spatial AI项目地址https://gitcode.com/GitHub_Trending/ko/kornia点击查看免费下载相关推荐终极指南Shoelace动画工具之Animation注册表系统完全掌握终极指南Shoelace动画工具之Animation注册表系统完全掌握 Shoelace现更名为Web Awesome是一个强大的开源Web组件库其An人工智能计算机视觉深度学习图像处理Kornia 增强模块 torch.compile 兼容性修复共享编译缓存、张量化采样坐标与半精度梯度累积Kornia 增强模块 torch.compile 兼容性修复共享编译缓存、张量化采样坐标与半精度梯度累积 导读 本文围绕 Kornia 仓库 changel人工智能计算机视觉深度学习图像处理Kornia RectangleEraseGenerator 修复解析半精度下的安全位置采样与非负擦除坐标保持Kornia RectangleEraseGenerator 修复解析半精度下的安全位置采样与非负擦除坐标保持 本文以 Kornia 仓库 changelog计算机视觉深度学习图像处理上一篇Apereo CAS 必选属性认证策略Required Attributes Authentication Policy配置指南下一篇Talos Linux 镜像签名校验配置指南ImageVerificationConfig 完整解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。