李沐动手学深度学习V2-图像增广和代码实现

图像增广

大型数据集是成功应用深度神经网络的先决条件,因为解决了大型复杂网络的过拟合性。 图像增广在对训练图像进行一系列的随机变化之后,生成相似但不同的训练样本,从而扩大了训练集的规模。 此外,应用图像增广的原因是,随机改变训练样本可以减少模型对某些属性的依赖,从而提高模型的泛化能力。 例如,我们可以以不同的方式裁剪图像,使感兴趣的对象出现在不同的位置,减少模型对于对象出现位置的依赖。 我们还可以调整亮度、颜色等因素来降低模型对颜色的敏感度。 可以说,图像增广技术对于AlexNet的成功是必不可少的。

1. 常见的图像增广方法

import PIL.Image
import torch
import d2l.torch
import torchvision
from torch import nn
from torch.utils import data
d2l.torch.set_figsize()
image = d2l.torch.Image.open('../images/cat1.jpg')
#PIL.Image.Image.show(image)
d2l.torch.plt.imshow(image)
#大多数图像增广方法都具有一定的随机性。为了便于观察图像增广的效果,我们下面定义辅助函数apply。 此函数在输入图像img上多次运行图像增广方法aug并显示所有结果。
def apply(image,aug,num_rows=2,num_columns=4,scale=1.5):
    image_aug = [aug(image) for _ in range(num_rows*num_columns)]
    d2l.torch.show_images(image_aug,num_rows,num_columns,scale=scale)
  1. 左右翻转:通常不会改变对象的类别,是最早且最广泛使用的图像增广方法之一
#图像各有50%的几率向左或向右翻转
apply(image,torchvision.transforms.RandomHorizontalFlip())

左右翻转

  1. 上下翻转:
#图像各有50%的几率向上或向下翻转
apply(image,torchvision.transforms.RandomVerticalFlip())

上下翻转
3. 随机裁剪:随机裁剪一个面积为原始面积10%到100%的区域,该区域的宽高比从0.5到2之间随机取值。 然后,裁剪出来的区域宽度和高度都被缩放到200像素。 𝑎 和 𝑏 之间的随机取值指的是在区间 [𝑎,𝑏] 中通过均匀采样获得的连续值。通过对图像进行随机裁剪,使物体以不同的比例出现在图像的不同位置。 这也可以降低模型对目标位置的敏感性。

#随机裁剪一个面积为原始面积10%到100%的区域(scale=(0.1,1)),该区域的宽高比从0.5到2之间随机取值(ratio=(0.5,2))。 然后,裁剪出来的区域的宽度和高度都被缩放到200像素(size=(200,200))。
shape_aug = torchvision.transforms.RandomResizedCrop(size=(200,200),scale=(0.1,1),ratio=(0.5,2))
apply(image,shape_aug)

随机裁剪
4. 颜色改变:改变图像颜色的四个方面:亮度、对比度、饱和度和色调。

#随机更改图像的亮度,随机值为原始图像的50%( 1−0.5 )到150%( 1+0.5 )之间(brightness=0.5)。
bright_aug = torchvision.transforms.ColorJitter(brightness=0.5,contrast=0,saturation=0,hue=0)
apply(image,bright_aug)

颜色改变

  1. 随机更改图像的色调
hue_aug = torchvision.transforms.ColorJitter(brightness=0,contrast=0,saturation=0,hue=0.5)
apply(image,hue_aug)

色调改变
6. 同时随机更改图像的亮度(brightness)、对比度(contrast)、饱和度(saturation)和色调(hue)。

colors_aug = torchvision.transforms.ColorJitter(brightness=0.5,contrast=0.5,saturation=0.5,hue=0.5)
apply(image,colors_aug)

更改图像亮度对比度饱和度色调
7. 结合多种图像增广方法

augs = torchvision.transforms.Compose([horizontal_aug,colors_aug,shape_aug])
apply(image,augs)

结合多种图像增广方法

2. 使用图像增广进行训练

  1. 使用CIFAR-10数据集
CIFAR10_dataset = torchvision.datasets.CIFAR10(root='../data/CIFAR10',train=True,download=True)
#查看前32个训练图片
d2l.torch.show_images([CIFAR10_dataset[i][0] for i in range(32)],4,8,scale=1.5)

CIFAR10前32个图像
2. 为了在预测过程中得到确切的结果,我们通常对训练样本只进行图像增广,且在预测过程中不使用随机操作的图像增广。 在这里,我们只使用最简单的随机左右翻转。 此外,我们使用ToTensor实例将一批图像转换为深度学习框架所要求的格式,即形状为(批量大小,通道数,高度,宽度)的32位浮点数。

