概述:图像模型训练前为什么必须做 Transform
图像数据不能直接随便送进模型。即使模型结构完全正确,如果图像预处理错了,训练也会异常。
常见图像预处理包括:
- 调整图片尺寸,例如
Resize。 - 把图片转成 Tensor,例如
ToTensor。 - 按通道归一化,例如
Normalize。 - 随机裁剪、翻转、颜色扰动等数据增强。
- 保证输入 shape 满足 CNN 要求。
在 Paddle 中,图像处理常用 paddle.vision.transforms。它可以把多个预处理步骤组合起来,和 paddle.vision.datasets 或自定义 Dataset 配合使用。
读完本文,你应该能写出训练和验证两套图像 Transform,并理解图像 Tensor 的 shape、归一化和数据增强策略。
图像 Tensor 的常见 shape
CNN 中常见图像输入格式是:
text
[batch_size, channels, height, width]
也就是 NCHW。
例如:
text
[32, 3, 224, 224]
含义:
- 32 张图片。
- 每张图片 3 个通道。
- 高度 224。
- 宽度 224。
单张图片通常是:
text
[3, 224, 224]
DataLoader 组合成 batch 后才会变成:
text
[N, 3, 224, 224]
transforms.Compose:串联多个图像预处理
常见写法:
python
from paddle.vision import transforms
train_transform = transforms.Compose([
transforms.Resize(size=(224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
Compose 会按顺序执行每个 transform:
text
原始图片
|
Resize
|
ToTensor
|
Normalize
|
模型输入 Tensor
顺序很重要。通常先做几何变换,再转 Tensor,再归一化。
Resize:统一图像尺寸
模型要求输入尺寸一致,所以常用 Resize:
python
transform = transforms.Compose([
transforms.Resize(size=(224, 224)),
transforms.ToTensor(),
])
如果数据集中图片尺寸不同,DataLoader 默认无法直接堆叠成 batch。Resize 可以解决尺寸不一致问题。
注意:
- 直接拉伸可能改变宽高比。
- 分类任务中通常影响可接受。
- 检测、分割任务要更谨慎,因为标签也要同步变换。
ToTensor:把图像转成 Paddle Tensor
ToTensor 通常会把图像转成 Paddle Tensor,并把通道维整理成模型常用格式。
示例:
python
from paddle.vision import datasets, transforms
transform = transforms.Compose([
transforms.ToTensor(),
])
dataset = datasets.MNIST(mode="train", transform=transform)
image, label = dataset[0]
print(image.shape)
print(image.dtype)
print(label)
MNIST 是灰度图,常见 shape 是:
text
[1, 28, 28]
彩色图通常是:
text
[3, H, W]
Normalize:按通道归一化
归一化常用于让输入分布更稳定:
python
transform = transforms.Compose([
transforms.Resize(size=(224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225],
),
])
归一化公式:
xnorm=x−meanstd x_{norm} = \frac{x - mean}{std} xnorm=stdx−mean
对于 RGB 图片,mean 和 std 通常各有 3 个值,分别对应 3 个通道。
如果是 MNIST 灰度图,可以写:
python
transforms.Normalize(mean=[0.5], std=[0.5])
关键点:通道数要匹配。RGB 用 3 个值,灰度图用 1 个值。
训练 Transform 与验证 Transform
训练集可以使用随机增强:
python
train_transform = transforms.Compose([
transforms.RandomHorizontalFlip(prob=0.5),
transforms.Resize(size=(224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
验证集应该稳定:
python
val_transform = transforms.Compose([
transforms.Resize(size=(224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
原则:
- 训练集可以有随机增强。
- 验证集和测试集不要使用随机增强。
- 训练和验证应使用一致的基础尺寸和归一化规则。
常见数据增强方法
| Transform | 作用 | 常见用途 |
|---|---|---|
RandomHorizontalFlip |
随机水平翻转 | 分类、检测、分割常见 |
RandomCrop |
随机裁剪 | 增强位置鲁棒性 |
ColorJitter |
调整亮度、对比度等 | 增强光照鲁棒性 |
Resize |
改变尺寸 | 统一输入大小 |
Normalize |
通道归一化 | 稳定训练 |
不是所有增强都适合所有任务。例如数字识别中水平翻转可能改变语义,医学图像也要谨慎。
使用 MNIST 验证 Transform
MNIST 可以快速验证图像数据管道。
python
import paddle
from paddle.vision import datasets, transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=[0.5], std=[0.5]),
])
train_dataset = datasets.MNIST(mode="train", transform=transform)
train_loader = paddle.io.DataLoader(train_dataset, batch_size=32, shuffle=True)
images, labels = next(iter(train_loader))
print("images:", images.shape, images.dtype)
print("labels:", labels.shape, labels.dtype)
期望形状:
text
images: [32, 1, 28, 28]
labels: [32]
如果 shape 不符合预期,先检查 transform 顺序。
自定义图像 Dataset 的基本写法
真实项目常见目录结构:
text
dataset/
cats/
001.jpg
002.jpg
dogs/
001.jpg
002.jpg
自定义数据集示例:
python
from pathlib import Path
from PIL import Image
import paddle
class ImageFolderDataset(paddle.io.Dataset):
def __init__(self, root_dir, transform=None):
super().__init__()
self.root_dir = Path(root_dir)
self.transform = transform
self.class_names = sorted([p.name for p in self.root_dir.iterdir() if p.is_dir()])
self.class_to_id = {name: idx for idx, name in enumerate(self.class_names)}
self.samples = []
for class_name in self.class_names:
class_dir = self.root_dir / class_name
for path in class_dir.glob("*.jpg"):
self.samples.append((path, self.class_to_id[class_name]))
def __len__(self):
return len(self.samples)
def __getitem__(self, index):
path, label = self.samples[index]
image = Image.open(path).convert("RGB")
if self.transform is not None:
image = self.transform(image)
label = paddle.to_tensor(label, dtype="int64")
return image, label
使用:
python
dataset = ImageFolderDataset("dataset", transform=train_transform)
loader = paddle.io.DataLoader(dataset, batch_size=32, shuffle=True)
可视化检查:预处理后图像是否合理
图像预处理最怕"看起来代码没错,实际图片已经乱了"。建议抽样检查:
python
images, labels = next(iter(loader))
print(images.shape)
print(images.min().numpy(), images.max().numpy())
print(labels[:8])
如果接入 VisualDL,也可以记录图像样本。检查重点:
- 图片是否被错误拉伸。
- 通道是否颠倒。
- 归一化后范围是否异常。
- label 是否和目录类别对应。
训练前先看数据,比训练崩了再排查更省时间。
常见错误:图像 Transform 排查清单
错误一:Normalize 的通道数不匹配
RGB 图片:
python
Normalize(mean=[...三个值...], std=[...三个值...])
灰度图:
python
Normalize(mean=[0.5], std=[0.5])
错误二:验证集用了随机增强
验证集不要使用 RandomCrop、RandomHorizontalFlip 这类随机增强,否则指标会不稳定。
错误三:忘记 ToTensor
模型需要 Tensor 输入。如果 Dataset 返回 PIL Image,模型前向会失败。
错误四:输入 shape 不符合 CNN
CNN 通常期待:
text
[N, C, H, W]
如果是 [N, H, W, C],需要调整通道顺序。
错误五:类别名到 ID 的映射不稳定
建议排序:
python
self.class_names = sorted(...)
否则不同运行环境下类别 ID 可能不一致。
建议练习:把图像预处理跑通
- 用 MNIST 打印单张图像 shape。
- 用 DataLoader 打印 batch 图像 shape。
- 对 MNIST 使用
Normalize(mean=[0.5], std=[0.5])。 - 给训练集添加
RandomHorizontalFlip,观察是否适合任务。 - 写一个
ImageFolderDataset读取本地图片。 - 打印
class_to_id,确认类别映射。
总结
这一篇讲了 PaddleVision 图像处理基础:
- 图像模型常用 NCHW 格式。
Compose串联多个 transform。Resize统一尺寸。ToTensor将图像转成 Tensor。Normalize做通道归一化。- 训练集可以随机增强,验证集应稳定。
- 自定义图像数据集要保证类别映射稳定。
如果只能记住一句话,那就是:
图像 Transform 的目标,是把不规则图片稳定转换成模型期望的 [N, C, H, W] Tensor。