如何用几行干净的代码训练一个神经网络

214 阅读8分钟

可重复使用、可维护和易于理解的机器学习代码

图片来源:Milad FakurianonUnsplash- 由作者编辑

较少的代码通常会产生可读的代码,易于理解和易于维护。在机器学习界已经非常流行的Python编程语言,与其他编程语言相比,可以用较少的代码达到很好的效果。

PyTorch是一个流行的Python深度学习框架,它有一个干净的API,让你写的代码真的感觉像Python。由于这个原因,用PyTorch在Python中创建模型和进行机器学习实验真的很有趣。

在这篇文章中,我将向你展示训练一个识别手写数字的简单分类器所需的基本步骤。你将看到如何

  • 用PyTorch的数据加载器加载MNIST数据集(机器学习的 "你好世界 "数据集)。
  • 声明我们模型的结构
  • 选择一个优化器
  • 实现训练循环
  • 确定训练后的模型的准确性

我想让一切都尽可能的简单。因此,我不涉及诸如过拟合、数据预处理或不同的指标来评估我们分类器的性能。我们将只实现训练分类器所需的基本构件,这些构件可以很容易地在其他机器学习实验中重复使用。

因此,让我们开始编写一些代码。

我们需要做的第一件事是导入必要的包。由于我们使用的是PyTorch,我们需要导入Torch和Torchvision这两个包。

import torch

加载数据

现在,我们可以通过torchvision加载我们的训练和验证数据集。