train_aug = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.ToTensor()])
test_aug = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
  1. 定义一个辅助函数,以便于读取图像和应用图像增广。PyTorch数据集提供的transform函数应用图像增广来转化图像,使用Dataloader来读取图像。
def load_cifar10(is_train,batch_size,augs):
    datasets = torchvision.datasets.CIFAR10(root='../data/CIFAR10',train=is_train,download=True,transform=augs)
    dataloader = torch.utils.data.DataLoader(datasets,batch_size,shuffle=True,num_workers=d2l.torch.get_dataloader_workers())
    return dataloader
  1. 使用GPU训练和评估
def train_batch_ch13(net,X,y,loss,optim,devices):
    if isinstance(X,list):
        X = [x.to(devices[0]) for x in X]
    else:
        X = X.to(devices[0])
    y = y.to(devices[0])
    net.train()
    optim.zero_grad()
    y_hat = net(X)
    ls = loss(y_hat,y).sum()
    ls.backward()
    optim.step()
    train_batch_loss = ls
    train_batch_accuracy = d2l.torch.accuracy(y_hat,y)
    return train_batch_loss,train_batch_accuracy

def train_ch13(net,train_iter,test_iter,epochs,optim,loss,devices=d2l.torch.try_all_gpus()):
    timer,num_batchs = d2l.torch.Timer(),len(train_iter)
    net = nn.DataParallel(net,devices).to(devices[0])

    animator = d2l.torch.Animator(xlabel='epoch',xlim=[1,epochs],ylim=[0,1],legend=['train loss','train acc','test acc'])
    for epoch in range(epochs):
    	# 4个维度:累加存储训练损失,训练准确度,样本数,样本数
        accumulator = d2l.torch.Accumulator(4)
        for i,(X,y) in enumerate(train_iter):
            timer.start()
            train_batch_loss,train_batch_accuracy = train_batch_ch13(net,X,y,loss,optim,devices)
            accumulator.add(train_batch_loss,train_batch_accuracy,y.shape[0],y.numel())
            timer.stop()
            if (i+1)%(num_batchs//5) ==0 or i==num_batchs-1:
                animator.add(epoch+(i+1)/num_batchs,(accumulator[0]/accumulator[2],accumulator[1]/accumulator[3],None))
        test_accuracy = d2l.torch.evaluate_accuracy_gpu(net,test_iter)
        animator.add(epoch+1,(None,None,test_accuracy))
    print(f'loss {accumulator[0]/accumulator[2]:.3f},train acc {accumulator[1]/accumulator[3]},test acc {test_accuracy:.3f}')
    print(f'{accumulator[2]*epochs/timer.sum() :.1f}个样本/sec ,在{str(devices[0])}')
  1. 定义train_with_data_aug函数,使用图像增广来训练模型。该函数获取所有的GPU,并使用Adam作为训练的优化算法,将图像增广应用于训练集,最后调用刚刚定义的用于训练和评估模型的train_ch13()函数。
batch_size,devices = 256,d2l.torch.try_all_gpus()
net = d2l.torch.resnet18(num_classes=10,in_channels=3)
def init_weights(m):
    if m in [nn.Linear,nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
train_iter = load_cifar10(is_train=True,batch_size=batch_size,augs=train_aug)
test_iter = load_cifar10(is_train=False,batch_size=batch_size,augs=test_aug)
def train_data_augs(net,train_iter,test_iter,lr,epochs,devices):
    optim = torch.optim.Adam(net.parameters(),lr=lr)
    loss = nn.CrossEntropyLoss(reduction='none')
    train_ch13(net,train_iter,test_iter,epochs,optim,loss,devices)
train_data_augs(net,train_iter,test_iter,0.01,10,devices)
  1. 训练和测试结果(lr=0.001,batch_size=256,epochs=10)
    训练和测试结果

3.小结

  1. 图像增广基于现有的训练数据生成随机图像,来提高模型的泛化能力。
  2. 为了在预测过程中得到确切的结果,我们通常对训练样本只进行图像增广,而在预测过程中不使用带随机操作的图像增广。
  3. 深度学习框架提供了许多不同的图像增广方法,这些方法可以被同时应用。

4. 使用图像增广训练全部代码

import PIL.Image
import torch
import d2l.torch
import torchvision
from torch import nn
from torch.utils import data

d2l.torch.set_figsize()
image = d2l.torch.Image.open('../images/cat1.jpg')
#PIL.Image.Image.show(image)
d2l.torch.plt.imshow(image)

def apply(image,aug,num_rows=2,num_columns=4,scale=1.5):
    image_aug = [aug(image) for _ in range(num_rows*num_columns)]
    d2l.torch.show_images(image_aug,num_rows,num_columns,scale=scale)

apply(image,torchvision.transforms.RandomHorizontalFlip())
apply(image,torchvision.transforms.RandomVerticalFlip())

horizontal_aug = torchvision.transforms.RandomHorizontalFlip()
shape_aug = torchvision.transforms.RandomResizedCrop(size=(200,200),scale=(0.1,1),ratio=(0.5,2))
apply(image,shape_aug)

bright_aug = torchvision.transforms.ColorJitter(brightness=0.5,contrast=0,saturation=0,hue=0)
apply(image,bright_aug)
hue_aug = torchvision.transforms.ColorJitter(brightness=0,contrast=0,saturation=0,hue=0.5)
apply(image,hue_aug)

colors_aug = torchvision.transforms.ColorJitter(brightness=0.5,contrast=0.5,saturation=0.5,hue=0.5)
apply(image,colors_aug)

augs = torchvision.transforms.Compose([horizontal_aug,colors_aug,shape_aug])
apply(image,augs)

CIFAR10_dataset = torchvision.datasets.CIFAR10(root='../data/CIFAR10',train=True,download=True)
d2l.torch.show_images([CIFAR10_dataset[i][0] for i in range(32)],4,8,scale=1.5)

train_aug = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.ToTensor()])
test_aug = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])

