Compose 是将多个 transform 函数封装为单个可调用对象的工具,用于在 Dataset.__getitem__ 中顺序执行图像预处理;直接用 list 调用会报 TypeError,因 list 不可调用。

Compose 是什么,为什么不能直接用 list 调用 transforms
它不是容器,而是可调用对象——Compose 把一串 transform 函数打包成一个函数,让 Dataset.__getitem__ 能统一传入一张 PIL.Image 或 Tensor 就跑完全部步骤。直接写 [ToTensor(), Normalize(...)](img) 会报 TypeError: 'list' object is not callable。
- 必须用
Compose包一层,否则无法被torch.utils.data.DataLoader内部的 collate 逻辑识别 -
Compose按顺序执行,前一个 transform 的输出类型必须是后一个的输入类型(比如Resize输出PIL.Image,ToTensor才能接住;但ToTensor后输出Tensor,再跟RandomHorizontalFlip就没问题,它支持Tensor输入) - 别在
Compose里混用 PIL 和 Tensor 操作却不检查类型——比如把ColorJitter放在ToTensor后面,会触发TypeError: img should be PIL Image. Got <class></class>
常见 transforms 顺序和参数陷阱
顺序错了,结果可能完全不对。比如归一化 Normalize 必须在 ToTensor 之后——因为 ToTensor 会把像素值从 [0, 255] 缩放到 [0.0, 1.0],而 Normalize 默认按 ImageNet 统计值设计:mean=[0.485, 0.456, 0.406]、std=[0.229, 0.224, 0.225],都是针对 [0.0, 1.0] 区间的。
-
RandomRotation和RandomResizedCrop都需要指定interpolation参数,默认是PIL.Image.BILINEAR,但如果用torchvision.transforms.InterpolationMode.BILINEAR(PyTorch 1.9+),要确保你用的是新 API,否则会报AttributeError: module 'torchvision.transforms' has no attribute 'InterpolationMode' -
RandomHorizontalFlip(p=0.5)的p是概率,不是开关;设成p=1.0才是强制翻转,p=0才是禁用 -
Resize((224, 224))和CenterCrop(224)不等价:前者拉伸图像,后者裁剪中心区域;训练常用RandomResizedCrop(224),验证用Resize(256)+CenterCrop(224),这是 ImageNet 标准流程
如何调试 Compose 中某一步出错
错误信息往往只说 “transform failed”,但不知道哪步崩了。最直接的办法是拆开单步试:
transform = transforms.ToTensor() img_tensor = transform(img) # 这里崩了?说明原图不是 PIL.Image 或 numpy array
- 用
isinstance(img, PIL.Image.Image)检查输入类型;如果来自 OpenCV(cv2.imread),得先cv2.cvtColor(img, cv2.COLOR_BGR2RGB)再PIL.Image.fromarray(img) - 对
Tensor类型 transform(如Normalize),确认维度是(C, H, W),且C == 3 or 1;若你是灰度图但用了三通道 mean/std,会报RuntimeError: invalid argument - 别在
Compose里塞自定义 lambda ——比如lambda x: x[:, ::-1]翻转通道,容易破坏后续操作的预期形状;真要写,用transforms.Lambda并加类型注释和断言
Compose 在 Dataset 和 DataLoader 中的实际位置
它属于数据集层面的逻辑,不是 dataloader 的配置项。典型写法是传给 Dataset 的 __init__,而不是 DataLoader 构造时传进去。
- 训练集和验证集通常用不同
Compose:训练加随机增强,验证只做确定性变换(Resize+CenterCrop+ToTensor+Normalize) - 如果你把
Compose写在DataLoader的collate_fn里,会导致每 batch 重复构造 transform 对象,性能差,且无法复用预编译逻辑(比如RandomResizedCrop的内部随机种子管理) - 注意
torchvision.datasets.ImageFolder的transform参数就是接收Compose实例,别传函数列表或单个 transform
最容易被忽略的是:所有 transform 都默认不改变原始图像对象,但有些操作(比如 RandomAffine 的插值)在 CPU 上做会比 GPU 慢很多;如果模型在 GPU 上训,别指望 transform 会自动上卡——ToTensor 输出 CPU tensor,Normalize 也是,得靠 data.to(device) 统一搬运。


















