【Pytorch教程】迅速入门Pytorch深度学习框架
@TOC
前言
本文只是对于pytorch深度学习框架的使用方法的介绍,如果涉及算法中复杂的数学原理,本文将不予阐述,敬请读者自行阅读相关论文或者文献。
1.tensor基础操作
1.1 tensor的dtype类型
float32
32位float
float
floa
float64
64位float
double
double
float16
16位float
bfloat16
比float范围大但精度低
int8
8位int
int16
16位int
short
short
int32
32位int
int
int
int64
64位int
long
long
complex32
32位complex
complex64
64位complex
cfloat
complex float
complex128
128位complex float
cdouble
complex double
1.2 创建tensor(建议写出参数名字)
创建tensor时,有很多参数可以选择,为节省篇幅,本文在列举API时只列举一次,不列举重载的API。
1.2.1 空tensor(无用数据填充)
API
size:[行数,列数]
dtype(deepth type):数据类型
device:选择运算设备
requires_grad:是否进行自动求导,默认为False
示例
输出
1.2.2 全一tensor
size:[行数,列数]
dtype(deepth type):数据类型
device:选择运算设备
requires_grad:是否进行自动求导,默认为False
1.2.3 全零tensor
1.2.4 随机值[0,1)的tensor
1.2.5 随机值为整数且规定上下限的tensor
API
示例
输出
1.2.6 随机值均值0方差1的tensor
1.2.7 从列表或numpy数组创建tensor
如果使用
torch.from_numpy(),返回的tensor与ndarray共享内存。
1.3 tensor常用成员函数和成员变量
1.3.1 转为numpy数组
只有在CPU上运算的tensor才可以转为numpy数组
tensor.requires_grad属性为True的tensor不能转为numpy数组
1.3.2 获得单元素tensor的值item
item如果tensor只有一个元素,就返回它的值
如果tensor有多个元素,抛出ValueError
1.3.3 获取维度个数
返回一个int表示维度个数
1.3.4 获取数据类型
1.3.5 获取形状
使用
.shape效果相同
1.3.6 浅拷贝与深拷贝
detach函数浅拷贝
假设有模型A和模型B,我们需要将A的输出作为B的输入,但训练时我们只训练模型B. 那么可以这样做:
它可以使两个计算图的梯度传递断开,从而实现我们所需的功能。
返回一个新的tensor,新的tensor和原来的tensor共享数据内存,但不涉及梯度计算,即requires_grad=False。修改其中一个tensor的值,另一个也会改变,因为是共享同一块内存。
输出
深拷贝
法一:
.clone().detach()法二:
.new_tensor()
1.3.7 形状变换
转置
向量或矩阵转置
返回值与原tensor共享内存!
指定两个维度进行转置:
返回值与原tensor共享内存!
对矩阵来说,
.t()等价于.permute(0, 1)
多维度同时转置
把要转置的维度放到对应位置上,比如对于三维tensor,x、y、z分别对应0、1、2,如果想要转置x轴和z轴,则输入2、1、0即可
返回值与原tensor共享内存!
cat堆叠
cat可以把两个或多个tensor沿着指定的维度进行连接,连接后的tensor维度个数不变,指定维度上的大小改变,非指定维度上的大小不变。譬如,两个shape=(3,)行向量按dim=0连接,变成1个shape=(6,)的行向量;2个3阶方阵按dim=0连接,就变成1个(6, 3)的矩阵。
cat在使用时对输入的这些tensor有要求:除了指定维度,其他维度的大小必须相同。譬如,1个shape=(1, 6)的矩阵可以和1个shape=(2, 6)的矩阵在dim=0连接。
例子可以参考下面的定义和注释。
返回值与原tensor不共享内存!
stack堆叠
stack与cat有很大的区别,stack把两个或多个tensor在dim上创建一个全新的维度进行连接,非指定维度个数不变,创建的维度的大小取决于这次连接使用了多少个tensor。譬如,3个shape=(3,)行向量按dim=0连接,会变成一个shape=(3, 3)的矩阵;两个3阶方阵按dim=-1连接,就变成一个(3, 3, 2)的tensor。
返回值与原tensor不共享内存!
view改变形状
view先把数据变成一维数组,然后再转换成指定形状。变换前后的元素个数并不会改变,所以变换前后的shape的乘积必须相等。详细例子如下:
返回值与原tensor共享内存
reshape改变形状
reshape与view的区别如下:
view只能改变连续(.contiguous())的tensor,如果已经对tensor进行了permute、transpose等操作,tensor在内存中会变得不连续,此时调用view会报错。且view方法与原来的tensor共享内存。reshape再调用时自动检测原tensor是否连续,如果是,则等价于view;如果不是,先调用.contiguous(),再调用view,此时返回值与原来tensor不共享内存。
1.3.8 数学运算
mean/sum/median/mode
均值/和/中位数/众数
norm/dist
范数/距离
std/var
标准差/方差
cumsum/cumprod
累加/累乘
以上大多数函数都有一个参数 dim,用来指定这些操作是在哪个维度上执行的。关于 dim(对应于 Numpy 中的 axis)的解释众说纷纭,这里提供一个简单的记忆方式。
假设输入的形状是 (m, n, k):
如果指定
dim=0,输出的形状就是(1, n, k)或者(n, k);如果指定
dim=1,输出的形状就是(m, 1, k)或者(m, k);如果指定
dim=2,输出的形状就是(m, n, 1)或者(m, n)。
size 中是否有 “1”,取决于参数 keepdim,keepdim=True 会保留维度1。从 PyTorch 0.2.0 版本起,keepdim 默认为 False。注意,以上只是经验总结,并非所有函数都符合这种形状变化方式,如 cumsum。
1.3.9 使用指定设备计算tensor
to可以把tensor转移到指定设备上。
2.线性回归模型
2.1 自动求导机制
在pytorch中,如果设置一个 tensor 的属性 requires_grad 为 True,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 tensor.backward 函数,来自动计算所有的梯度。这个张量的所有梯度将会自动累加到 grad 属性。
由于是累加,因此在进行线性回归模型的计算时,每轮都要用 tensor.zero_ 函数清空一次 grad 属性
示例
输出
2.2 nn.Module的继承(from torch import nn)
2.2.1 概述
nn.Module是torch.nn提供的一个类,是pytorch中定义网络的必要的一个父类,在这个类中定义了很多有用的方法,使我们非常方便地计算。在我们进行网络的定义时,有两个地方需要特别注意:
在定义成员变量时必须调用super函数,继承父类__init__参数,即,在__init__中必须调用super(<the name of the variable>,self)函数
通常还会在__init__中定义网络的结构
必须定义forward函数,表示网络中前向传播的过程
2.2.2 实例
其中,nn.Linear函数的参数为:输入的特征量,输出的特征量。
2.3 优化器类(from torch import optim)
2.3.1 概述
优化器(optimizer),用来操纵参数的梯度以更新参数,常见的方法有随机梯度下降(stochastic gradient descent)(SGD)等。
torch.optim.SGD(参数,float 学习率)
torch.optim.Adam(参数,float 学习率)
2.3.2 流程
调用Module.parameters函数获取模型参数,并定义学习率,进行实例化
用实例化对象调同 .zero_grad 函数,将参数重置为0
调用tensor.backward函数反向传播,获得梯度
用实例化对象调用 .step 函数更新参数
2.3.3 动态学习率(import torch.optim.lr_scheduler)
lr_scheduler允许模型在训练的过程中动态更新学习率,且提供了许多种策略可供选择,以下列举一些常用的:
指数衰减:在训练的过程中,学习率以设定的gamma参数进行指数的衰减。
固定步长衰减:在固定的训练周期后,以指定的频率进行衰减。
用法:创建scheduler的时候绑定optimizer对象,然后在调用
optimizer.step()后面跟着scheduler.step()即可。
2.4 代价函数(from torch import nn)
在torch.nn中已经定义好了很多代价函数,只需要调用它们并且传入真实值、预测值,就可以返回结果,例如:
均方误差:nn.MSELoss()
交叉熵误差:nn.CrossEntropyLoss()
当然,也可以自己定义loss的计算过程。
2.5 评估模型
Module.eval()表示设置模型为评估模式,即预测模式
Module.train(mdoe=True)表示设置模型为训练模式
2.6 线性回归模型的建立
2.6.1 流程
定义网络,注意:实现super函数和forward函数
准备数据
实例化网络、代价函数、优化器
进行循环,调用Module.forward函数前向传播,调用代价函数进行计算,调用优化器类进行参数更新
使用pyplot进行模型评估
2.6.2 示例
输出
绘制图

