第三章 基于Pytorch的深度学习开发
前面章节我们已经了解tensor及其操作,这章主要就是学习如何用Pytorch进行基础深度学习应用开发。
本章主要内容
- 创建一深度学习模型
- 通过普通模式进行训练
- 测试模型效果
- 同时对超参数进行微调以提高模型精度和速度
- 部署模型到生产环境中
在整个过程中的每一步,都将提供相应的代码参考及一些小建议。
在后面的篇章中,本书也会提供其它更加复杂模型来阐述一个完整的深度学习结构中各个环节的技巧及意义,如自主化,优化,加速,分布式训练及高效部署方案等等。
本章主要以一个基本的神经网络模型结构展开讨论的,即达到以小见大的作用。
整体流程
虽然每个人所构建的模型结构各不相同,但整个流程却是一样的。即不管是监督学习,非监督学习或是半监督学习,整个流程还都是 训练,测试,部署。
- 在训练过程中,每次epoch后,我们都用验证数据(validation data)去验证一下我们的模型并微调超参数,使模型能达到一个比较好的泛化能力。
- 最后通过测试数据(unseen data)进行模型评价,验证模型是否具有泛化性。
- 深度学习模型开发的最后一步就是模型的部署,包括服务器或智能终端部署。
数据预处理(Data Preparation)
-
数据加载 Data Loading
Pytorch内置了数据处理的几个类及工具,诸如Dataset,DataLoader和Sampler类。
- Dataset类提供怎么从文件或数据源里获取和预处理数据的方法
- Sampler类提供了如何进行数据采样并形成批量数据
- DataLoader类联合了Dataset和Sampler进行数据分批次的迭代提取
Pytorch的一些包,如Torchvision和Torchtext等也都提供了一些不错数据加载处理方法。
torchvison.datasets就提供了很多的子类用于加载一些不错的图片数据(CIFAR-10,MNIST等)。
示例:CIFAR10数据加载及预览
from torchvision.datasets import CIFAR10
from PIL import Image
train_data = CIFAR10(root='./train/', train=True, download=True)
print(train_data)
"""
Dataset CIFAR10
Number of datapoints: 50000
Root location: ./train/
Split: Train
"""
print(len(train_data), train_data.data.shape, train_data.classes, train_data.class_to_idx)
print(train_data[0])
"""
50000
(50000, 32, 32, 3)
['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
{'airplane': 0, 'automobile': 1, 'bird': 2, 'cat': 3, 'deer': 4, 'dog': 5, 'frog': 6, 'horse': 7, 'ship': 8, 'truck': 9}
(<PIL.Image.Image image mode=RGB size=32x32 at 0x2577B5677F0>, 6)
"""
test_data = CIFAR10(root='./test/', train=False, download=True)
print(test_data)
"""
Dataset CIFAR10
Number of datapoints: 10000
Root location: ./test/
Split: Test
"""
print(len(test_data))
print(test_data.data.shape)
"""
10000
(10000, 32, 32, 3)
"""
查看一下图片数据
plt show
- 数据转换 Data Transforms
一般情况下,我们都需要将原始数据进行转换后,才能输出到Pytorch的处理流程中,即转换为torch形式数据。
这些转换操作基本上都是通过transforms包里提供的各类方法进行一系列处理操作。
示例:对CIFAR10数据进行一些预处理操作
from torchvision.datasets import CIFAR10
from torchvision import transforms
# 定义一系列transform算子,随机裁剪,水平翻转等
train_transforms = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(
mean=(0.4914, 0.4822, 0.4465),
std=(0.2023, 0.1994, 0.2010)
)
])
# 通过参数tranform指定数据预处理操作
train_data = CIFAR10(root="./train/",
train=True,
download=True,
transform=train_transforms
)
print(train_data)
"""
Dataset CIFAR10
Number of datapoints: 50000
Root location: ./train/
Split: Train
StandardTransform
Transform: Compose(
RandomCrop(size=(32, 32), padding=4)
RandomHorizontalFlip(p=0.5)
ToTensor()
Normalize(mean=(0.4914, 0.4822, 0.4465), std=(0.2023, 0.1994, 0.201))
)
"""
# torch.Size([3, 32, 32])
print(train_data[0][0].size())
print(train_data[0])
"""
(tensor([[[-2.4291, -2.4291, -2.4291, ..., -2.4291, -2.4291, -2.4291],
[-2.4291, -2.4291, -2.4291, ..., -2.4291, -2.4291, -2.4291],
[-2.4291, -2.4291, -2.4291, ..., -2.4291, -2.4291, -2.4291],
...,
[ 1.1959, 0.4981, 0.0522, ..., 1.1765, -0.1610, -2.4291],
[ 1.3122, 0.8276, 0.4981, ..., -0.0253, -1.0527, -2.4291],
[ 1.4673, 1.1765, 0.9051, ..., -1.3435, -1.7894, -2.4291]],
[[-2.4183, -2.4183, -2.4183, ..., -2.4183, -2.4183, -2.4183],
[-2.4183, -2.4183, -2.4183, ..., -2.4183, -2.4183, -2.4183],
[-2.4183, -2.4183, -2.4183, ..., -2.4183, -2.4183, -2.4183],
...,
[ 0.1188, -0.4516, -0.8646, ..., 0.6301, -0.7269, -2.4183],
[ 0.2564, -0.0189, -0.2352, ..., -0.5892, -1.4742, -2.4183],
[ 0.5318, 0.4924, 0.3154, ..., -1.8479, -2.0446, -2.4183]],
[[-2.2214, -2.2214, -2.2214, ..., -2.2214, -2.2214, -2.2214],
[-2.2214, -2.2214, -2.2214, ..., -2.2214, -2.2214, -2.2214],
[-2.2214, -2.2214, -2.2214, ..., -2.2214, -2.2214, -2.2214],
...,
[-1.7141, -1.7336, -1.5580, ..., -0.4460, -1.2849, -2.2214],
[-1.9092, -1.8507, -1.5385, ..., -1.2654, -1.7141, -2.2214],
[-1.7922, -1.7531, -1.6751, ..., -2.0458, -2.0458, -2.2214]]]), 6)
"""
# 对于测试数据同样需要做相应的处理,至少也要将图像数据转换为Tensor形式
test_transforms = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(
(0.4914, 0.4822, 0.4465),
(0.2023, 0.1994, 0.2010))
])
test_data = CIFAR10(
root="./test/",
train=False,
transform=test_transforms)
print(test_data)
"""
Dataset CIFAR10
Number of datapoints: 10000
Root location: ./test/
Split: Test
StandardTransform
Transform: Compose(
ToTensor()
Normalize(mean=(0.4914, 0.4822, 0.4465), std=(0.2023, 0.1994, 0.201))
)
"""
对比预处理前后照片效果图
transform effort
从图中我们可以发现前后数据发生了一些变化,可能对于我们来说看不懂,但经过这样的处理后,模型的预测效率有不错的提升。
%matplotlib inline
import matplotlib.pyplot as plt
from torchvision.datasets import CIFAR10
from torchvision import transforms
# 加载数据,不做任何预处理
train_data_ori = CIFAR10(root='./train/', train=True, download=True)
# 定义一系列transform算子,随机裁剪,水平翻转等
train_transforms = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(
mean=(0.4914, 0.4822, 0.4465),
std=(0.2023, 0.1994, 0.2010)
)
])
# 通过参数tranform指定数据预处理操作
train_data_trans = CIFAR10(root="./train/",
train=True,
download=True,
transform=train_transforms
)
data_ori, label_ori = train_data_ori[0]
data_trans, label_trans = train_data_trans[0]
# transform前后图片对比
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.imshow(data_ori)
plt.subplot(1, 2, 2)
# 将tensor转换为PIL Image格式
image_trans = transforms.ToPILImage()(data_trans) # 自动转换为0-255
plt.imshow(image_trans)
plt.tight_layout()
plt.show()
- 数据批量化 Data Batching
因为在实际模型训练中,受限于我们的的资源条件等因素,也不可能一下子把所有数据全部加载到内存中进行计算的,通常情况下都是会对训练数据进行批量化处理,每批次进行迭代训练模型,即在有限资源条件下快速训练模型效果。
这样做不仅达到高效的模型训练效果,同时也能充分发挥GPUs的并行计算优势。
同样数据批处理也十分简单的,只要通过torch.utils.data.DataLoader类即可实现。
示例:对训练数据进行批量化处理
下面的例子是有放回的批量化处理,每批次有16个样本进行模型训练计算。经过DataLoader处理过的数据相当于一个迭代器,可以通过next()及iter()方法进行数据获取next(iter(train_loader))。
train_loader = torch.utils.data.DataLoader(
train_data_trans,
batch_size=16,
shuffle=True
)
data_batch, labels_batch = next(iter(train_loader))
# 每批次的数据情况,16个 3*32*32
print(data_batch.size())
print(labels_batch.size())
"""
torch.Size([16, 3, 32, 32])
torch.Size([16])
"""
普通应用中数据预处理(General Data Preparation)
torch.utils.data
前面的例子我们知道了图片数据是如何通过torchvision这个包进行加载,转换和批处理。但对于我们实际应用中的数据,我们是要怎么来做这些预处理呢?Pytorch提供了一个内置模块来帮我们完成这些工作,即torch.utils.data。
Pytorch提供了对数据进行映射和迭代类型的数据集类torch.utils.data.Dataset,通过继承其并重写相关函数方法实现数据的加载,处理及返回等预处理操作。
-
Dataset类
在实际应用中,子类需要重写getitem(),len()这两个方法。- getitem(),通过一给定key取得样本数据(数据及标签值)
- len(),返回数据size
-
Sampler类
提供数据采样器的方法,且这些采样器一般不直接使用,而是直接内嵌在数据加载器中,作为一参数配置一起使用的。
sampler -
DataLoader类
Dataset类返回包含数据和相关信息的数据对象,Sampler类以特定方式或随机返回实际数据本身,DataLoader类就是把Dataset和Sampler类联合起来构建出一数据迭代器返回数据。
torch.utils.data.DataLoader(
dataset,
batch_size=1,
shuffle=False,
sampler=None,
batch_sampler=None,
num_workers=0,
collate_fn=None,
pin_memory=False,
drop_last=False,
timeout=0,
worker_init_fn=None,
multiprocessing_context=None,
generator=None)
在正常情况下,dataset,batch_size,shuffle和sampler这几个参数比较常用到,其他一些参数主要是在特殊场景下使用,主要还是要根据实际应用来选择。期中num_workers主要是利用cpu的多核技术来并行处理生成数据,提高效率。
If you write your own dataset class, all you need to do is call the built-in DataLoader to generate an iterable for your data. There is no need to create a dataloader class from scratch.
网友评论