ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kornia 修复 RandomTransplantation 在 MPS 上的移植失效:空 excluded_labels 触发的 all() 归约陷阱与规避方案

Kornia 修复 RandomTransplantation 在 MPS 上的移植失效:空 excluded_labels 触发的 all() 归约陷阱与规避方案 计算机视觉深度学习人工智能图像处理【免费下载链接】kornia 空间人工智能的几何计算机视觉库项目地址https://gitcode.com/kornia/kornia点击查看免费下载本文围绕 Kornia 的变更记录片段changelog.d/migration-101.fixed.md对应 PR #4160展开深入剖析RandomTransplantation/RandomTransplantation3D在 Apple MPS 后端上移植无效、输出等于输入这一隐性缺陷的根因、修复策略与验证方式。读完本文你将理解批量图像/掩码移植增强的完整机制掌握空张量归约在不同后端的语义差异并能复现与验证该修复在 源码 与 测试 中的落点。一、问题背景从一条 fixed 变更记录说起Kornia 使用 Towncrier 管理变更记录每个对用户可见的改动在 changelog.d 目录下以PR.type.md形式提交片段发布时再合并进CHANGELOG.md。其中migration-*前缀的文件是迁移工作流引入时的一次性遗留例外保留了当时已存在于 Unreleased 区块的条目。migration-101.fixed.md记录的正是这样一个真实缺陷RandomTransplantation和RandomTransplantation3D在不传excluded_labels时在 MPS 上什么都没移植PyTorch 的 MPS 后端对空的 excluded-label 轴执行all()归约会得到未定义值通常为False于是每个供体标签都被过滤掉输出等于输入。现在当没有需要排除的标签时直接跳过该过滤。 (#4160)这条记录包含三个关键信息缺陷的现象MPS 上移植失效、根因空轴上的all()归约在后端间语义不一致、修复方式无标签可排除时跳过过滤。下面逐一深入。二、RandomTransplantation批量图像之间的移植增强要理解这个 bug先要弄清楚被修复的对象是什么。RandomTransplantation定义于 kornia/augmentation/_2d/mix/transplantation.py其 docstring 明确了算法语义在批内图像之间随机移植copy paste图像特征与对应的分割掩码。该增强来自论文Semantic segmentation of surgical hyperspectral images under geometric domain shiftssellner2023semantic。其工作流程分为三步依据概率参数p从批中选出若干张图像作为受体acceptor对每个受体取批中下一位的图像作为供体donor采用循环取模方式donor (acceptor - 1) mod B从供体中随机选取一个标签将其对应的图像特征与分割掩码移植到受体上。构造函数签名见源码 transplantation.py展示了全部可配置项def __init__( self, excluded_labels: Optional[Union[Sequence[int], torch.Tensor]] None, p: float 0.5, p_batch: float 1.0, data_keys: Optional[list[str | int | DataKey]] None, ) - None:参数默认值作用excluded_labelsNone不允许从供体移植的标签序列。常用于只标注了部分区域的图像把未标注区域对应的标签索引排除避免把背景/未知区域当作对象移植过去。若供体中没有剩余可用标签则什么都不移植p0.5单张图像应用增强的概率控制批中有多少图像成为受体p_batch1.0整个批次应用增强的概率按批控制增强概率data_keys[input, mask]输入张量的类型序列至少需要一个mask张量未指定时默认第一个张量为DataKey.INPUT、第二个为DataKey.MASK仅接受input与mask此外还有两条重要约束必须提供分割掩码批内所有图像都要有掩码且至少部分对象被标注否则没有可移植的内容2D/3D 使用差异直接调用RandomTransplantation()(...)时支持任意空间维度含 3D若包在AugmentationSequential中2D 用RandomTransplantation、3D 用RandomTransplantation3D。后者定义在 kornia/augmentation/_3d/mix/transplantation.py接口与 2D 版本完全一致仅继承自AugmentationBase3D。容器层的特殊处理在 kornia/augmentation/container/ops.py 中有明确注释RandomTransplantation属于需要完整输入才能计算参数的变换因此AugmentationSequentialOps会先对其调用module.params_from_input(...)聚合出受体/供体索引、选中标签与像素选择掩码再逐个张量应用。三、缺陷现象MPS 上输出等于输入在未指定excluded_labels即传入None构造函数内部会将其规范化为空列表[]再转换为空张量torch.tensor([])时理论上所有供体标签都应当可移植p1.0时每张图像都应收到来自相邻图像的移植输出应与输入不同。但在 MPS 后端上实际行为是什么都没移植——每个供体标签都被过滤掉selected_labels为空输出与输入完全一致。这种静默失效比直接报错更危险训练流程照常运行但增强退化为恒等映射模型实际上没有获得任何移植增强的数据多样性且不会产生任何可见的错误提示。四、根因剖析空轴上的 all() 归约后端间语义不一致修复所指向的根因位于params_from_input中的标签过滤逻辑。修复前的代码如下所示当前仓库中已被修复版本取代注释即解释了原因labels current_mask.unique() # 修复前无条件执行过滤 labels labels[(labels.view(1, -1) ! self.excluded_labels.view(-1, 1)).all(dim0)]逐层拆解这条过滤表达式labels.view(1, -1)把供体掩码中的唯一标签整理为形状(1, N)self.excluded_labels.view(-1, 1)把待排除标签整理为形状(M, 1)两者广播比较得到一个(M, N)的布尔张量第i行第j列为True表示第j个标签不等于第i个被排除标签all(dim0)沿排除标签轴归约只有当某标签不等于任何被排除标签时才保留即完成集合差运算。当excluded_labels为空M 0时比较结果张量的第一个维度长度为 0all(dim0)变成对空轴的归约。问题正出在这里在 CPU 与 CUDA 后端上对空轴执行all()返回True数学上空集合的所有元素都满足条件为真因此所有标签都通过过滤行为符合预期而在 PyTorch 的 MPS 后端上空轴归约的语义未定义实际往往返回False于是所有标签都被判定为属于被排除集合而全部丢弃。源码注释还进一步指出该问题在 PyTorch 2.9 的 MPS 上可复现且其结果甚至可能因进程而异详见下文的测试注释。正是这种跨后端语义不一致 结果不确定的组合让该缺陷极难在 CPU/CUDA 环境下被发现。五、修复方案无事可做时直接跳过修复策略非常朴素而有效当没有标签需要排除时直接跳过整个过滤步骤。当前仓库中的修复代码transplantation.pyif selected_labels not in params: if self.excluded_labels.device ! mask.device: self.excluded_labels self.excluded_labels.to(mask.device) donor_labels: list[torch.Tensor] [] for d in range(len(params[donor_indices])): # Select a random label from the donor image current_mask mask[params[donor_indices][d]] labels current_mask.unique() # Remove any label which is part of the excluded labels. Skip the reduction when there is nothing # to exclude: on MPS (PyTorch 2.9) all over an empty axis yields an undefined result, usually # False, where CPU and CUDA return True, and the filter would then discard every label. if self.excluded_labels.numel() 0: labels labels[(labels.view(1, -1) ! self.excluded_labels.view(-1, 1)).all(dim0)] if len(labels) 0: selected_label labels[torch.randperm(len(labels))[0]] donor_labels.append(selected_label) params[selected_labels] torch.stack(donor_labels) if len(donor_labels) 0 else torch.empty(0)修复要点可以总结为三点用numel() 0判定是否存在排除需求excluded_labels可能是None、空列表或空张量统一在构造函数中规范化为 1 维张量后numel()是最直接的空性判据仅在确有排除标签时才执行广播比较 all(dim0)归约避免触碰空轴归约这条跨后端语义不一致的路径保留了原有的空标签保护即使经过过滤后供体中没有可用标签例如供体掩码只包含被排除的标签该供体就不会产生移植len(labels) 0分支后续torch.stack对空列表返回torch.empty(0)与原有行为一致。值得注意的是修复只影响RandomTransplantation的实现而RandomTransplantation3D直接复用 2D 类的逻辑见 kornia/augmentation/_3d/mix/transplantation.py因此一处修复同时覆盖两个类与变更记录中同时修复两个类的描述吻合。六、验证针对该回归的专门测试修复并非停留在代码层面仓库中新增了针对性的回归测试。在 tests/augmentation/test_augmentation_mix.py 的test_no_excluded_labels_transplants中测试注释完整复述了缺陷机理With nothing excluded every donor label stays eligible, so with p1 some pixels change. On MPS theallreduction over the empty excluded-label axis is undefined (usually False, and it varies between processes) and used to drop every label, which made this class flaky there.测试构造了一个批大小为 2、大小为 4×4 的掩码图像 0 左上 2×2 区域标签为 1图像 1 右下 2×2 区域标签为 2image mask.unsqueeze(1)。然后以p1即批内每张图像都是受体执行f RandomTransplantation(p1) image_out, mask_out f(image, mask) assert len(f._params[selected_labels]) 2 assert f._params[selection].any() assert not torch.equal(mask_out, mask) self.assert_close(image_out.squeeze(dim1), mask_out)四个断言逐一锁定修复目标selected_labels长度必须为 2批内两个受体各收到一个供体标签证明标签不再被全部过滤selection布尔掩码存在True确实有像素被选中移植mask_out ! mask输出不再等于输入缺陷现象被消除移植后的图像与掩码逐像素一致图像特征与分割掩码的移植保持同步。此外同文件中的test_smokeexcluded_labels[0]验证了正常排除路径仍正确工作test_module在 2/3/4 维空间上验证移植几何关系的正确性test_gradcheck与test_exception则保证该修复没有破坏梯度路径与参数校验如excluded_labels必须是 1 维、selected_labels的维度与数量约束等。七、对使用者的影响与建议升级获取修复该修复随 PR #4160 合入如果需要在 MPSApple Silicon上训练或推理且依赖RandomTransplantation的数据增强效果应确保使用的 Kornia 版本包含此修复修复前在 MPS 上该增强静默退化为恒等映射。跨后端一致性此案例揭示了一个普遍教训——即使代码在 CPU/CUDA 上结果正确也不能假定 MPS 等后端的归约语义完全一致。涉及空轴归约all/any/sum等的逻辑应显式判空后短路正如修复中用numel() 0所做的那样。验证建议对依赖增强效果的实验建议至少做一次增强确实改变了输入的冒烟断言如not torch.equal(out, input)并在目标部署后端而非仅开发后端上运行关键测试。仓库中的test_no_excluded_labels_transplants正是这种断言范式可作为自定义验证的参考。小结changelog.d/migration-101.fixed.md虽只有寥寥数行却完整记录了一次后端语义差异导致静默功能失效的典型缺陷修复现象是 MPS 上RandomTransplantation移植失效、输出等于输入根因是 PyTorch MPS 对空轴all()归约返回未定义值通常False与 CPU/CUDA 返回True不一致修复是当excluded_labels为空时跳过过滤并用 回归测试 锁死行为。理解这一案例既能帮你规避跨后端增强失效的坑也能为你在其他算子中处理空张量归约提供直接可复用的思路。赞分享计算机视觉深度学习人工智能图像处理【免费下载链接】kornia 空间人工智能的几何计算机视觉库项目地址https://gitcode.com/kornia/kornia点击查看免费下载相关推荐Kornia 修复解析warp_affine、warp_perspective 与 remap 空目标在 MPS 上的崩溃4032、4354Kornia 修复解析warp_affine、warp_perspective 与 remap 空目标在 MPS 上的崩溃 4032、 4354 本指南聚计算机视觉人工智能深度学习图像处理Kornia 性能修复解读total_variation 从二维归约改为单维展平归约加速 MPS 上的总变差计算Kornia 性能修复解读 total_variation 从二维归约改为单维展平归约加速 MPS 上的总变差计算 导读 本文聚焦 Kornia 中一条 f计算机视觉深度学习人工智能图像处理Kornia RandomTransplantation 约定全解mask 驱动的批内语义区域移植增强与 3D 扩展Kornia RandomTransplantation 约定全解mask 驱动的批内语义区域移植增强与 3D 扩展 本文以 Kornia 仓库为 Rando计算机视觉人工智能深度学习图像处理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表