进阶篇全流程学习《20天掌握Pytorch实战》纪实 | Day02 | 图片数据建模流程范例
Posted 府学路18号车神
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进阶篇全流程学习《20天掌握Pytorch实战》纪实 | Day02 | 图片数据建模流程范例相关的知识,希望对你有一定的参考价值。
💖作者简介:大家好,我是车神哥,府学路18号的车神🥇
⚡About—>车神:从寝室到实验室最快3分钟,最慢3分半(那半分钟其实是等红绿灯)
📝个人主页:车手只需要车和手,压力来自论文_府学路18号车神_CSDN博客
🥇 官方认证:人工智能领域优质创作者
🎉点赞➕评论➕收藏 == 养成习惯(一键三连)😋⚡希望大家多多支持🤗~一起加油 😁
不定期学习《20天掌握Pytorch实战》,有兴趣就跟着专栏
一起吧~
开源自由,知识无价~
最近读Paper,发现有很多提出的改进和创新都存在很多trick,无法复现。看到一句话,真的很戳搞科研的心,但也很现实:
很多的邻域门槛很低,当某一天那些灌水的paper干不过在网络搜出来的东西,那么这波人也就自动消失了。我们到底应不应该花大量的时间阅读那些最新的研究?应该,但更多的时候,它们不配。
图片数据建模流程范例
🤗前言:torch和torchvision安装
由于我是在PyCharm中进行实验的,所以需要自行安装torch的库,和其他的库安装一样,直接在Terminal中pip就OK了。
pip install torch
pip install torchvision -i https://pypi.tuna.tsinghua.edu.cn/simple/
整个安装过程可能有点慢,包也比较大,耐心等待即可。
如果是使用Jupyter notebook的同学,可以直接使用。(mac系统上pytorch和matplotlib在jupyter中同时跑需要更改环境变量)
所用到的源代码及书籍+数据集以帮各位小伙伴下载放在文末,自取即可~
😁概览
一、 🎉准备数据
老规矩,后面模型的history
需要获取每次迭代的时间,下面的打印时间代码必备~
import os
import datetime
#打印时间
def printbar():
nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("\\n"+"=========="*8 + "%s"%nowtime)
#mac系统上pytorch和matplotlib在jupyter中同时跑需要更改环境变量
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
cifar2数据集
为cifar10数据集
的子集
,只包括前两种类别airplane
和automobile
。
训练集有airplane和automobile图片各5000张,测试集有airplane和automobile图片各1000张。
cifar2任务的目标
是训练一个模型来对飞机airplane和机动车automobile两种图片进行分类
。
我们准备的Cifar2数据集的文件结构如下所示。
在Pytorch
中构建图片数据管道通常有两种方法
。
-
第一种是使用 torchvision中的datasets.ImageFolder来读取图片然后用 DataLoader来并行加载。
-
第二种是通过继承 torch.utils.data.Dataset 实现用户自定义读取逻辑然后用 DataLoader来并行加载。
第二种方法是读取用户自定义数据集的通用方法,既可以读取图片数据集,也可以读取文本数据集。
文档中只介绍了第一种方法,那就直接搞起来吧~
和上一篇的数据构建有所不同
导入库
import torch
from torch import nn
from torch.utils.data import Dataset,DataLoader
from torchvision import transforms,datasets
有的小伙伴可能也没有安装torchvision
,下面给出安装的指令,同样也是在terminal
命令行:
pip install torchvision -i https://pypi.tuna.tsinghua.edu.cn/simple/
国内由于网速的限制,使用命令安装时需在后面加上清华的镜像,这样下载速度会大大提升,安装更容易成功。
下面简单介绍一下torchvision
:
torchvision
是PyTorch中专门用来处理图像的库。这个包中有四个大类。
torchvision.datasets: 用来进行数据加载的,PyTorch团队在这个包中帮我们提前处理好了很多很多图片数据集。
torchvision.models: 为我们提供了已经训练好的模型,让我们可以加载之后,直接使用。
torchvision.transforms: 提供了一般的图像转换操作类
torchvision.utils: 数据处理工作箱,包括四个类,具体可以参考 这里。
- 划分数据集
transform_train = transforms.Compose(
[transforms.ToTensor()])
transform_valid = transforms.Compose(
[transforms.ToTensor()])
ds_train = datasets.ImageFolder("./data/cifar2/train/",
transform = transform_train,target_transform= lambda t:torch.tensor([t]).float()) # 训练集构建
ds_valid = datasets.ImageFolder("./data/cifar2/test/",
transform = transform_train,target_transform= lambda t:torch.tensor([t]).float()) # 验证集构建
print(ds_train.class_to_idx) # 输出训练集的类别与index,按顺序为这些类别定义索引为0,1...
ImageFolder
是一个通用的数据加载器,它要求我们以下面这种格式来组织数据集的训练、验证或者测试图片。
参数解析:
dataset=torchvision.datasets.ImageFolder(
root, transform=None,
target_transform=None,
loader=<function default_loader>,
is_valid_file=None)
root
:图片存储的根目录,即各类别文件夹所在目录的上一级目录。transform
:对图片进行预处理的操作(函数),原始图片作为输入,返回一个转换后的图片。target_transform
:对图片类别进行预处理的操作,输入为 target,输出对其的转换。 如果不传该参数,即对 target 不做任何转换,返回的顺序索引 0,1, 2…loader
:表示数据集加载方式,通常默认加载方式即可。is_valid_file
:获取图像文件的路径并检查该文件是否为有效文件的函数(用于检查损坏文件)
返回的dataset都有以下三种属性:
self.classes
:用一个 list 保存类别名称self.class_to_idx
:类别对应的索引,与不做任何转换返回的 target 对应self.imgs
:保存(img-path, class) tuple的 list
运行结果:
'0_airplane': 0, '1_automobile': 1
- 加载数据
dl_train = DataLoader(ds_train,batch_size = 50,shuffle = True,num_workers=0) # ds_train表示加载的数据集;batch_size:每批次加载了50个样本;num_workers:加载数据的子进程数有3个
dl_valid = DataLoader(ds_valid,batch_size = 50,shuffle = True,num_workers=0) # 验证集的数据加载如上
用到了torch.utils.data.DataLoader
- DataLoader(
dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None
)
参数说明:
- dataset (Dataset): 加载数据的数据集
- batch_size (int, optional): 每批加载多少个样本
- shuffle (bool, optional): 设置为“真”时,在每个epoch对数据打乱.(默认:False)
- sampler (Sampler, optional): 定义从数据集中提取样本的策略,返回一个样本
- batch_sampler (Sampler, optional): like sampler, but returns a batch of indices at a time 返回一批样本. 与atch_size, shuffle, sampler和
drop_last互斥.- num_workers (int, optional): 用于加载数据的子进程数。0表示数据将在主进程中加载。(默认:0)
- collate_fn (callable, optional): 合并样本列表以形成一个 mini-batch. # callable可调用对象
- pin_memory (bool, optional): 如果为 True, 数据加载器会将张量复制到 CUDA 固定内存中,然后再返回它们.
- drop_last (bool, optional): 设定为 True 如果数据集大小不能被批量大小整除的时候, 将丢掉最后一个不完整的batch,(默认:False).
- timeout (numeric, optional): 如果为正值,则为从工作人员收集批次的超时值。应始终是非负的。(默认:0)
- worker_init_fn (callable, optional): If not None, this will be called on each worker subprocess with the worker id (an int in
[0, num_workers - 1]
) as input, after seeding and before data loading.
(default: None).
这里提供下官方的手册:
下面准备数据的最后一步,查看上面加载到管道的数据,我们根据文档内容通过循环遍历输出图片:
# %matplotlib inline
# %config InlineBackend.figure_format = 'svg'
#查看部分样本
from matplotlib import pyplot as plt
plt.figure(figsize=(8,8)) # 设置图片大小,matplot就不解释了,过于基础
for i in range(9): # 循环遍历9个图片
img,label = ds_train[i]
img = img.permute(1,2,0)
ax=plt.subplot(3,3,i+1)
ax.imshow(img.numpy())
ax.set_title("label = %d"%label.item())
ax.set_xticks([])
ax.set_yticks([])
plt.show()
原始数据集图像和上面加载出来的一致,由于图片像素极低,放大后更低更模糊了。
# Pytorch的图片默认顺序是 Batch,Channel,Width,Height
for x,y in dl_train:
print(x.shape,y.shape)
break
注意:这里运行会报错,文档作者不知道是不是会报错,反正我的报错了。已经有博主解决这个问题啦~
原因
:datasets.ImageFolder使用了lambda函数,cell4中num_workers被设置为3,这两个因素共同作用导致报错。解决方法
:依旧是在jupyter notebook环境中,把num_workers=3改为num_workers=0
输出:
torch.Size([50, 3, 32, 32]) torch.Size([50, 1]) # 分别表示(batch size:50, channel:3, height:32, width::32)
对torch.Size
的理解:
- torch.Size括号中有几个数字就是几维
- 第一层(最外层)中括号里面包含了两个中括号(以逗号进行分割),这就是(2,3,4)中的2
第二层中括号里面包含了三个中括号(以逗号进行分割),这就是(2,3,4)中的3
第三层中括号里面包含了四个数(以逗号进行分割),这就是(2,3,4)中的4
二、 🎉定义模型
使用Pytorch通常有三种方式
构建模型
:使用nn.Sequential
按层顺序构建模型,继承nn.Module
基类构建自定义模型,继承nn.Module
基类构建模型并辅助应用模型容器(nn.Sequential
,nn.ModuleList
,nn.ModuleDict
)进行封装。
此处选择通过继承nn.Module
基类构建自定义模型。
#测试AdaptiveMaxPool2d的效果
pool = nn.AdaptiveAvgPool2d((1, 1)) # 输出尺寸指定为1*1
t = torch.randn(10, 8, 32, 32) #
print(pool(t).shape)
关于nn.AdaptiveMaxPool2d
的解释:
- class torch.nn.AdaptiveMaxPool2d(
output_size, return_indices=False
)
对输入信号,提供2维的自适应最大池化操作 对于任何输入大小的输入,可以将输出尺寸指定为H*W,但是输入和输出特征的数目不会变化。可以用来实现全局平均最大池化层output_size=1.
output_size
: 输出信号的尺寸,可以用(H,W)表示HW的输出,也可以使用数字H表示HH大小的输出return_indices
: 如果设置为True,会返回输出的索引。对 nn.MaxUnpool2d有用,默认值是False
关于torch.randn
的解释:
- torch.randn(
*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False
) → Tensor
返回一个符合均值为0,方差为1的正态分布(标准正态分布)中填充随机数的张量
参数解释:
size(int…)
--定义输出张量形状的整数序列。可以是数量可变的参数,也可以是列表或元组之类的集合。
Keyword Arguments:
out
(Tensor, optional) --输出张量dtype
(torch.dtype, optional) --返回张量所需的数据类型。默认:如果没有,使用全局默认值layout
(torch.layout, optional) --返回张量的期望布局。默认值:torch.strideddevice
(torch.device, optional) --返回张量的所需 device。默认:如果没有,则使用当前设备作为默认张量类型.(CPU或CUDA)requires_grad
(bool, optional) –autograd是否应该记录对返回张量的操作(说明当前量是否需要在计算中保留对应的梯度信息)。默认值:False。
输出结果:
torch.Size([10, 8, 1, 1])
- 模型构建
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3,out_channels=32,kernel_size = 3)
self.pool = nn.MaxPool2d(kernel_size = 2,stride = 2)
self.conv2 = nn.Conv2d(in_channels=32,out_channels=64,kernel_size = 5)
self.dropout = nn.Dropout2d(p = 0.1)
self.adaptive_pool = nn.AdaptiveMaxPool2d((1,1))
self.flatten = nn.Flatten()
self.linear1 = nn.Linear(64,32)
self.relu = nn.ReLU()
self.linear2 = nn.Linear(32,1)
self.sigmoid = nn.Sigmoid()
def forward(self,x):
"""
前向传播函数
:param x: 输入,tensor 类型
:return: 返回结果
"""
x = self.conv1(x)
x = self.pool(x)
x = self.conv2(x)
x = self.pool(x)
x = self.dropout(x)
x = self.adaptive_pool(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.relu(x)
x = self.linear2(x)
y = self.sigmoid(x)
return y
net = Net()
print(net)
- 输出打印出来构建的整个网络的具体信息,如池化层、优化器、卷积等设置:
Net(
(conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1))
(pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv2): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1))
(dropout): Dropout2d(p=0.1, inplace=False)
(adaptive_pool): AdaptiveMaxPool2d(output_size=(1, 1))
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear1): Linear(in_features=64, out_features=32, bias=True)
(relu): ReLU()
(linear2): Linear(in_features=32, out_features=1, bias=True)
(sigmoid): Sigmoid()
)
一一解释上面构建的网络:
nn.Conv2d
:Conv2d(in_channels, out_channels, kernel_size, stride=1,padding=0, dilation=1, groups=1,bias=True, padding_mode=‘zeros’)
in_channels
:输入的通道数目 【必选】out_channels
: 输出的通道数目 【必选】kernel_size
:卷积核的大小,类型为int或者元组,当卷积是方形的时候,只需要一个整数边长即可,卷积不是方形,要输入一个元组表示 高和宽。【必选】stride
:卷积每次滑动的步长为多少,默认是 1 【可选】padding
: 设置在所有边界增加 值为 0 的边距的大小(也就是在feature map外围增加几圈 0 ),例如当 padding =1 的时候,如果原来大小为 3 × 3 ,那么之后的大小为 5 × 5 。即在外围加了一圈0 。【可选】dilation
:控制卷积核之间的间距【可选】groups
:控制输入和输出之间的连接。(不常用)【可选】bias
: 是否将一个 学习到的 bias 增加输出中,默认是 True 。【可选】padding_mode
: 字符串类型,接收的字符串只有 “zeros” 和 “circular”。【可选】注意 :参数 kernel_size,stride,padding,dilation 都可以是一个整数或者是一个元组,一个值的情况将会同时作用于高和宽 两个维度,两个值的元组情况代表分别作用于 高 和 宽 维度。
nn.MaxPool2d
:
class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
参数解释:
kernel_size
:表示做最大池化的窗口大小,可以是单个值,也可以是tuple元组stride
:步长,可以是单个值,也可以是tuple元组padding
:填充,可以是单个值,也可以是tuple元组dilation
:控制窗口中元素步幅return_indices
:布尔类型,返回最大值位置索引ceil_mode
:布尔类型,为True,用向上取整的方法,计算输出形状;默认是向下取整。
具体参数的详解在:这里
也可在官方文档内查询。
nn.Dropout2d
:为了防止或减轻过拟合而使用的函数,它一般用在全连接层
Dropout
就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,让其停止工作,这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了。Dropout
常常用于抑制过拟合,pytorch也提供了很方便的函数。但是经常不知道dropout的参数p是什么意思。在TensorFlow中p叫做keep_prob,就一直以为pytorch中的p应该就是保留节点数的比例,但是实验结果发现反了,实际上表示的是不保留节点数的比例。
class torch.nn.AdaptiveMaxPool2d(output_size, return_indices=False)
对输入信号,提供2维的自适应最大池化操作 对于任何输入大小的输入,可以将输出尺寸指定为H*W,但是输入和输出特征的数目不会变化。可以用来实现全局平均最大池化层output_size=1.
参数:
output_size
: 输出信号的尺寸,可以用(H,W)表示HW的输出,也可以使用数字H表示HH大小的输出return_indices
: 如果设置为True,会返回输出的索引。对nn.MaxUnpool2d
有用,默认值是False
nn.Flatten()
:
torch.nn.Flatten(),因为其被用在神经网络中,输入为一批数据,第一维为batch,通常要把一个数据拉成一维,而不是将一批数据拉为一维。所以torch.nn.Flatten()默认从第二维开始平坦化。
torch.nn.Linear(in_features, out_features, bias=True)
nn.Linear()是用于设置网络中的全连接层的,需要注意在二维图像处理的任务中,全连接层的输入与输出一般都设置为二维张量,形状通常为[batch_size, size],不同于卷积层要求输入输出是四维张量。
参数:
in_features
指的是输入的二维张量的大小,即输入的[batch_size, size]
中的size
。out_features
指的是输出的二维张量的大小,即输出的二维张量的形状为[batch_size,output_size]
,当然,它也代表了该全连接层的神经元个数。
从输入输出的张量的shape角度来理解,相当于一个输入为[batch_size, in_features]
的张量变换成了[batch_size, out_features]
的输出张量。
class torch.nn.ReLU(inplace: bool = False)
线性整流函数(Rectified Linear Unit, ReLU),又称修正线性单元。其定义如下图,在横坐标的右侧,ReLU函数为线性函数。在横坐标的右侧,ReLU函数为值为0。
参数:
inplace=True
:inplace为True,将会改变输入的数据,否则不会改变原输入,只会产生新的输出
import torchkeras
torchkeras.summary(net,input_shape= (3,32,32))
输出:
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 32, 30, 30] 896
MaxPool2d-2 [-1, 32, 15, 15] 0
Conv2d-3 [-1, 64, 11, 11] 51,264
MaxPool2d-4 [-1, 64, 5, 5] 0
Dropout2d-5 [-1, 64, 5, 5] 0
AdaptiveMaxPool2d-6 [-1, 64, 1, 1] 0
Flatten-7 [-1, 64] 0
Linear-8 [-1, 32] 2,080
ReLU-9 [-1, 32] 0
Linear-10 [-1, 1] 33
Sigmoid-11 [-1, 1] 0
================================================================
Total params: 54,273
Trainable params: 54,273
Non-trainable params: 0
--------------------以上是关于进阶篇全流程学习《20天掌握Pytorch实战》纪实 | Day02 | 图片数据建模流程范例的主要内容,如果未能解决你的问题,请参考以下文章
进阶篇全流程学习《20天掌握Pytorch实战》纪实 | Day02 | 图片数据建模流程范例