Pytorch袖珍手册之四

pytorch pocket reference

第三章 基于Pytorch的深度学习开发

前面章节我们已经了解tensor及其操作,这章主要就是学习如何用Pytorch进行基础深度学习应用开发。
本章主要内容

  • 创建一深度学习模型
  • 通过普通模式进行训练
  • 测试模型效果
  • 同时对超参数进行微调以提高模型精度和速度
  • 部署模型到生产环境中
    在整个过程中的每一步,都将提供相应的代码参考及一些小建议。

在后面的篇章中,本书也会提供其它更加复杂模型来阐述一个完整的深度学习结构中各个环节的技巧及意义,如自主化,优化,加速,分布式训练及高效部署方案等等。

本章主要以一个基本的神经网络模型结构展开讨论的,即达到以小见大的作用。

整体流程

虽然每个人所构建的模型结构各不相同,但整个流程却是一样的。即不管是监督学习,非监督学习或是半监督学习,整个流程还都是 训练,测试,部署

  • 在训练过程中,每次epoch后,我们都用验证数据(validation data)去验证一下我们的模型并微调超参数,使模型能达到一个比较好的泛化能力。
  • 最后通过测试数据(unseen data)进行模型评价,验证模型是否具有泛化性。
  • 深度学习模型开发的最后一步就是模型的部署,包括服务器或智能终端部署。
overall process

数据预处理(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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容