3.数据集和数据加载器 (from torch.utils.data import Dataset,DataLoader)
3.1 Dataset类的继承(from torch.utils.data import Dataset)
3.1.1 概述
在pytorch中提供了数据集的父类torch.utils.data.Dataset,继承这个父类,我们可以非常快速地实现对数据的加载,与继承nn.Module类一样,我们同样必须定义一些必要的成员函数
__getitem__(self,index),用来进行索引,可以用 [ ]
__len__(self),用来获取元素个数
3.1.2 实例
输出
3.2 DataLoader类
3.2.1 API
dataset:以Dataset类为父类的自定义类的实例化对象
batch_size:批处理的个数
shuffle:bool类型,若为True则表示提前打乱数据
num_workers:加载数据时用到的线程数
drop_last :bool类型,若为True:这个是对最后的未完成的batch来说的,比如你的batch_size设置为64,而一个训练集只有100个样本,那么训练的时候后面的36个就被扔掉了。如果为False(默认),那么会继续正常执行,只是最后的batch_size会小一点。
timeout:如果是正数,表明等待从worker进程中收集一个batch等待的时间,若超出设定的时间还没有收集到,那就不收集这个内容了。这个numeric应总是大于等于0,默认为0
3.2.2 示例
输出
可见,DataLoader是一个可遍历对象,每轮中返回的数据以列表的方式存储,且列表中每个元素都是一个元组,列表的长度等于Dataset.__getitem__返回的列表长度,元组的长度等于batch_size参数的大小
4.图像处理:手写数字识别
4.1 torchvision模块
4.1.1 transforms.ToTensor类(仿函数)
将原始的PILImage数据类型或者numpy.array数据类型化为tensor数据类型。
如果 PIL Image 属于 (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)中的一种图像类型,或者 numpy.ndarray 格式数据类型是 np.uint8 ,则将 [0, 255] 的数据转为 [0.0, 1.0] ,也就是说将所有数据除以 255 进行归一化。
4.1.2 transforms.Normalize类(仿函数)
mean:数据类型为元组,元组的长度取决于通道数
std:数据类型为元组,元组的长度取决于通道数
此函数可以将tensor进行标准化,使其在每个通道上都转化为均值为mean,标准差为std的高斯分布。
4.1.3 transforms.Compose类(仿函数)
transforms:数据类型为列表,列表中每个元素都是transforms模块中的一个类,如ToTensor和Normalize(隐式构造)。
此函数可以将许多transforms类结合起来同时使用。
4.1.4 示例
4.2 网络构建
4.2.1 激活函数大全

