变换技巧#

性能考虑#

为了从变换中获得最佳性能,建议遵循以下指南:

  • 依赖 torchvision.transforms.v2 中的 v2 变换。

  • 使用张量而非 PIL 图像。

  • 特别是对于调整大小操作,使用 torch.uint8 数据类型。

  • Resize 采用 bilinear 或者 bicubic 模式进行图像缩放。

典型的变换管道可能的样子:

import torch
from torchvision.transforms import v2
transforms = v2.Compose([
    v2.ToImage(), # 转换为张量,仅在您拥有 PIL 图像时需要
    v2.ToDtype(torch.uint8, scale=True),  # 在这个阶段,大多数输入已经是 uint8 类型
    # ...
    v2.RandomResizedCrop(size=(224, 224), antialias=True),  # Or Resize(antialias=True)
    # ...
    v2.ToDtype(torch.float32, scale=True),  # Normalize expects float input
    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

在依赖 torch.utils.data.DataLoadernum_workers 大于 0 的典型训练环境中,上述内容应为您提供最佳性能。

变换操作通常对输入的步幅/内存格式较为敏感。某些变换在通道优先的图片上运行更快,而其他一些则偏好通道最后。与 torch 算子类似,大多数变换会保持输入的内存格式,但由于实现细节的原因,这可能并不总是被遵循。如果您追求最佳性能,可能需要稍作实验。在单个变换上使用 torch.compile() 也可能有助于消除内存格式变量(例如在 Normalize 上)。请注意,这里讨论的是内存格式,而不是张量形状

需要注意的是,像 ResizeRandomResizedCrop 这样的调整大小变换通常更倾向于通道最后的输入,并且目前通常不会从 torch.compile() 中受益。

变换类、函数和内核#

变换既可以像 Resize 这样的类提供,也可以像 torchvision.transforms.v2.functional 命名空间中的 resize() 这样的函数提供。这非常类似于torch.nn包,它在 torch.nn.functional 中定义了类和功能等价物。

这些函数支持 PIL 图像、纯张量或 TVTensors,例如,resize(image_tensor)resize(boxes) 都是有效的。

备注

随机变换,例如 RandomCrop,每次被调用时会随机采样一些参数。它们的函数式对应(crop())不执行任何形式的随机采样,因此在参数化上略有不同。在使用函数式 API 时,可以使用 transforms 类的 get_params() 类方法来执行参数采样。

torchvision.transforms.v2.functional 命名空间还包含我们所说的“内核”。这些是实现特定类型核心功能的低级函数,例如 resize_bounding_boxesresized_crop_mask。如果你希望对像边界框或掩码这样的类型提供 torchscript 支持,那么内核才真正有用。

Torchscript 支持#

大多数变换类和函数都支持 Torchscript。为了组合变换,请使用 torch.nn.Sequential 而不是 Compose

import torch
from torchvision.transforms import v2

transforms = torch.nn.Sequential(
    v2.CenterCrop(10),
    v2.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
)
scripted_transforms = torch.jit.script(transforms)

警告

v2 transforms 支持 torchscript,但是如果你尝试对 v2 类的 transform调用 torch.jit.script(),实际上你会得到它的(已脚本化的)v1 等价物。由于 v1 和 v2 之间的实现差异,这可能导致脚本化执行和即时执行之间有细微的结果差异。

如果你确实需要为 v2 transforms 提供 torchscript 支持,建议从 torchvision.transforms.v2.functional 命名空间中对函数进行脚本化处理,以避免意外情况发生。

请注意,这些功能仅支持 torchscript 用于纯张量操作,这些张量始终被视为图像。如果您需要对边界框或掩码等其他类型的数据提供 torchscript 支持,您可以依靠底层内核来实现。

任何自定义变换要与 torch.jit.script() 一起使用,都应从 torch.nn.Module 派生出来。

警告

可脚本化的变换操作 必须能够处理 torch.Tensor,并且不依赖于 lambda 函数或 PIL.Image