PyTorch学习笔记
1. PyTorch入门
1.1 Tensor
t.Tensor(4, 5)
# 此处构建 4*5 的矩阵,分配了空间,未初始化
x = t.rand(4, 5)
# 使用[0,1]均匀分布随机初始化二维数组
a.shape
# 查看 a 的形状,此处为 a 的属性
a.size()
# 获取 a 的形状
一个Tensor有很多的属性
Tensor作为矩阵是可以相加的,torch提供了多种写法
x + y
# 不改变x和y
t.add(x,y)
# 不改变x和y
t.add(x, y, out=result)
# 不改变x和y,将结果输出到result中,需要先给result分配空间
x.add_(y)
# 将x+y的结果赋给x,y不变
x.add(y)
# x和y都不变
减法也是一样的。
要注意的是矩阵的加减需要行列数相等,和线性代数一样的。
Tensor可以通过 .cuda
方法转为GPU的Tensor,从而享受GPU带来的加速运算。但在进行大规模数据和复杂运算下GPU的优势跟显著。
1.2 Autograd:自动微分
深度学习的算法本质上是通过反向传播求导数,pytorch的autograd模块实现了此功能。
autograd.Variable 是Autograd 中的核心类,Tensor在被封装为Variable的数据结构之后,可以调用它的 .backward 实现方向传播,自动计算所有梯度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M6zFfZm5-1629340824579)(pytorch使用.assets/1626083811297.png)]
Variable主要包含三个属性。
- data: 保存Variable所包含的Tensor
- grad:保存data对应的梯度,grad也是个Variable,而不是Tensor,它和data的形状一样。
- grad_fn:指向一个Function对象,这个Function用来反向传播计算输入的梯度
注意:grad 在方向传播过程中是累加的,这意味着每次运行反向传播,梯度都会累加之前的梯度,所以反向传播之前需把梯度清零。
1.3 神经网络
torch.nn 是专门为神经网络设计的模块化接口。nn 构建于Autograd之上,可用来定义和运行神经网络。nn.Module是 nn 中最重要的类,可以把它看作一个网络的封装,包含网络各层定义及 forward 方法,调用 forward(input) 方法,可以返回前向传播的结果。
基础的前向网络:接受输入, 经过层层传递运算,得到输出。
1.3.1 定义网络
定义网络时,需要继承nn.Module,并实现它的 forward 方法,把网络中具有可学习参数的层放在构造函数 __init__
中。如果某一层不具有可学习的参数,则既可以放在构造函数中,也可以不放。
forward函数的输入和输出都是Variable,只有Variable才具有自动求导功能,Tensor是没有的,所以需要把Tensor封装成Variable。
1.3.2 损失函数
nn 实现了神经网络中大多数的损失函数,例如 nn.MSELoss 用来计算均方误差, nn.CrossEntroLoss 用来计算交叉熵损失。
1.3.3 优化器
在反向传播计算完所有参数的梯度后,还需要使用优化方法更新网络的权重和参数。
torch.optim 实现了深度学习中绝大多数的优化方法。
1.4 CIFAR-10 分类
- 使用torchvision 加载并预处理CIFAR-10 数据集
- 定义网络
- 定义损失函数和优化器
- 训练网络并更新网络参数
- 测试网络
2. Tensor 和 autograd
2.1 Tensor
Tensor ,又叫张量
,可以认为就是一个数组,支持高效的科学计算。
2.1.1 基础操作
创建Tensor
函数 | 功能 |
---|---|
Tensor(*sizes) | 基础构造函数 |
ones(*sizes) | 全1Tensor |
zeros(*sizes) | 全0Tensor |
eye(*sizes) | 对角线为1,其他为0 |
arange(s, e, step) | 从s到e,步长为step |
linespace(s, e, steps) | 从s到e,均匀分成steps份 |
rand/randn(*sizes) | 均匀/标准分布 |
normal(mean, std)/uniform(from, to) | 正态分布/均匀分布 |
randperm(m) | 随机排列 |
其中Tensor函数新建tensor
是最复杂多变的方式,既可以接收list,也可以指定形状,还能传入其他的tensor
tensor.size()返回 torch.Size对象,等价于 tensor.shape
注意,t.Tensor(*sizes)
创建tensor
时,不会直接分配空间,而是计算剩余内存,只有在使用tensor
时才会分配,其他操作都是创建后直接空间分配。
Tensor 操作
tensor.view()
调整tensor的形状,但调整前后元素总数一致。不修改自身数据,新的tensor和源tensor共享内存。
tensor.squeeze()
减少维度
tensor.unsqueeze()
增加维度
tensor.resize_()
调整size,可以修改tensor的尺寸。如果超出原空间大小,则自动分配新的内存空间,如果小于原来的空间大小,则之前的数据保留。
索引操作
于numpy类似。
若无特殊说明,索引出来的结果与tensor共享内存,修改一个,另一个也会跟着修改。
函数 | 功能 |
---|---|
index_select(input, dim, index) | 在指定维度dim上选取 |
masked_select(input, mask) | 使用ByteTensor进行选取 |
non_zero(input) | 非0元素下标 |
gather(input, dim, index) | 根据index,在dim维度上选取数据,输出的size与index一样 |
Tensor类型
默认的tensor时FloatTensor,可通过 t.set_default_tensor_type 修改默认tensor类型
各数据类型之间可以互相转换,type(new_type)是通用的做法。
主元素操作
对tensor的每一个元素进行操作
归并操作
输出形状小于输入形状,并可以沿着某一维度进行指定操作。
使用参数dim
指定在哪个维度执行操作。
比较
线性代数
矩阵的转置会导致存储空间不连续,需调用它的 .contiguous
方法将其转为连续。
2.1.2 Tensor 和 Numpy
Numpy 和 Tensor 共享内存
Numpy的广播法则:
- 让所有输入数组都向其中shape最长的数组看起,shape中不足的部分通过在前面加1补齐
- 两个数组要么在某一个维度长度一致,要么其中一个为1,否则不能计算。
广播法则在快速执行向量化的同时不会占用额外的内存/显存。
2.1.3 内部结构
tensor分为头信息区(Tensor)和存储区(Storage),信息区主要保存tensor的形状(size)、步长(stride)、数据类型(type)等信息,而真正的数据则保存成连续的数组。
2.1.4 其他
持久化
t.save()
t.load()
向量化
2.2 autograd
2.2.1 Variable
Variable封装了tensor,并记录对tensor的操作记录用来构建计算图。
-
data:保存variable所包含的tensor
-
grad:保存data对应的梯度,grad也是variable,而非tensor,它与data形状一致。
-
grad_fn:指向一个Function,记录variable的操作历史。
Variable的构造函数需要传入tensor,同时有两个可选参数。
- requires_grad(bool):是否需要对该variable进行求导。
- volatile(bool):设置为True,构建在该variable上的图都不会求导,专为推理阶段设计
2.2.2 计算图
3. 神经网络nn
3.1 nn.Module
torch.nn的核心结构是Module,它是一个抽象的概念。既可以表示神经网络中的某一个层(layer),也可以表示一个包含很多层的神经网络。
自定义nn.Module
- 继承nn.Module,构造函数调用nn.Module的构造函数,
super(class_name, self).__init__()
或nn.Module.__init__(self)
- 在构造函数中自己定义可学习参数,封装成Parameter
- forward函数实现前向传播过程,其输入可以是一个或多个variable,对x的操作也必须是variable的操作
- 通常将多个layer放在
ModuleList
或Sequential
中。
3.2 常用神经网络层
3.2.1 图像相关层
卷积层(Conv)、池化层(Pool),实际使用中分为一维(1D)、二维(2D)、三维(3D)。池化层又分为平均池化(AvgPool)、最大值池化(Max Pool)、自适应池化(AdaptiveAvgPool)等,通常池化过程中将矩阵变小,提取特征。
除了卷积层和池化层,还常用到一下几个层。
- Linear:全连接层。
- BatchNorm:批规范层
- Dropout:dropout层,用来防止过拟合。
3.2.2 激活函数
激活函数可作为独立的layer使用。其中最常见的就是ReLu。
R e L u ( x ) = m a x ( 0 , x ) ReLu(x)=max(0,x) ReLu(x)=max(0,x)
3.2.3 循环神经网络层
PyTorch中实现了如今常用的三种RNN:RNN(vanilla RNN)、LSTM、和GRN。此外还有对应的三种RNNCell。
RNN和RNNCell层的区别在于前者能够处理整个序列,而后者一次只能处理序列中的一个时间点的数据,前者封装更完备更易于使用,后者更具灵活性。
3.2.4 损失函数
损失函数可看作是一种特殊的layer
3.3 优化器
所有的优化方法都是继承基类optim.Optimizer,并实现自己的优化步骤。
3.4 nn.functional
nn中的大多数layer在functional中都有一个与之对应的函数。nn.functional
中的函数和nn.Module
的主要区别在于,用nn.Module
实现的layers是一个特殊的类,都是由class Layer(nn.Module)
定义,会自动提取可学习的参数,而nn.functional
中的函数更像是纯函数,有def functional(input)
定义。