PyTorch 简介:构建神经网络以识别手写数字

作者选择Code 2040接受捐赠,作为Write for DOnations计划的一部分。

介绍

机器学习是计算机科学的一个领域,它在数据中发现模式。到 2021 年,机器学习从业者使用这些模式来检测自动驾驶汽车的车道训练机械手解决魔方产生可疑的艺术品味的图像随着机器学习模型变得更加准确和高效,我们看到主流应用程序和产品越来越多地被采用。

深度学习是机器学习的一个子集,专注于特别复杂的模型,称为神经网络在稍后的高级 DigitalOcean 文章中(例如构建Atari 机器人的教程),我们将正式定义“复杂”的含义。神经网络是您听说过的高度准确且引人入胜的现代模型,其应用范围涵盖广泛的任务。在本教程中,您将专注于一项称为对象识别或图像分类的特定任务。给定手写数字的图像,您的模型将预测显示的是哪个数字。

您将在PyTorch 中构建、训练和评估深度神经网络,PyTorchFacebook AI Research为深度学习开发的框架与其他深度学习框架(如 Tensorflow)相比,PyTorch 是一个初学者友好的框架,具有有助于构建过程的调试功能。它还可以为高级用户高度定制,研究人员和从业人员可以在 Facebook 和特斯拉等公司使用它。在本教程结束时,您将能够:

  • 在 PyTorch 中构建、训练和评估深度神经网络
  • 了解应用深度学习的风险

虽然您不需要具有实际深度学习或 PyTorch 方面的先前经验来学习本教程,但我们假设您熟悉机器学习术语和概念,例如训练和测试、特征和标签、优化和评估。您可以在机器学习简介中了解有关这些概念的更多信息

先决条件

要完成本教程,您需要一个具有至少 1GB RAM 的 Python 3 本地开发环境。您可以按照如何为 Python 3 安装和设置本地编程环境来配置您需要的一切。

第 1 步 – 创建您的项目并安装依赖项

让我们为此项目创建一个工作区并安装您需要的依赖项。您将调用您的工作区pytorch

  • mkdir ~/pytorch

导航到pytorch目录:

  • cd ~/pytorch

然后为项目创建一个新的虚拟环境:

  • python3 -m venv pytorch

激活您的环境:

  • source pytorch/bin/activate

然后安装PyTorch在 macOS 上,使用以下命令安装 PyTorch:

  • python -m pip install torch==1.4.0 torchvision==0.5.0

在 Linux 和 Windows 上,对仅 CPU 构建使用以下命令:

  • pip install torch==1.4.0+cpu torchvision==0.5.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
  • pip install torchvision

安装依赖项后,您现在将构建您的第一个神经网络。

第 2 步 — 构建“Hello World”神经网络

在这一步中,您将构建您的第一个神经网络并对其进行训练。您将了解 Pytorch 中的两个子库,分别torch.nn用于神经网络操作和torch.optim神经网络优化器。要了解什么是“优化器”,您还将了解一种称为梯度下降的算法在本教程中,您将使用以下五个步骤来构建和训练模型:

  1. 构建计算图
  2. 设置优化器
  3. 设置标准
  4. 设置数据
  5. 训练模型

在本教程的第一部分中,您将使用可管理的数据集构建一个小型模型。首先创建一个新文件step_2_helloworld.py,使用nano或您最喜欢的文本编辑器:

  • nano step_2_helloworld.py

您现在将编写一个简短的 18 行代码片段来训练一个小模型。首先导入几个 PyTorch 实用程序:

step_2_helloworld.py
import torch
import torch.nn as nn
import torch.optim as optim

在这里,您将 PyTorch 库别名为几个常用的快捷方式:

  • torch包含所有 PyTorch 实用程序。但是,常规 PyTorch 代码包括一些额外的导入。我们在这里遵循相同的约定,以便您可以在线了解 PyTorch 教程和随机代码片段。
  • torch.nn包含用于构建神经网络的实用程序。这通常表示nn
  • torch.optim包含培训实用程序。这通常表示optim

