PyTorch 学习笔记
PyTorch 学习笔记
前言
PyTorch 是一个基于 python 的科学计算包,主要针对两类人群:
- 作为 NumPy 的替代品,可以利用 GPU 的性能进行计算
- 作为一个高灵活性、速度快的深度学习平台
安装 pytorch
你可以登录 官方网站 选择你想要的安装方式,然后 根据对应的安装命令安装即可。
有几点需要注意:
- PyTorch 对应的 Python 包名为
torch
而非pytorch
- 若需使用 GPU 版本的 PyTorch, 需要先配置英伟达显卡驱动,再安装 PyTorch
PS: 为了方便最好是将 conda 和 pip 的软件源修改成内地源,这样的话,使用 conda 或者 pip 安装软件速度会快很多,你可以点击 这里 了解如何对 conda 和 pip 进行换源。
PPS: 如果只是为了学习,没有 N 卡,或者说显卡的显存较小,可以只安装 CPU 版本的 torch,对于数据集较小、规模不大的神经网络还是 ok 的。当然如果你想要 GPU
加速,安装 GPU
版本,那么就需要安装 cuda
和 cudnn
,你可以点击 这里 了解更加具体的操作。
安装完成之后,就可以试着导入 torch,查看版本信息:
1 | import torch |
张量(tensor)
tensor 中文意为张量,你可以将其理解为多维矩阵,类似 numpy 中的 ndarray,区别就是 tensor 可以在 GPU 上运行。
不必过多纠结于张量本身,只要理解就好。
创建张量
- 使用现有数据创建张量
可以使用 torch.tensor()
构造函数从 list 或序列直接构造张量,你只需要把列表直接放进去即可:
1 | 1,2,3],[4,5,6]]) a = torch.tensor([[ |
- 创建具有特定大小的张量
有许多构造张量的方法,如 zeros()
,full()
,ones()
,rand()
,arange()
,eye()
等,这里就不详细介绍了,使用方法与 numpy 类似,方法名也是类似的。
1 | 2, 3) # 构造一个2×3大小全为0的矩阵 b = torch.zeros( |
- 通过已有的张量来生成新的张量
要创建与另一个张量具有相同大小(和相似类型)的张量,请使用 torch.*_like
张量创建操作。
1 | d = torch.ones_like(a) |
在上面的例子中,我们根据张量 a
创建了张量 d
,并保留了 a
的属性。
当然你也可以重新指定类型,就像这样:
1 | float) d_float = torch.ones_like(a, dtype=torch. |
类似的,要创建与其他张量具有相似类型但大小不同的张量,使用 tensor.new_*
创建操作。
张量属性
从张量属性我们可以得到张量的维数、数据类型以及它们所存储的设备(CPU 或 GPU)。
1 | 2,3) x = torch.rand( |
张量运算
张量可以进行转置、索引、切片、数学运算等操作,使用方法也是与 numpy 类似
特别需要注意的是,自动赋值运算通常在方法后有 _
作为后缀, 例如: x.copy_(y)
, x.t_()
操作会改变 x
的取值。
1 | # 查看第一列元素 |
与 numpy 的桥接
我们可以很简单的实现一个 Torch 张量和一个 NumPy 数组之间的相互转化。
需要注意的是,Torch 张量和 NumPy 数组将共享它们的底层内存位置,因此当一个改变时,另外一个也会改变。
由张量变换为 Numpy array 数组
1 | 5) t = torch.ones( |
由 Numpy array 数组转为张量
1 | 5) n = np.ones( |
自动求导(autograd)
torch.autograd
是 PyTorch 的自动差分引擎,可为神经网络训练提供支持。
具体来说,我们可以在张量创建时,通过设置 requires_grad
标识为 Ture
,那么 autograd 将会追踪对于该张量的所有操作,当完成计算后可以通过调用 backward()
,来自动计算所有的梯度,并将其存储在各个张量的 .grad
属性中
可能有点不明所以,还是通过具体的例子来说明
简单实现
我们从一个最简单的例子开始:求 在 处的导数:
1 | 3., requires_grad=True) x = torch.tensor( |
简单计算一下就可以发现,我们得到的结果是正确的。
类似的,求 在 和 处的偏导:
1 | 3., requires_grad=True) x = torch.tensor( |
在上面的例子中,我们使用的都是标量,过程也很简单。
在深度学习中,我们更多的是考虑标量对向量/矩阵求导,因为损失函数一般都是一个标量,参数又往往是向量或者是矩阵。
还是来看一个例子,对前面的例子进行简单修改,增加 x 和 y 的维度,一样的方法,我们来看一看得到什么。
1 | 1., 2., 3.], requires_grad=True) x = torch.tensor([ |
你会发现,光荣的报错了,翻译一下,也就是说只有标量才能对其他东西求导。
1 | 1., 2., 3.], requires_grad=True) x = torch.tensor([ |
在上面的例子中,我们对 z
求了平均,让 L
成为一个标量,之后便可以进行同样的操作。
一些注意点:
- 要想使 x 支持求导,必须让
x
为浮点类型,定义时应该是[1., 2., 3.]
而不是[1, 2, 3]
。 - 在求导时,只能是标量对标量,或者标量对向量/矩阵求导。
一些其它问题
在官方文档中,有这么一段代码,它使用了 backward
中的 gradient
参数,刚开始没搞懂这是什么意思,为什么前面明明报错了,加进去一个参数又好了?
1 | a = torch.tensor([2., 3.], requires_grad=True) |
就像上面说的,损失函数一般都是一个标量,我们直接通过 loss.backward()
即可。
但是,有时候我们可能会有多个输出值,比如 loss=[loss1,loss2,loss3]
,那么我们可以让 loss
的各个分量分别对 x
求导
1 | loss.backward(torch.tensor([[1.0,1.0,1.0,1.0]])) |
也可以实现权重的调整,只需要调整赋值即可:
1 | loss.backward(torch.tensor([[0.1,1.0,10.0,0.001]])) |
神经网络
torcn.nn 是专门为神经网络设计的模块化接口。构建于 autograd 之上,可以用来定义和运行神经网络。
下面假设你已经基本具备了神经网络的基础知识,你也可以点击 这里了解关系神经网络的基础知识。
自定义一个神经网络
torch.nn.Module
是所有神经网络模块的基类,我们可以通过继承它来编写我们自己的网络,只要继承 nn.Module
,并实现它的 forward
方法,PyTorch 会根据 autograd,自动实现 backward
函数。
- 自定义一个类,该类继承自
nn.Module
类 - 在构造函数中要调用
nn.Module
的构造函数,super(Net, self).__init__()
- 在构造函数
__init__()
中添加具有可学习参数的层 - 在
forward
中实现层之间的连接关系,也就是实现前向传播(forward
方法是必须要重写的)
下面是一个简单的网络示例:
1 | import torch |
在上面的示例中,我们建立了一个简单的网络,首先让我们关注一下卷积层和全连接层是如何建立的
可学习参数的层
创建卷积层很简单,你只需要指定,输入通道数,输出通道数,卷积核大小
1 | conv1 = nn.Conv2d(1, 6, kernel_size=3) |
你也可以根据需要指定 stride
,padding
。
class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
全连接层也是类似的,指定输入通道数,输出通道数
1 | fc1 = nn.Linear(6 * 6 * 16, 120) |
class torch.nn.Linear(in_features, out_features, bias=True)
重写 forward
在 forward
中,你需要实现整个正向传播的过程,也就是把上面建立的网络连接起来
只需要定义 forward
函数,就可以使用 autograd 为您自动定义 backward
函数(计算梯度)
损失函数
损失函数用于计算模型的预测值与实际值之间的误差,PyTorch 同样预置了许多损失函数,https://pytorch.org/docs/stable/nn.html#loss-functions。
一个简单的例子是 nn.MSELoss
,它会计算预测值和真实值的均方误差。
1 | # 使用我们上面建立的网络 |
优化器
在反向传播计算完所有参数的梯度后,还需要使用优化方法来更新网络的权重和参数
在 torch.optim
中实现大多数的优化方法,例如 RMSProp、Adam、SGD 等。
1 | output = net(input) |
这样,神经网络的数据的一个完整的传播就已经通过 PyTorch 实现了