transforms 数据变换与数据增强
本教程共 60 篇 · 第 12 篇 · 更新于 2026-08-17 · 约 3 分钟阅读
本节目标:学会用
Compose组合图像变换,理解归一化为什么必要,认识常见的数据增强操作。
变换是什么
图片刚读进来时是 PIL 图像或 NumPy 数组:像素值 0-255,尺寸五花八门。模型只认张量,还要求尺寸统一、数值范围合适。transforms 就是这段「洗菜切菜」的流水线,它来自 torchvision。
最基础的组合长这样:
from torchvision import transforms
transform = transforms.Compose([
transforms.Resize((128, 128)), # 统一尺寸
transforms.ToTensor(), # 转张量,像素归一到 [0, 1]
transforms.Normalize(mean=[0.5], std=[0.5]), # 标准化
])
Compose 把多个变换包成一个整体,数据按顺序依次穿过。顺序有讲究:Resize 吃的是 PIL 图像,Normalize 吃的是张量,所以 ToTensor 必须夹在两者之间。顺序错了直接报类型错,我第一次写就把 Normalize 放到了 Resize 前面。
三个必学基础变换
ToTensor:把 PIL 图像转成张量,形状从 H×W×C 变成 C×H×W(通道放最前),像素从 [0, 255] 缩到 [0, 1]。它不做别的,但「转张量」这一步没它不行。
Resize:把图像缩放到指定大小。网络第一层的输入维度是定死的,同一批图像必须尺寸一致,这是它能进 batch 的前提。传整数表示短边缩到该长度、长边等比缩放;传元组表示直接定死宽高。
Normalize:按通道做标准化,公式是 (x - mean) / std。好处是把数据拉到零均值附近,模型收敛更稳。mean 和 std 怎么选?两种情况:
- 用 ImageNet 预训练模型:老老实实用官方值
mean=[0.485, 0.456, 0.406]、std=[0.229, 0.224, 0.225]。这是训练 ImageNet 时用的统计值,乱改会掉精度。 - 自己从零训练:可以用
[0.5, 0.5, 0.5]这种粗略值(把数据压到 [-1, 1]),或者先算数据集的真实统计值。单通道灰度图传单值就行,如[0.5]。
数据增强:免费的新数据
训练集只有 5 万张图,能不能「变出」更多?可以。每张图随机翻转一下、裁一裁、调调亮度,模型看到的就是新样本。这叫数据增强(Data Augmentation)。
它的价值在于逼模型学「本质」:一只猫,翻转了还是猫,裁掉一半也还是猫。模型见得多了,就不会死记某张图的固定细节,泛化能力自然上去。所以增强是防过拟合的廉价神器。
常用随机操作:
# MNIST 是 28×28 灰度图(单通道),别套 ImageNet 的 224 + RGB 均值
train_transform = transforms.Compose([
transforms.RandomAffine(degrees=15, translate=(0.1, 0.1)), # 小角度旋转 + 平移
transforms.ToTensor(),
transforms.Normalize(mean=[0.1307], std=[0.3081]), # MNIST 实测统计值
])
# 验证集:不做随机增强,只做 ToTensor + 归一化
valid_transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=[0.1307], std=[0.3081]),
])
裁剪家族有三个兄弟,别搞混:
CenterCrop(224):固定裁中间,位置永远不变,验证集常用。RandomCrop(224):随机挑位置裁,训练集可用。RandomResizedCrop(224):先随机裁剪(比例也随机)再缩放,增强力度最大,训练标配。注意尺寸要跟输入匹配——MNIST 这种 28×28 小图,裁到 224 没意义,用 RandomCrop(28, padding=4) 或 RandomAffine 更合适。
增强只在训练集用。验证集和测试集要老老实实,用 Resize + CenterCrop + ToTensor + Normalize 这套固定流程,别加随机。总不能在考场上给学生「随机改题」。
Tip
RandomResizedCrop让模型见过各种局部画面,比固定 Resize 效果好得多。图像分类的官方示例几乎都靠它撑场子。
训练和验证各一套
上一章讲过,Dataset 的 __getitem__ 负责出样本。变换就挂在这里:数据集读图后、返回前,把 transform 过一遍。所以训练集和验证集要建两个 Dataset,各自带自己的 transform:
from torchvision import datasets
train_dataset = datasets.MNIST(
root="./data", train=True,
transform=train_transform, download=True,
)
valid_dataset = datasets.MNIST(
root="./data", train=False,
transform=valid_transform, download=True,
)
同一个数据集类,换一个 transform 参数,就得到两个行为不同的数据集。这是 PyTorch 数据管线的标准套路:数据不变,变换分离。
自定义变换
内置变换不够用时,自己写一个类,实现 __call__ 即可:
class ScaleByTwo:
def __call__(self, x):
return x * 2
transform = transforms.Compose([ScaleByTwo(), transforms.ToTensor()])
写成类而不是函数,是为了把参数保存在实例里,复用起来干净。还有个场景几乎必须自定义:变换要同时改标签。比如图像翻转了,关键点坐标也得跟着翻,这种「连图带标签一起变」的活,内置变换干不了。
其他增强手段还有随机仿射变换 RandomAffine、高斯模糊 GaussianBlur,甚至自动搜索增强策略的 AutoAugment。入门阶段,翻转、旋转、颜色、裁剪这四样就足够用。
Notetorchvision 现在推荐新版接口
transforms.v2,API 和 v1 基本一致,额外支持更严格的类型检查。新项目可以直接from torchvision.transforms import v2,本文写法原样照搬即可。
小结
Compose按顺序串起变换,ToTensor 的位置别放错。- Resize 统一尺寸、ToTensor 转张量、Normalize 标准化,是图像入模的基本盘。
- 增强只加在训练集:翻转、旋转、颜色扰动、随机裁剪。
- 训练、验证各建一套 transform,数据不变、变换分离。
- 特殊需求写自定义变换,实现
__call__就够。
到这里,数据管线闭环了:Dataset 取数据,transforms 加工,DataLoader 喂给模型。下一章进入训练核心——损失函数。