接下来,定义神经网络、训练实用程序和数据集:

step_2_helloworld.py
. . .
net = nn.Linear(1, 1)  # 1. Build a computation graph (a line!)
optimizer = optim.SGD(net.parameters(), lr=0.1)  # 2. Setup optimizers
criterion = nn.MSELoss()  # 3. Setup criterion
x, target = torch.randn((1,)), torch.tensor([0.])  # 4. Setup data
. . .

在这里,您定义任何深度学习训练脚本的几个必要部分:

  • net = ...定义了“神经网络”。在这种情况下,模型是形式的一条线y = m * x参数nn.Linear(1, 1)是线的斜率。模型参数 nn.Linear(1, 1)将在训练期间更新。请注意,torch.nn(以 为别名nn)包括许多深度学习操作,例如此处使用的全连接层 ( nn.Linear) 和卷积层 ( nn.Conv2d)。
  • optimizer = ...定义优化器。这个优化器决定了神经网络将如何学习。我们将在编写更多行代码后更详细地讨论优化器。请注意torch.optim(别名为optim)包括许多您可以使用的此类优化器。
  • criterion = ...定义损失。简而言之,损失定义您的模型试图最小化的内容。对于线的基本模型,目标是最小化线的预测 y 值与训练集中实际 y 值之间的差异。请注意,torch.nn(以 为别名nn)包括您可以使用的许多其他损失函数。
  • x, target = ...定义你的“数据集”。现在,数据集只有一个坐标——一个 x 值和一个 y 值。在这种情况下,torch包本身提供tensor, 创建一个新的张量,并randn创建一个具有随机值的张量。

最后,通过对数据集迭代十次来训练模型。每次,您调整模型的参数:

step_2_helloworld.py
. . .
# 5. Train the model
for i in range(10):
    output = net(x)
    loss = criterion(output, target)
    print(round(loss.item(), 2))

    net.zero_grad()
    loss.backward()
    optimizer.step()

您的总体目标是通过调整线的斜率来最小化损失。为了实现这一点,此训练代码实现了一种称为梯度下降的算法梯度下降的直觉如下:想象一下,你正直视一个碗。碗上有很多点,每个点对应不同的参数值。碗本身就是损失面:碗的中心——最低点——表示损失最低的最佳模型。这是最优化的碗的边缘——最高点,以及离你最近的碗的部分——持有损失最高的最差模型。

要找到具有最低损失的最佳模型:

  1. 随着net = nn.Linear(1, 1)你初始化一个随机模型。这相当于在碗上随机选取一个点。
  2. for i in range(10)循环中,您开始训练。这相当于更接近碗的中心。
  3. 每一步的方向由梯度给出。您将在这里跳过正式的证明,但总而言之,负梯度指向碗中的最低点。
  4. 使用lr=0.1inoptimizer = ...指定步长这决定了每个步骤的大小。

只需十步,您就可以到达碗的中心,这是损失最低的最佳模型。有关梯度下降的可视化,请参阅 Distill 的“为什么 Momentum 真正起作用”,页面顶部的第一个图。

这段代码的最后三行也很重要:

  • net.zero_grad 清除上一步迭代中可能残留的所有梯度。
  • loss.backward 计算新的梯度。
  • optimizer.step使用这些梯度来采取步骤。请注意,您没有自己计算梯度。这是因为 PyTorch 和其他类似的深度学习库会自动区分.

现在,您的“hello world”神经网络到此结束。保存并关闭您的文件。

仔细检查您的脚本是否与step_2_helloworld.py. 然后,运行脚本:

  • python step_2_helloworld.py

您的脚本将输出以下内容:

Output
0.33 0.19 0.11 0.07 0.04 0.02 0.01 0.01 0.0 0.0