t = tv.transforms.ToTensor()
mnist_training = tv.datasets.MNIST(
mnist_val = tv.datasets.MNIST(

首先,我们创建一个ToTensor()的实例,用来将我们从数据集包中得到的图像转换成张量。我们需要这个步骤,因为所有的PyTorch函数都是在张量上操作的。如果你不知道张量,它们基本上只是多维数组的一个华丽的名字。一个张量有一个等级。例如,等级为0的张量是一个标量,等级为1的张量是一个矢量,等级为2的张量是一个矩阵,依此类推。

然后,我们加载我们的训练和验证数据集。通过root,我们可以指定用于在磁盘上存储数据集的目录。如果我们把train设置为true,就会加载训练集。否则就加载验证数据集。如果我们将download设置为true,PyTorch将下载数据集并将其存储到通过root指定的目录中。最后,我们可以指定应用于训练和验证数据集的每个例子的转换。在我们的例子中,它只是ToTensor()。

指定我们模型的架构

接下来,我们指定我们模型的架构。

model = torch.nn.Sequential(

选择一个优化器和损失函数

接下来,我们指定优化器和损失函数。

我们使用的是Adam优化器。通过第一个参数,我们指定优化器需要优化的模型的参数。通过第二个参数lr,我们指定学习率。

在第二行,我们选择CrossEntropyLoss作为损失函数(损失函数的另一个常用词是准则)。这个函数采用我们输出层的非标准化(N x 10)维输出(N是我们批次的样本数),并计算网络的输出和目标标签之间的损失。目标标签被表示为一个N维向量(或者更具体地说,是一个等级为1的张量),它包含了输入样本的类别索引。正如你所看到的,CrossEntropyLoss是一个非常方便的函数。首先,我们不需要在网络的末尾设置正常化层,如softmax。其次,我们不需要在不同的标签表示法之间进行转换。我们的网络输出一个10维的分数向量,目标标签被提供为一个类指数向量(0到9之间的整数)。

接下来我们为训练数据集创建一个数据加载器。

loader = torch.utils.data.DataLoader(

数据加载器是用来从数据集中检索样本的。我们可以使用数据加载器来轻松地迭代成批的样本。这里,我们创建了一个加载器,在每次迭代中从训练数据集中返回500个样本。如果我们将shuffle设置为 "true",样本将在该批次中被洗牌。

训练一个机器学习模型

现在,我们有了训练模型所需的一切。

for epoch in range(10):

我们使用10个epochs来训练我们的网络(第1行)。在每个历时中,我们对加载器进行迭代,在每次迭代中得到500张带有标签的图像(第2行)。变量imgs是一个形状的张量(500,1,28,28)。变量labels是一个等级为1的张量,有500个类索引。

在第3行,我们将当前批次的图像数量保存在变量n中。在第4行,我们将imgs张量从形状(n,1,28,28)重塑为形状(n,784)的张量。在第5行,我们使用我们的模型来预测我们当前批次的所有图像的标签。然后,在第6行,我们计算这些预测和地面真相之间的损失。张量预测是一个形状为(n, 10)的张量,标签是一个等级为1的张量,包含类的索引。在第7行到第9行,我们为网络的所有参数重置梯度,我们计算梯度并更新模型的参数。

我们还打印了每个历时后的损失,这样我们就可以验证网络在每个历时后变得更好(即损失减少)。

确定准确度

现在,我们已经训练了网络,我们可以确定该模型识别手写数字的准确性。

首先,我们需要从我们的验证数据集中获得数据。

n = 10000

我们的验证数据集mnist_val包含10000张图片。为了获得所有这些图像,我们使用一个DataLoader,并将batch_size设置为10000。然后,我们可以通过从数据加载器中创建一个迭代器,并在该迭代器上调用next()来获取第一个元素来获得数据。

其结果是一个元组。这个元组的第一个元素是一个形状为(10000,1,28,28)的张量。第二个元素是一个等级为1的张量,包含了图像的类指数。

现在,我们可以使用我们的模型来预测所有图像的标签。

predictions = model(images.view(n, -1))

在我们提供数据给我们的模型之前,我们需要重新塑造它(类似于我们在训练循环中已经做的)。我们模型的输入张量需要有(n,784)的形状。当数据具有正确的形状时,我们就可以把它作为我们模型的输入了。

其结果是一个形状为(10000,10)的张量。对于我们验证集的每张图片,这个张量存储了十个可能的标签中每一个的分数。一个标签的分数越高,该样本就越有可能属于相应的标签。

通常情况下,一个样本被分配到得分最高的标签上。我们可以通过张量的argmax()方法轻松地确定该标签,因为该方法会返回最大值的位置。

predicted_labels = predictions.argmax(dim=1)

我们对维度1的最大值感兴趣,因为每个样本的分数都沿着这个维度存储。结果是一个等级为1的张量,现在存储的是预测的类指数而不是分数。

现在,我们可以将预测的类指数与地面真实标签进行比较,以计算验证数据集的准确性。准确率被定义为预测正确的样本的百分比,即正确预测的样本数除以样本总数的百分比。

得到这个指标的诀窍是对预测的标签和地面真实标签进行元素比较。

torch.sum(predicted_labels == labels) / n

我们将predicted_labels与标签进行平等比较,得到一个布尔运算的向量。如果同一位置的两个元素相等,这个向量中的一个元素就是真。否则,一个元素为假。然后,我们用sum来计算是真的元素的数量,并将这个数字除以n。

如果我们执行所有这些步骤,我们应该达到大约97%的精确度。

完整的代码也可以在GitHub上找到。

medium.com/media/c9e2f…

结论

作为一名软件工程师,我喜欢干净的代码。有时候,我坐在某一小段代码前半小时甚至更长时间,只是为了让它更优雅、更漂亮。

PyTorch是一个伟大的深度学习框架,它可以让你写出干净的代码,易于理解,而且感觉像Python。我们已经看到,只用几行代码就可以产生富有表现力的代码来创建和训练最先进的机器学习模型。

我们在这个简单的演示中使用的构件可以在许多机器学习项目中重复使用,我希望它们在你的机器学习之旅中也会对你有所帮助。


用几行干净的代码训练神经网络》最初发表在《走向数据科学》杂志上,人们在那里通过强调和回应这个故事来继续对话。