尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

《动手学深度学习》图像增广(Image Augmentation)全解析:常用方法、CIFAR-10 实战与多 GPU 训练

《动手学深度学习》图像增广(Image Augmentation)全解析:常用方法、CIFAR-10 实战与多 GPU 训练 人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载图像增广Image Augmentation是深度学习计算机视觉训练中提升泛化能力、缓解过拟合的核心手段它通过对训练图像施加一系列随机变换生成相似但不同的新样本既扩大了训练集的规模又降低了模型对目标位置、颜色等属性的依赖。本文以《动手学深度学习》第 13 章 图像增广章节 为骨架结合仓库 d2l 工具库 的源码实现系统讲解左右/上下翻转、随机裁剪、颜色抖动亮度/对比度/饱和度/色调以及Compose组合等常用方法并完整演示如何在 CIFAR-10 数据集上配合 ResNet-18 与多 GPU 训练流程让读者获得一套可直接复制运行的图像增广实战方案。为什么要做图像增广概念与动机在 AlexNet 章节 中已经强调大型数据集是成功应用深度神经网络的先决条件。图像增广正是无中生有地扩充训练数据的关键手段它在对训练图像进行一系列随机变化之后生成相似但不同的训练样本从而扩大训练集规模。除了扩充数据量图像增广还有一个更深层的作用——降低模型对某些属性的依赖提高泛化能力以不同方式裁剪图像可以让感兴趣的对象出现在不同位置从而减少模型对对象出现位置的依赖调整亮度、颜色等因素可以降低模型对颜色的敏感度。从历史上看图像增广技术对 AlexNet 的成功几乎是必不可少的——它让深度卷积网络在有限的数据上获得了足够强的鲁棒性。可以认为本节讨论的技术是整个计算机视觉CV领域被使用最广泛的数据处理手段之一。实验环境准备三种深度学习框架的导入方式本书的代码以#tab标签区分不同深度学习框架分别使用 MXNet/Gluon、PyTorch 与飞桨 Paddle 实现同一套功能。本节示例均通过d2l工具包仓库中对应 d2l/torch.py、d2l/mxnet.py、d2l/paddle.py提供统一的高层封装。# MXNet / Gluon %matplotlib inline from d2l import mxnet as d2l from mxnet import autograd, gluon, image, init, np, npx from mxnet.gluon import nn npx.set_np()# PyTorch %matplotlib inline from d2l import torch as d2l import torch import torchvision from torch import nn# Paddle %matplotlib inline from d2l import paddle as d2l import warnings warnings.filterwarnings(ignore) import paddle import paddle.vision as paddlevision from paddle import nn接下来使用一张尺寸为 400×500 的图像即仓库中的 img/cat1.jpg作为演示原图# MXNet d2l.set_figsize() img image.imread(../img/cat1.jpg) d2l.plt.imshow(img.asnumpy());# PyTorch / Paddle d2l.set_figsize() img d2l.Image.open(../img/cat1.jpg) d2l.plt.imshow(img);可视化辅助函数apply一次查看多次增广结果大多数图像增广方法都具有一定的随机性。为了便于观察增广效果本章定义了一个辅助函数apply它会在输入图像img上多次运行同一个图像增广方法aug并将所有结果以网格形式展示出来。num_rows × num_cols决定了展示的总张数scale控制每张子图的显示比例#tab all def apply(img, aug, num_rows2, num_cols4, scale1.5): Y [aug(img) for _ in range(num_rows * num_cols)] d2l.show_images(Y, num_rows, num_cols, scalescale)这里的d2l.show_images在 d2l/torch.py 中实现负责将图像列表排版为num_rows × num_cols的子图网格并隐藏坐标轴。常用图像增广方法一翻转与裁剪左右翻转最经典的位置增广左右翻转图像通常不会改变对象的类别因此是最早且使用最广泛的增广方法之一。下面是三框架中各 50% 概率向左或向右翻转的写法# MXNet apply(img, gluon.data.vision.transforms.RandomFlipLeftRight())# PyTorch apply(img, torchvision.transforms.RandomHorizontalFlip())# Paddle apply(img, paddlevision.transforms.RandomHorizontalFlip())上下翻转需谨慎使用上下翻转不如左右翻转常用但对于某些类别例如示例中的猫上下翻转同样不会妨碍识别。三框架对应的实现为# MXNet apply(img, gluon.data.vision.transforms.RandomFlipTopBottom())# PyTorch apply(img, torchvision.transforms.RandomVerticalFlip())# Paddle apply(img, paddlevision.transforms.RandomVerticalFlip())随机裁剪打破目标位置的敏感性示例图像中猫位于画面中间但真实数据并非总是如此。在 汇聚层章节 中我们了解到汇聚层可以降低卷积层对目标位置的敏感性而随机裁剪则是另一种降低位置敏感性的手段——它让物体以不同的比例出现在图像的不同位置。下面的代码随机裁剪一个面积为原始面积 10% 到 100% 的区域区域的宽高比在 0.52 之间随机取值随后将裁剪区域的宽度和高度都缩放为 200 像素# MXNet shape_aug gluon.data.vision.transforms.RandomResizedCrop( (200, 200), scale(0.1, 1), ratio(0.5, 2)) apply(img, shape_aug)# PyTorch shape_aug torchvision.transforms.RandomResizedCrop( (200, 200), scale(0.1, 1), ratio(0.5, 2)) apply(img, shape_aug)# Paddle shape_aug paddlevision.transforms.RandomResizedCrop( (200, 200), scale(0.1, 1), ratio(0.5, 2)) apply(img, shape_aug)参数说明scale(0.1, 1)随机裁剪区域面积占原图面积的比例范围10%100%在该区间内均匀采样ratio(0.5, 2)裁剪区域的宽高比范围同样均匀采样(200, 200)裁剪后统一缩放到的输出尺寸。本书约定除非另有说明$a$ 和 $b$ 之间的随机数指的是在区间 $[a, b]$ 中通过均匀采样获得的连续值。常用图像增广方法二改变颜色颜色增广可以改变图像的四个方面亮度brightness、对比度contrast、饱和度saturation和色调hue。随机改变亮度下面的示例将亮度在原始值的 50%$1-0.5$到 150%$10.5$之间随机调整# MXNet apply(img, gluon.data.vision.transforms.RandomBrightness(0.5))# PyTorch apply(img, torchvision.transforms.ColorJitter( brightness0.5, contrast0, saturation0, hue0))# Paddle apply(img, paddlevision.transforms.ColorJitter( brightness0.5, contrast0, saturation0, hue0))随机改变色调类似地可以单独随机更改色调参数hue0.5表示色调偏移量为 50%# MXNet apply(img, gluon.data.vision.transforms.RandomHue(0.5))# PyTorch apply(img, torchvision.transforms.ColorJitter( brightness0, contrast0, saturation0, hue0.5))# Paddle apply(img, paddlevision.transforms.ColorJitter( brightness0, contrast0, saturation0, hue0.5))同时改变四个方面创建一个RandomColorJitter/ColorJitter实例可以同时随机更改亮度、对比度、饱和度和色调。这里四个参数全部取 0.5即各自在 50%150%或对应幅度范围内随机扰动# MXNet color_aug gluon.data.vision.transforms.RandomColorJitter( brightness0.5, contrast0.5, saturation0.5, hue0.5) apply(img, color_aug)# PyTorch color_aug torchvision.transforms.ColorJitter( brightness0.5, contrast0.5, saturation0.5, hue0.5) apply(img, color_aug)# Paddle color_aug paddlevision.transforms.ColorJitter( brightness0.5, contrast0.5, saturation0.5, hue0.5) apply(img, color_aug)结合多种图像增广方法Compose 流水线实践中我们几乎总是组合多种增广方法使用。三框架均通过Compose将一组变换按顺序串联依次作用于每一张图像。下面将前面定义的随机左右翻转、颜色抖动color_aug与随机裁剪shape_aug组合起来# MXNet augs gluon.data.vision.transforms.Compose([ gluon.data.vision.transforms.RandomFlipLeftRight(), color_aug, shape_aug]) apply(img, augs)# PyTorch augs torchvision.transforms.Compose([ torchvision.transforms.RandomHorizontalFlip(), color_aug, shape_aug]) apply(img, augs)# Paddle augs paddlevision.transforms.Compose([ paddle.vision.transforms.RandomHorizontalFlip(), color_aug, shape_aug]) apply(img, augs)Compose中的变换按书写顺序依次执行前一个变换的输出作为后一个变换的输入。合理编排顺序例如先随机裁剪再改颜色可以组合出更丰富的样本分布。使用图像增广进行训练CIFAR-10 实战为什么选择 CIFAR-10 而不是 Fashion-MNIST本节训练实验选用CIFAR-10数据集而非之前章节反复使用的 Fashion-MNIST。原因在于Fashion-MNIST 中对象的位置和大小已被规范化而 CIFAR-10 中对象的颜色和大小差异更明显因而更能体现图像增广的收益。先查看 CIFAR-10 训练集中前 32 张图像# MXNet d2l.show_images(gluon.data.vision.CIFAR10( trainTrue)[0:32][0], 4, 8, scale0.8);# PyTorch all_images torchvision.datasets.CIFAR10(trainTrue, root../data, downloadTrue) d2l.show_images([all_images[i][0] for i in range(32)], 4, 8, scale0.8);# Paddle all_images paddlevision.datasets.Cifar10(modetrain , downloadTrue) print(len(all_images)) d2l.show_images([all_images[i][0] for i in range(32)], 4, 8, scale0.8);训练集与测试集使用不同的增广策略一个关键原则是为了在预测时获得确切、可复现的结果通常只对训练样本做图像增广预测测试阶段不使用带随机操作的增广。这里训练集只使用最简单的随机左右翻转测试集不做任何增广两类流水线末尾都追加ToTensor将一批图像转换为深度学习框架要求的格式——形状为批量大小通道数高度宽度的 32 位浮点数取值 01# MXNet train_augs gluon.data.vision.transforms.Compose([ gluon.data.vision.transforms.RandomFlipLeftRight(), gluon.data.vision.transforms.ToTensor()]) test_augs gluon.data.vision.transforms.Compose([ gluon.data.vision.transforms.ToTensor()])# PyTorch train_augs torchvision.transforms.Compose([ torchvision.transforms.RandomHorizontalFlip(), torchvision.transforms.ToTensor()]) test_augs torchvision.transforms.Compose([ torchvision.transforms.ToTensor()])# Paddle train_augs paddlevision.transforms.Compose([ paddlevision.transforms.RandomHorizontalFlip(), paddlevision.transforms.ToTensor()]) test_augs paddlevision.transforms.Compose([ paddlevision.transforms.ToTensor()])数据加载函数load_cifar10接下来定义辅助函数将图像读取与增广管线绑定到数据集上并返回配置好批量大小、打乱策略与并行读取进程数的DataLoaderMXNettransform_first将增广应用于每个训练样本的第一个元素即图像并行读取进程数由d2l.get_dataloader_workers()提供在 d2l/mxnet.py 中实现默认使用 4 个进程PyTorch通过数据集的transform参数应用增广Paddle同样通过transform参数应用增广。# MXNet def load_cifar10(is_train, augs, batch_size): return gluon.data.DataLoader( gluon.data.vision.CIFAR10(trainis_train).transform_first(augs), batch_sizebatch_size, shuffleis_train, num_workersd2l.get_dataloader_workers())# PyTorch def load_cifar10(is_train, augs, batch_size): dataset torchvision.datasets.CIFAR10(root../data, trainis_train, transformaugs, downloadTrue) dataloader torch.utils.data.DataLoader(dataset, batch_sizebatch_size, shuffleis_train, num_workersd2l.get_dataloader_workers()) return dataloader# Paddle def load_cifar10(is_train, augs, batch_size): dataset paddlevision.datasets.Cifar10(modetrain, transformaugs, downloadTrue) dataloader paddle.io.DataLoader(dataset, batch_sizebatch_size, num_workersd2l.get_dataloader_workers(), shuffleis_train) return dataloaderd2l.get_dataloader_workers()在 d2l/torch.py 中固定返回 4 个进程用于数据并行读取。多 GPU 小批量训练train_batch_ch13本章在 CIFAR-10 上训练 ResNet 章节 中的ResNet-18模型并复用 多 GPU 训练章节 的多 GPU 训练范式。下面定义多 GPU 小批量训练函数这些函数以#save标记保存后续章节会继续复用MXNet 版本将批量按设备切分split_fd2l.split_batch在多个设备上分别前向传播、分别反向传播并使用ignore_stale_gradTrue允许使用过时梯度这在微调 BERT 等场景中很有用#save def train_batch_ch13(net, features, labels, loss, trainer, devices, split_fd2l.split_batch): 用多GPU进行小批量训练 X_shards, y_shards split_f(features, labels, devices) with autograd.record(): pred_shards [net(X_shard) for X_shard in X_shards] ls [loss(pred_shard, y_shard) for pred_shard, y_shard in zip(pred_shards, y_shards)] for l in ls: l.backward() # True标志允许使用过时的梯度这很有用例如在微调BERT中 trainer.step(labels.shape[0], ignore_stale_gradTrue) train_loss_sum sum([float(l.sum()) for l in ls]) train_acc_sum sum(d2l.accuracy(pred_shard, y_shard) for pred_shard, y_shard in zip(pred_shards, y_shards)) return train_loss_sum, train_acc_sumPyTorch 版本将数据统一放到devices[0]上借助nn.DataParallel自动并行前向与反向#save def train_batch_ch13(net, X, y, loss, trainer, devices): 用多GPU进行小批量训练 if isinstance(X, list): # 微调BERT中所需 X [x.to(devices[0]) for x in X] else: X X.to(devices[0]) y y.to(devices[0]) net.train() trainer.zero_grad() pred net(X) l loss(pred, y) l.sum().backward() trainer.step() train_loss_sum l.sum() train_acc_sum d2l.accuracy(pred, y) return train_loss_sum, train_acc_sumPaddle 版本同样将数据放到devices[0]对应的设备上飞桨暂不支持在 notebook 上进行多 GPU 训练#save def train_batch_ch13(net, X, y, loss, trainer, devices): 用多GPU进行小批量训练 飞桨不支持在notebook上进行多GPU训练 Defined in :numref:sec_image_augmentation if isinstance(X, list): # 微调BERT中所需稍后讨论 X [paddle.to_tensor(x, placedevices[0]) for x in X] else: X paddle.to_tensor(X, placedevices[0]) y paddle.to_tensor(y, placedevices[0]) net.train() trainer.clear_grad() pred net(X) l loss(pred, y) l.sum().backward() trainer.step() train_loss_sum l.sum() train_acc_sum d2l.accuracy(pred, y) return train_loss_sum, train_acc_sum完整训练循环train_ch13train_ch13负责多 GPU 下的完整训练流程维护训练损失、训练准确度、实例数、样本数四维累加器d2l.Accumulator(4)每个 epoch 内周期性更新动画曲线并在每个 epoch 结束时用 GPU 评估测试准确率最后打印平均损失、训练/测试准确率与吞吐量examples/sec#save def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devicesd2l.try_all_gpus(), split_fd2l.split_batch): 用多GPU进行模型训练 timer, num_batches d2l.Timer(), len(train_iter) animator d2l.Animator(xlabelepoch, xlim[1, num_epochs], ylim[0, 1], legend[train loss, train acc, test acc]) for epoch in range(num_epochs): # 4个维度储存训练损失训练准确度实例数特点数 metric d2l.Accumulator(4) for i, (features, labels) in enumerate(train_iter): timer.start() l, acc train_batch_ch13( net, features, labels, loss, trainer, devices, split_f) metric.add(l, acc, labels.shape[0], labels.size) timer.stop() if (i 1) % (num_batches // 5) 0 or i num_batches - 1: animator.add(epoch (i 1) / num_batches, (metric[0] / metric[2], metric[1] / metric[3], None)) test_acc d2l.evaluate_accuracy_gpus(net, test_iter, split_f) animator.add(epoch 1, (None, None, test_acc)) print(floss {metric[0] / metric[2]:.3f}, train acc f{metric[1] / metric[3]:.3f}, test acc {test_acc:.3f}) print(f{metric[2] * num_epochs / timer.sum():.1f} examples/sec on f{str(devices)})PyTorch 版本将网络包装为nn.DataParallel(net, device_idsdevices)以利用全部 GPU并用d2l.evaluate_accuracy_gpu在 GPU 上评估该函数在 d2l/torch.py 中实现评估前会自动切换到net.eval()模式Paddle 版本使用paddle.DataParallel(net)#save def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devicesd2l.try_all_gpus()): 用多GPU进行模型训练 timer, num_batches d2l.Timer(), len(train_iter) animator d2l.Animator(xlabelepoch, xlim[1, num_epochs], ylim[0, 1], legend[train loss, train acc, test acc]) net nn.DataParallel(net, device_idsdevices).to(devices[0]) for epoch in range(num_epochs): # 4个维度储存训练损失训练准确度实例数特点数 metric d2l.Accumulator(4) for i, (features, labels) in enumerate(train_iter): timer.start() l, acc train_batch_ch13( net, features, labels, loss, trainer, devices) metric.add(l, acc, labels.shape[0], labels.numel()) timer.stop() if (i 1) % (num_batches // 5) 0 or i num_batches - 1: animator.add(epoch (i 1) / num_batches, (metric[0] / metric[2], metric[1] / metric[3], None)) test_acc d2l.evaluate_accuracy_gpu(net, test_iter) animator.add(epoch 1, (None, None, test_acc)) print(floss {metric[0] / metric[2]:.3f}, train acc f{metric[1] / metric[3]:.3f}, test acc {test_acc:.3f}) print(f{metric[2] * num_epochs / timer.sum():.1f} examples/sec on f{str(devices)})组装训练train_with_data_aug最后定义train_with_data_aug函数获取所有可用 GPUd2l.try_all_gpus()在 d2l/torch.py 中实现无 GPU 时回退到 CPU构建 ResNet-18 并用 Xavier 初始化权重使用Adam优化器学习率 0.001将图像增广作用于训练集训练 10 个 epoch# MXNet batch_size, devices, net 256, d2l.try_all_gpus(), d2l.resnet18(10) net.initialize(initinit.Xavier(), ctxdevices) def train_with_data_aug(train_augs, test_augs, net, lr0.001): train_iter load_cifar10(True, train_augs, batch_size) test_iter load_cifar10(False, test_augs, batch_size) loss gluon.loss.SoftmaxCrossEntropyLoss() trainer gluon.Trainer(net.collect_params(), adam, {learning_rate: lr}) train_ch13(net, train_iter, test_iter, loss, trainer, 10, devices)# PyTorch batch_size, devices, net 256, d2l.try_all_gpus(), d2l.resnet18(10, 3) def init_weights(m): if type(m) in [nn.Linear, nn.Conv2d]: nn.init.xavier_uniform_(m.weight) net.apply(init_weights) def train_with_data_aug(train_augs, test_augs, net, lr0.001): train_iter load_cifar10(True, train_augs, batch_size) test_iter load_cifar10(False, test_augs, batch_size) loss nn.CrossEntropyLoss(reductionnone) trainer torch.optim.Adam(net.parameters(), lrlr) train_ch13(net, train_iter, test_iter, loss, trainer, 10, devices)# Paddle batch_size, devices, net 256, d2l.try_all_gpus(), d2l.resnet18(10, 3) def init_weights(m): if type(m) in [nn.Linear, nn.Conv2D]: nn.initializer.XavierUniform(m.weight) net.apply(init_weights) def train_with_data_aug(train_augs, test_augs, net, lr0.001): train_iter load_cifar10(True, train_augs, batch_size) test_iter load_cifar10(False, test_augs, batch_size) loss nn.CrossEntropyLoss(reductionnone) trainer paddle.optimizer.Adam(learning_ratelr, parametersnet.parameters()) train_ch13(net, train_iter, test_iter, loss, trainer, 10, devices[:1])几点说明仓库中的d2l.resnet18是稍加修改的 ResNet-18见 d2l/torch.py使用了更小的卷积核3×3步长 1填充 1、删除了最大汇聚层输入通道数通过参数in_channels指定PyTorch/Paddle 版本传入3以匹配彩色图像输出类别数为 10对应 CIFAR-10 的 10 个类别批量大小为 256数据读取采用 4 个并行进程d2l.get_dataloader_workersPaddle 版本训练时仅使用devices[:1]的单个设备。最后使用前面定义的随机左右翻转增广管线train_augs/test_augs直接启动训练#tab all train_with_data_aug(train_augs, test_augs, net)训练结束后会输出类似loss X.XXX, train acc X.XXX, test acc X.XXX以及X.X examples/sec on [...]的结果用于与不使用增广的基线对比。小结图像增广基于现有的训练数据生成随机图像从而提高模型的泛化能力为了在预测过程中得到确切的结果通常只对训练样本进行图像增广预测阶段不使用带随机操作的增广深度学习框架MXNet/Gluon、PyTorch、Paddle提供了大量不同的图像增广方法这些方法可以通过Compose被同时应用。练习与延伸思考验证增广的收益在不使用图像增广的情况下训练模型即执行train_with_data_aug(no_aug, no_aug)并与使用增广的训练结果、测试精度对比。这个对比实验能否支持图像增广可以减轻过拟合的论点为什么组合多种增广在基于 CIFAR-10 的模型训练中同时组合多种不同的图像增广方法翻转、随机裁剪、颜色抖动等能否进一步提高测试准确率可尝试调整scale、ratio、抖动幅度等超参数观察变化。探索更多方法查阅你所使用深度学习框架torchvision.transforms 对应的 PyTorch 生态、飞桨paddlevision.transforms、Gluongluon.data.vision.transforms的在线文档了解它还提供了哪些其他图像增广方法例如旋转、缩放、仿射变换、灰度化、高斯模糊、随机擦除等并思考它们适用于哪些场景。附相关章节与仓库资源索引本文所属章节chapter_computer-vision/image-augmentation.md图像增广的多 GPU 训练基础多 GPU 训练简洁实现ResNet-18 模型定义ResNet 章节及仓库源码 d2l/torch.py、d2l/mxnet.py、d2l/paddle.py工具函数实现show_imagesd2l/torch.py、try_all_gpusd2l/torch.py、evaluate_accuracy_gpud2l/torch.py示例原图img/cat1.jpg赞分享人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载相关推荐《动手学深度学习》实战Kaggle 图像分类竞赛 CIFAR-10 全流程指南《动手学深度学习》实战Kaggle 图像分类竞赛 CIFAR 10 全流程指南 本指南基于《动手学深度学习》d2l zh仓库中的 kaggle cifar人工智能深度学习机器学习教程PaddlePaddle深度学习实战Kaggle CIFAR-10图像分类竞赛全流程解析PaddlePaddle深度学习实战Kaggle CIFAR 10图像分类竞赛全流程解析 引言 计算机视觉是深度学习最重要的应用领域之一而图像分类作为计算机文档教程人工智能深度学习从提示词到 PPTX 只要 10 分钟Presenton 本地 AI 演示文稿生成器上手指南从提示词到 PPTX 只要 10 分钟Presenton 本地 AI 演示文稿生成器上手指南 周五晚上还在改 PPT 字体或者临时被拉去分享手里却没有现成人工智能深度学习机器学习教程上一篇NASA ICER算法从论文到代码icer_compression实现原理深度解读下一篇MaterialScrollBar核心组件探秘AlphabetIndicator与DateAndTimeIndicator使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表