请注意,您的损失不断减少,表明您的模型正在学习。使用 PyTorch 时,还有两个其他实现细节需要注意:

  1. PyTorch 用于torch.Tensor保存所有数据和参数。在这里,torch.randn使用提供的形状生成一个具有随机值的张量。例如, atorch.randn((1, 2))创建一个 1×2 张量,或一个二维行向量。
  2. PyTorch 支持多种优化器。此功能torch.optim.SGD,也称为随机梯度下降 (SGD)。粗略地说,这就是本教程中描述的算法,您在其中朝着最佳方向迈进了一步。有更多涉及的优化器可以在 SGD 之上添加额外的功能。还有很多损失,torch.nn.MSELoss只是其中之一。

这结束了您在玩具数据集上的第一个模型。在下一步中,您将用神经网络替换这个小模型,用常用的机器学习基准替换玩具数据集。

第 3 步 — 在手写数字上训练您的神经网络

在上一节中,您构建了一个小型 PyTorch 模型。但是,为了更好地理解 PyTorch 的好处,您现在将使用 构建一个深度神经网络torch.nn.functional,其中包含更多神经网络操作,并且torchvision.datasets支持您可以使用的许多数据集,开箱即用。在本节中,您将使用预制数据集构建一个相对复杂的自定义模型

您将使用卷积,它是模式查找器。对于图像,卷积在“意义”的各个层次上寻找 2D 模式:直接应用于图像的卷积寻找“较低层次”的特征,例如边缘。然而,应用于许多其他操作的输出的卷积可能正在寻找“更高级别”的特征,例如门。有关可视化和更彻底的卷积演练,请参阅斯坦福深度学习课程的一部分

您现在将通过定义稍微复杂的模型来扩展您构建的第一个 PyTorch 模型。您的神经网络现在将包含两个卷积和一个全连接层,以处理图像输入。

首先step_3_mnist.py使用文本编辑器创建一个新文件

  • nano step_3_mnist.py

您将遵循与以前相同的五步算法:

  1. 构建计算图
  2. 设置优化器
  3. 设置标准
  4. 设置数据
  5. 训练模型

首先,定义你的深度神经网络。请注意,这是您可能在 MNIST 上找到的其他神经网络的精简版本——这是有意为之,以便您可以在笔记本电脑上训练您的神经网络:

step_3_mnist.py
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR

# 1. Build a computation graph
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc = nn.Linear(1024, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 1)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        output = F.log_softmax(x, dim=1)
        return output
net = Net()
. . .

