变换技巧#
性能考虑#
为了从变换中获得最佳性能,建议遵循以下指南:
依赖
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.DataLoader
且 num_workers
大于 0
的典型训练环境中,上述内容应为您提供最佳性能。
变换操作通常对输入的步幅/内存格式较为敏感。某些变换在通道优先的图片上运行更快,而其他一些则偏好通道最后。与 torch
算子类似,大多数变换会保持输入的内存格式,但由于实现细节的原因,这可能并不总是被遵循。如果您追求最佳性能,可能需要稍作实验。在单个变换上使用 torch.compile()
也可能有助于消除内存格式变量(例如在 Normalize
上)。请注意,这里讨论的是内存格式,而不是张量形状。
需要注意的是,像 Resize
和 RandomResizedCrop
这样的调整大小变换通常更倾向于通道最后的输入,并且目前通常不会从 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_boxes
或 resized_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
。