def load_cifar10(is_train,batch_size,augs):
    datasets = torchvision.datasets.CIFAR10(root='../data/CIFAR10',train=is_train,download=True,transform=augs)
    dataloader = torch.utils.data.DataLoader(datasets,batch_size,shuffle=True,num_workers=d2l.torch.get_dataloader_workers())
    return dataloader

def train_batch_ch13(net,X,y,loss,optim,devices):
    if isinstance(X,list):
        X = [x.to(devices[0]) for x in X]
    else:
        X = X.to(devices[0])
    y = y.to(devices[0])
    net.train()
    optim.zero_grad()
    y_hat = net(X)
    ls = loss(y_hat,y).sum()
    ls.backward()
    optim.step()
    train_batch_loss = ls
    train_batch_accuracy = d2l.torch.accuracy(y_hat,y)
    return train_batch_loss,train_batch_accuracy

def train_ch13(net,train_iter,test_iter,epochs,optim,loss,devices=d2l.torch.try_all_gpus()):
    timer,num_batchs = d2l.torch.Timer(),len(train_iter)
    net = nn.DataParallel(net,devices).to(devices[0])

    animator = d2l.torch.Animator(xlabel='epoch',xlim=[1,epochs],ylim=[0,1],legend=['train loss','train acc','test acc'])
    for epoch in range(epochs):
        accumulator = d2l.torch.Accumulator(4)
        for i,(X,y) in enumerate(train_iter):
            timer.start()
            train_batch_loss,train_batch_accuracy = train_batch_ch13(net,X,y,loss,optim,devices)
            accumulator.add(train_batch_loss,train_batch_accuracy,y.shape[0],y.numel())
            timer.stop()
            if (i+1)%(num_batchs//5) ==0 or i==num_batchs-1:
                animator.add(epoch+(i+1)/num_batchs,(accumulator[0]/accumulator[2],accumulator[1]/accumulator[3],None))
        test_accuracy = d2l.torch.evaluate_accuracy_gpu(net,test_iter)
        animator.add(epoch+1,(None,None,test_accuracy))
    print(f'loss {accumulator[0]/accumulator[2]:.3f},train acc {accumulator[1]/accumulator[3]},test acc {test_accuracy:.3f}')
    print(f'{accumulator[2]*epochs/timer.sum() :.1f}个样本/sec ,在{str(devices[0])}')

batch_size,devices = 256,d2l.torch.try_all_gpus()
net = d2l.torch.resnet18(num_classes=10,in_channels=3)
def init_weights(m):
    if m in [nn.Linear,nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
train_iter = load_cifar10(is_train=True,batch_size=batch_size,augs=train_aug)
test_iter = load_cifar10(is_train=False,batch_size=batch_size,augs=test_aug)
def train_data_augs(net,train_iter,test_iter,lr,epochs,devices):
    optim = torch.optim.Adam(net.parameters(),lr=lr)
    loss = nn.CrossEntropyLoss(reduction='none')
    train_ch13(net,train_iter,test_iter,epochs,optim,loss,devices)

train_data_augs(net,train_iter,test_iter,0.01,10,devices)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值