在这里,您定义了一个神经网络类,它继承自nn.Module. 神经网络中的所有操作(包括神经网络本身)都必须继承自nn.Module. 神经网络类的典型范式如下:

  1. 在构造函数中,定义网络所需的任何操作。在这种情况下,您有两个卷积和一个全连接层。(要记住的提示:构造函数始终以 开头super().__init__()。)PyTorch 期望在将模块(例如,nn.Conv2d分配给实例属性 ( self.conv1)之前初始化父类
  2. forward方法中,运行初始化操作。该方法确定了神经网络架构,明确定义了神经网络将如何计算其预测。

这个神经网络使用了几种不同的操作:

  • nn.Conv2d: 卷积。卷积在图像中寻找模式。早期的卷积寻找像边缘这样的“低级”模式。网络中的后续卷积寻找“高级”模式,例如狗的腿或耳朵。
  • nn.Linear:全连接层。全连接层将所有输入特征与所有输出维度相关联。
  • F.relu, F.max_pool2d: 这些是非线性的类型。(非线性是任何不是线性relu的函数f(x) = max(x, 0)是函数max_pool在每个值块中取最大值。在这种情况下,您在整个图像中取最大值。
  • log_softmax:对向量中的所有值进行归一化,使值总和为 1。

其次,像以前一样,定义优化器。这一次,您将使用不同的优化器和不同的超参数设置。超参数配置训练,而训练调整模型参数。这些超参数设置取自PyTorch MNIST 示例

step_3_mnist.py
. . .
optimizer = optim.Adadelta(net.parameters(), lr=1.)  # 2. Setup optimizer
. . .

第三,与以前不同,您现在将使用不同的损失。此损失用于分类问题,其中模型的输出是类索引。在这个特定的例子中,模型将输出输入图像中包含的数字(可能是 0 到 9 之间的任何数字):

step_3_mnist.py
. . .
criterion = nn.NLLLoss()  # 3. Setup criterion
. . .

第四,设置数据。在这种情况下,您将设置一个名为MNIST的数据集,其中包含手写数字。Deep Learning 101 教程经常使用这个数据集。每个图像都是一个包含手写数字的 28×28 像素小图像,目标是将每个手写数字分类为 0、1、2、… 或 9:

step_3_mnist.py
. . .
# 4. Setup data
transform = transforms.Compose([
    transforms.Resize((8, 8)),
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST(
    'data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=512)
. . .

在这里,您transform = ...通过调整图像大小、将图像转换为 PyTorch 张量并将张量归一化为均值 0 和方差 1 来预处理图像

在接下来的两行中,您设置train=True,因为这是训练数据集,download=True因此您可以下载数据集(如果尚未下载)。

batch_size=512确定一次训练网络的图像数量。除非大批量(例如,数以万计),否则较大的批次对于大致更快的训练更可取。

第五,训练模型。在以下代码块中,您进行了最少的修改。您现在将对提供的数据集中的所有样本迭代一次,而不是在同一样本上运行十次。通过一次传递所有样本,以下是一个epoch 的训练

step_3_mnist.py
. . .
# 5. Train the model
for inputs, target in train_loader:
    output = net(inputs)
    loss = criterion(output, target)
    print(round(loss.item(), 2))

    net.zero_grad()
    loss.backward()
    optimizer.step()
. . .

保存并关闭您的文件。

仔细检查您的脚本是否与step_3_mnist.py. 然后,运行脚本。

  • python step_3_mnist.py

您的脚本将输出以下内容:

Output
2.31 2.18 2.03 1.78 1.52 1.35 1.3 1.35 1.07 1.0 ... 0.21 0.2 0.23 0.12 0.12 0.12

请注意,最终损失小于初始损失值的 10%。这意味着您的神经网络正在正确训练。

培训到此结束。然而,0.12 的损失很难推理:我们不知道 0.12 是“好”还是“坏”。要评估您的模型的执行情况,您接下来要计算此分类模型的准确度。

第 4 步 – 评估您的神经网络

早些时候,您在数据集训练分割上计算了损失值但是,保持数据集的单独验证拆分是一种很好的做法您可以使用此验证拆分来计算模型的准确性。但是,您不能将其用于训练。接下来,您设置验证数据集并在其上评估您的模型。在此步骤中,您将使用与之前相同的 PyTorch 实用程序,包括torchvision.datasets用于 MNIST 数据集。

首先将您的step_3_mnist.py文件复制step_4_eval.py. 然后,打开文件:

  • cp step_3_mnist.py step_4_eval.py
  • nano step_4_eval.py

首先,设置验证数据集:

step_4_eval.py
. . .
train_loader = ...
val_dataset = datasets.MNIST(
    'data', train=False, download=True, transform=transform)
val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=512)
. . .

在文件的末尾,在训练循环之后,添加一个验证循环:

step_4_eval.py
    . . .
    optimizer.step()

correct = 0.
net.eval()
for inputs, target in val_loader:
    output = net(inputs)
    _, pred = output.max(1)
    correct += (pred == target).sum()
accuracy = correct / len(val_dataset) * 100.
print(f'{accuracy:.2f}% correct')

在这里,验证循环执行一些操作来计算准确度:

  • 运行net.eval()可确保您的神经网络处于评估模式并准备好进行验证。评估模式下的几种操作与训练模式下的运行方式不同。
  • 迭代中的所有输入和标签val_loader
  • 运行模型net(inputs)以获得每个类的概率。
  • 找到概率最高的类别output.max(1)output是具有尺寸的张量(n, k)n样品和k类。1意味着您沿索引1维度计算最大值
  • 计算正确分类的图像数量:pred == target计算布尔值向量。.sum()将这些布尔值转换为整数并有效地计算真值的数量。
  • correct / len(val_dataset) 最后计算正确分类的图像百分比。

保存并关闭您的文件。

仔细检查您的脚本是否与step_4_eval.py. 然后,运行脚本:

  • python step_4_eval.py

您的脚本将输出以下内容。请注意,具体的损失值和最终精度可能会有所不同:

Output
2.31 2.21 ... 0.14 0.2 89% correct

您现在已经训练了您的第一个深度神经网络。您可以通过调整用于训练的超参数进行进一步的修改和改进:这包括不同的时期数、学习率和不同的优化器。我们包含一个带有调整过的超参数的示例脚本;这个脚本训练了相同的神经网络,但训练了 10 个 epoch,获得了 97% 的准确率。

深度学习的风险

一个问题是深度学习并不总能获得最先进的结果。深度学习在特征丰富、数据丰富的场景中表现良好,但在数据稀疏、特征稀疏的情况下表现不佳。尽管在深度学习的薄弱领域进行了积极的研究,但许多其他机器学习技术已经非常适合特征稀疏机制,例如决策树、线性回归或支持向量机 (SVM)。

另一个问题是深度学习还没有被很好地理解。无法保证准确性、最优性甚至收敛性。另一方面,经典的机器学习技术经过充分研究并且相对可解释。同样,有积极的研究来解决深度学习中缺乏可解释性的问题。您可以在“可解释 AI 无法解释的内容(以及我们如何解决该问题)”中阅读更多内容

最重要的是,深度学习缺乏可解释性会导致被忽视的偏见。例如,加州大学伯克利分校的研究人员能够在字幕中显示模型的性别偏见(“Women also Snowboard”)。其他研究工作侧重于社会问题,例如机器学习中的“公平性”鉴于这些问题正在进行积极的研究,很难对模型中的偏差推荐一个规定的诊断。因此,作为从业者,您应该负责任地应用深度学习。

结论

PyTorch 是面向爱好者和研究人员的深度学习框架。为了熟悉 PyTorch,您既训练了一个深度神经网络,还学习了一些自定义深度学习的技巧和窍门。

您还可以使用预先构建的神经网络架构,而不是构建自己的架构。这是一个可选部分的链接:您可以尝试使用 Google Colab 上的现有神经网络架构出于演示目的,此可选步骤使用更大的图像训练更大的模型。

查看我们的其他文章,深入了解机器学习和相关领域:

  • 你的模型足够复杂吗?太复杂?深度强化学习的偏差-方差中了解偏差-方差权衡:如何使用 OpenAI Gym 为 Atari 构建机器人以找出答案在本文中,我们为 Atari Games 构建 AI 机器人,并探索称为强化学习的研究领域。或者,在这篇理解偏差-方差权衡文章中找到偏差-方差权衡的直观解释
  • 机器学习模型如何处理图像?构建基于情感的狗过滤器中了解更多信息在本文中,我们将更详细地讨论模型如何处理和分类图像,探索称为计算机视觉的研究领域。
  • 神经网络会被愚弄吗?了解如何欺骗神经网络在本文中,我们探索对抗性机器学习,这是一个研究领域,为神经网络设计攻击和防御,以实现更强大的现实世界深度学习部署。
  • 我们如何才能更好地理解神经网络的工作原理?How To Visualize and Interpret Neural Networks 中阅读一类称为“可解释 AI”的方法在本文中,我们探索可解释的 AI,特别是可视化神经网络认为对其预测很重要的像素。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