Pytorch深入浅出(二)之数据预处理Transforms(上)

在上一篇文章中提及自定义Dataset,常常伴有Transforms的操作,本文章简要介绍一下Transforms的数据预处理操作,包含各种数据变换操作以及数据标准化Normalize,都是使用torchvision库中的transforms里的方法来实现。
Torchvision 0.24 文档是PyTorch的计算机视觉工具包,包含了一些与CV相关的处理。有三个需要重要介绍:

  • torchvision.transforms:包含了常用的图像预处理方法,如数据中心化、标准化、缩放、裁剪等。
  • torchvision.datasets:包含了常用数据集的dataset实现,如MNIST、CIFAR-10、ImageNet等。
  • torchvision.model:包含了常用的预训练模型,如AlexNet、VGG、ResNet、GoogleNet等

预处理过程

读入的数据都是需要进行预处理的。实际的数据预处理,会用到很多方法,他们往往不是在要使用单独调用,而是会以组合形式出现。transforms.Compose()中以list的形式存放若干transforms方法,就可以实现这些方法的组合,定义一个全局的变量(如下代码),以后就可以在任何地方使用用该变量,进而实现这些方法的整体调用。
具体transforms的方法:Transforms API文档(这里用的是V2版本)

from torchvision.transforms import v2

# 定义数据增强和转换
data_transform = v2.Compose([
    v2.RandomResizedCrop(224, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0)),  # 随机裁剪图像,所得图像为原始面积的0.08到1之间,高宽比在3/4和4/3之间,然后,缩放图像以创建224 x 224的新图像
    v2.RandomHorizontalFlip(),  # 随机水平翻转
    v2.RandomRotation(10),  # 随机旋转
    v2.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.2),  # 随机调整亮度、对比度、饱和度、色调
    v2.ToImage(), v2.ToDtype(torch.float32, scale=True),      # 1.先转成 tv_tensors.Image 2.再转 dtype 并 [0,1] 化
    # v2.ToTensor(),  V1版本还能用,在V2已经被弃用了,用上述方法代替,转为 Tensor
    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 归一化
    # 使用均值和标准差对张量图像进行归一化,使得图像的像素值分布符合标准正态分布(均值为0,标准差为1)→ RGB三通道的均值与标准差,从ImageNet数据集中统计得到(如果你使用预训练模型)
])

数据预处理的过程渗透在整个训练迭代过程中,每读入一张图片就会给它用一次预处理组合。数据读取真正的实现是在我们定义的DataSet类的__getitem__方法中,如果要进行数据预处理就是在这里实现的,读取一张图片,调用一次预处理组合,进行一次预处理,在上一篇笔记里有提及。

class DataSet(Dataset):
  def __init__(self, , , ,data_transform=None):
    ……
    self.transform = data_transform
    ……

def __getitem__(self, index):
    ……
    if self.transform:
      image = self.transform(image)
      ……

数据标准化

  • Normalize()方法
    此转换不支持 PIL Image。给定 n 通道的均值:(mean[1],...,mean[n]) 和标准差:(std[1],..,std[n]),此转换将对输入 torch.*Tensor 的每个通道进行归一化,即 output[channel] = (input[channel] - mean[channel]) / std[channel]
torchvision.transforms.Normalize(mean, std, inplace=False)
output[channel] = (input[channel] - mean[channel]) / std[channel]

数据标准化的目的,是为了将数据分布的均值化成0,标准差化成1。模型一般被初始化成0均值,所以这样有利于加快训练过程中模型的收敛。原本需要自己计算自己数据集的均值和标准差来进行标准化,但是许多项目直接采用了imagenet的均值和标准差来进行标准化,所以经常见到这样的一组数字:mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225],下表是一些数据集的均值与标准差。

数据集均值方差
cifar10[0.491, 0.482, 0.446][0.247, 0.243, 0.261]
coco[0.471, 0.448, 0.408][0.234, 0.239, 0.242]
imagenet[0.485, 0.456, 0.406][0.229, 0.224, 0.225]
MNIST[0.1307][ 0.3081]
  • 数据集标准差与均值计算
    我们在使用模型训练之前一般要对数据进行归一化(Normalize),归一化之前需要得到数据集整体的方差和均值,这里提供了一个简单计算数据标准差和均值的接口,方便大家使用。
def get_mean_std(dataset, ratio=0.01):
    dataloader = DataLoader(dataset, batch_size=min(32, int(len(dataset)*ratio)),
                                             shuffle=True, num_workers=10)
    train = iter(dataloader).next()[0]   # iter(dataloader).next() 会得到一个 batch (images, labels),[0] 表示只取图像张量,不要标签
    mean = np.mean(train.numpy(), axis=(0,2,3))  # axis=(0,2,3) → 对 batch、H、W 三个通道分别求均值/方差,只保留 C 维度,shape = (3,) → 每个通道一个 mean和std
    std = np.std(train.numpy(), axis=(0,2,3))
    return mean, std

mean和std的操作本质是对像素值进行操作,涉及图像的本质,我将在下篇文章中提及。
mean(dim=[0,2,3]) = 就是把所有像素值加起来,再除以像素总数
std(dim=[0,2,3]) = 对所有像素求 (x-mean)² → 求和 → 除以像素数量 → 开根号
[0,2,3] → 对 batch、H、W 三个通道分别求均值/方差,只保留 C 维度,shape = (3,) → 每个通道一个 mean和std

from torchvision.transforms import v2
# ====== mean/std 计算前需要totensor ======
calc_transform = v2.Compose([
    v2.ToImage(),
    v2.ToDtype(torch.float32, scale=True)
])
  
# ====== 需要计算的 dataset ======
calc_dataset = CustomDataset(
    csv_file="train.csv",
    data_transform=calc_transform
)
  
# ====== 使用你原来的 get_mean_std 来计算 ======
train_mean, train_std = get_mean_std(calc_dataset)
  
print("Dataset Mean:", train_mean)
print("Dataset Std:", train_std)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值