PyTorch 的简单实现
1.必要的 PyTorch 背景
PyTorch 是一个建立在 Torch 库之上的 Python 包,旨在加速深度学习应用。
PyTorch 提供一种类似 NumPy 的抽象方法来表征张量(或多维数组),它可以利用 GPU 来加速训练。
1.1 PyTorch 张量
PyTorch 的关键数据结构是张量,即多维数组。其功能与 NumPy 的 ndarray 对象类似,如下我们可以使用 torch.Tensor() 创建张量。如果你需要一个兼容 NumPy 的表征,或者你想从现有的 NumPy 对象中创建一个 PyTorch 张量,那么就很简单了。
1.2 PyTorch vs. NumPy
PyTorch 并不是 NumPy 的简单替代品,但它实现了很多 NumPy 功能。其中有一个不便之处是其命名规则,有时候它和 NumPy 的命名方法相当不同。我们来举几个例子说明其中的区别:
1.3 PyTorch 变量
PyTorch 张量的简单封装
帮助建立计算图
Autograd(自动微分库)的必要部分
将关于这些变量的梯度保存在 .grad 中
结构图:
计算图和变量:在 PyTorch 中,神经网络会使用相互连接的变量作为计算图来表示。PyTorch 允许通过代码构建计算图来构建网络模型;之后 PyTorch 会简化估计模型权重的流程,例如通过自动计算梯度的方式。
举例来说,假设我们想构建两层模型,那么首先要为输入和输出创建张量变量:
输出为:
1.4 PyTorch 反向传播
这样我们有了输入和目标、模型权重,那么是时候训练模型了。我们需要三个组件:
损失函数:描述我们模型的预测距离目标还有多远;
优化算法:用于更新权重;
反向传播步骤:
1.5 PyTorch CUDA 接口
PyTorch 的优势之一是为张量和 autograd 库提供 CUDA 接口。使用 CUDA GPU,你不仅可以加速神经网络训练和推断,还可以加速任何映射至 PyTorch 张量的工作负载。
你可以调用 torch.cuda.is_available() 函数,检查 PyTorch 中是否有可用 CUDA。
如果有 GPU,.cuda()之后,使用 cuda 加速代码就和调用一样简单。如果你在张量上调用 .cuda(),则它将执行从 CPU 到 CUDA GPU 的数据迁移。如果你在模型上调用 .cuda(),则它不仅将所有内部储存移到 GPU,还将整个计算图映射至 GPU。
要想将张量或模型复制回 CPU,比如想和 NumPy 交互,你可以调用 .cpu()。
我们来定义两个函数(训练函数和测试函数)来使用我们的模型执行训练和推断任务。该代码同样来自 PyTorch 官方教程,我们摘选了所有训练/推断的必要步骤。
对于训练和测试网络,我们需要执行一系列动作,这些动作可直接映射至 PyTorch 代码:
我们将模型转换到训练/推断模式;
我们通过在数据集上成批获取图像,以迭代训练模型;
对于每一个批量的图像,我们都要加载数据和标注,运行网络的前向步骤来获取模型输出;
我们定义损失函数,计算每一个批量的模型输出和目标之间的损失;
训练时,我们初始化梯度为零,使用上一步定义的优化器和反向传播,来计算所有与损失有关的层级梯度;
训练时,我们执行权重更新步骤。
2. 使用 PyTorch 进行数据分析
使用 torch.nn 库构建模型
使用 torch.autograd 库训练模型
将数据封装进 torch.utils.data.Dataset 库
使用 NumPy interface 连接你的模型、数据和你最喜欢的工具
在查看复杂模型之前,我们先来看个简单的:简单合成数据集上的线性回归,我们可以使用 sklearn 工具生成这样的合成数据集。
PyTorch 的 nn 库中有大量有用的模块,其中一个就是线性模块。如名字所示,它对输入执行线性变换,即线性回归。
为了转向更复杂的模型,我们下载了 MNIST 数据集至「datasets」文件夹中,并测试一些 PyTorch 中可用的初始预处理。PyTorch 具备数据加载器和处理器,可用于不同的数据集。数据集下载好后,你可以随时使用。你还可以将数据包装进 PyTorch 张量,创建自己的数据加载器类别。
批大小(batch size)是机器学习中的术语,指一次迭代中使用的训练样本数量。批大小可以是以下三种之一:
batch 模式:批大小等于整个数据集,因此迭代和 epoch 值一致;
mini-batch 模式:批大小大于 1 但小于整个数据集的大小。通常,数量可以是能被整个数据集整除的值。
随机模式:批大小等于 1。因此梯度和神经网络参数在每个样本之后都要更新。
3. PyTorch 中的 LeNet 卷积神经网络(CNN)
现在我们从头开始创建第一个简单神经网络。该网络要执行图像分类,识别 MNIST 数据集中的手写数字。这是一个四层的卷积神经网络(CNN),一种分析 MNIST 数据集的常见架构。该代码来自 PyTorch 官方教程,你可以在这里(http://pytorch.org/tutorials/)找到更多示例。
我们将使用 torch.nn 库中的多个模块:
线性层:使用层的权重对输入张量执行线性变换;
Conv1 和 Conv2:卷积层,每个层输出在卷积核(小尺寸的权重张量)和同样尺寸输入区域之间的点积;
Relu:修正线性单元函数,使用逐元素的激活函数 max(0,x);
池化层:使用 max 运算执行特定区域的下采样(通常 2x2 像素);
Dropout2D:随机将输入张量的所有通道设为零。当特征图具备强相关时,dropout2D 提升特征图之间的独立性;
Softmax:将 Log(Softmax(x)) 函数应用到 n 维输入张量,以使输出在 0 到 1 之间。
输出:
MNIST_net model:
LeNet((conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))(conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))(conv2_drop): Dropout2d(p=0.5)(fc1): Linear(in_features=320, out_features=50, bias=True)(fc2): Linear(in_features=50, out_features=10, bias=True))
若无预训练模型,输出:
Training model
Test set: Average loss: 0.1348, Accuracy: 9578/10000 (0%)Test set: Average loss: 0.0917, Accuracy: 9703/10000 (0%)Test set: Average loss: 0.0746, Accuracy: 9753/10000 (0%)Test set: Average loss: 0.0659, Accuracy: 9795/10000 (0%)Test set: Average loss: 0.0553, Accuracy: 9828/10000 (0%)
现在我们来看下模型。首先,打印出该模型的信息。打印函数显示所有层(如 Dropout 被实现为一个单独的层)及其名称和参数。同样有一个迭代器在模型中所有已命名模块之间运行。当你具备一个包含多个「内部」模型的复杂 DNN 时,这有所帮助。在所有已命名模块之间的迭代允许我们创建模型解析器,可读取模型参数、创建与该网络类似的模块。
输出:
Internal models:0 -> ('', LeNet((conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))(conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))(conv2_drop): Dropout2d(p=0.5)(fc1): Linear(in_features=320, out_features=50, bias=True)(fc2): Linear(in_features=50, out_features=10, bias=True)))-------------------------------------------------------------------------1 -> ('conv1', Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1)))-------------------------------------------------------------------------2 -> ('conv2', Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1)))-------------------------------------------------------------------------3 -> ('conv2_drop', Dropout2d(p=0.5))-------------------------------------------------------------------------4 -> ('fc1', Linear(in_features=320, out_features=50, bias=True))-------------------------------------------------------------------------5 -> ('fc2', Linear(in_features=50, out_features=10, bias=True))-------------------------------------------------------------------------
你可以使用 .cpu() 方法将张量移至 CPU(或确保它在那里)。或者,当 GPU 可用时(torch.cuda. 可用),使用 .cuda() 方法将张量移至 GPU。你可以看到张量是否在 GPU 上,其类型为 torch.cuda.FloatTensor。如果张量在 CPU 上,则其类型为 torch.FloatTensor。
输出:
<class 'torch.Tensor'>Cuda is available<class 'torch.Tensor'>
如果张量在 CPU 上,我们可以将其转换成 NumPy 数组,其共享同样的内存位置,改变其中一个就会改变另一个。
现在我们了解了如何将张量转换成 NumPy 数组,我们可以利用该知识使用 matplotlib 进行可视化!我们来打印出个卷积层的卷积滤波器。
学习更多编程知识,请关注我的公众号:
评论