在pytorch中已经实现了上述很多的激活函数,下面我们将使用ReLU激活函数进行网络构建。
4.2.2 演示代码(在gpu上)
输出
loss散点图

5.制作图片数据集(以flower102为例)
在刚刚的MNIST手写数字识别分类任务中,我们使用的数据集是pytorch官方内置的图片数据集。现在,我们要从零开始,尝试制作我们自己的数据集。
Oxford 102 Flower 是一个图像分类数据集,由 102 个花卉类别组成。被选为英国常见花卉的花卉。每个类别由 40 到 258 张图像组成。图像具有大尺度、姿势和光线变化。此外,还有一些类别在类别内有很大的变化,还有几个非常相似的类别。这里是flower102数据集的下载地址。解压后的文件目录如下:

5.1 建立数据集骨架
如第三章一样建立即可,如下:
5.2 建立从名称到数字标签的映射

在训练集中,这102种花的类别名称如上图所示(我这里是经过重命名的),我们定义名称flower1为数字标签1,这样我们就建立了一个映射。接下来,稍微修改一下构造函数,就可以实现全部的映射。如下:
结果如下:
5.3 建立csv数据
在建立了从名称到数字标签的映射后,我们希望有一个csv文件,里面存储了所有的图片路径及其数字标签,接下来,我们将定义一个load_csv函数去完成这件事,如下:
然后,我们获得了一个如下的csv文件:

5.4 完善成员函数和transform过程
在完成了load_csv函数后,这个数据集基本制作完成,接下来只需要完善__len__函数和__getitem__函数,并定义transform过程即可。
5.5 DataLoader检验
成功显示:

6.迁移学习
6.1 现有模型的保存和加载
6.1.1 保存(torch.save函数)
我们要保存的是:
实例化的网络的数据
实例化的优化器的数据
我们只需要把string类型的文件名作为参数输入即可
把数据加载进网络
Module.load_state_dict函数,我们只需要用torch.load函数的返回值作为参数即可
把数据加载进优化器
optim.load_state_dict函数,我们只需要用torch.load函数的返回值作为参数即可
6.1.3 示例
6.2 使用预训练的模型(以resnet50为例)
pytoch官方提供了不少与训练的模型可供使用,如下:
关于这些模型的详细用途,可以自行前往pytorch官网查阅相关资料,具体原理本文不再涉及。
6.2.1 确定初始化参数
在使用预训练模型的过程中,最重要的一步是,确定这个预训练模型中哪些参数是需要训练的,哪些参数是不需要训练的,哪些参数是要修改的。
首先,查看一下resnet50的网络结构:
看到最后一层是一个1000分类的全连接层,而我们第五章制作的数据集里,只需要102分类,因此,我们选择只修改最后一层的参数并训练。如下所示:
6.3 开始训练
训练的流程和记录如第四章所示即可,如下:
下面是我训练5轮的结果:
最后更新于
这有帮助吗?