本文通过详细的代码,从如何安装PyTorch入手,带领读者一步步熟悉PyTorch和Jupyter笔记本。最后用PyTorch实现了线性回归、logistic回归和图像分类,非常适合0基础的初学者。

今天给大家带来一个非常详细的PyTorch教程。本文分为三个部分:

安装PyTorch和Jupyter笔记本

用PyTorch实现线性回归

基于逻辑回归的图像分类

文章很长,秉承用代码搞定一切的原则,包含了大量代码。建议收藏起来,分享给喜欢的人。同时,如果您有任何问题,请给我们留言。

温馨提示:为了演示方便,代码和返回的结果以截图的形式给出。这个系列的所有代码都是以Jupyter Notebook的形式提供的,它托管在Jovian中。托管链接:

https://jvn.io/aakashns/e5cfe043873f4f3c9287507016747ae5

安装PyTorch和Jupyter笔记本

我们将使用Anaconda 的Python发行版来安装库和管理虚拟环境。对于交互式编码和实验,我们将使用Jupyter笔记本。

首先根据官方教程安装Anaconda,然后安装jovian:

$ pip安装jovian -升级

下载本教程的笔记本:

$木星克隆人e5cfe 043873 F4 F3 c 9287507016747 AE 5

此时会创建一个01-pytorch-basics的目录,包括01-pytorch-basics.ipynb和environment.yml文件。

$ CD 01-py torch-基础$ conda环境更新

目的是在不破坏本地Python环境的情况下使用虚拟环境。激活下一个。

$ conda激活01-py torch-基础

开始Jupyter

$ jupyter笔记本

打开浏览器,输入http://localhost:8888。然后点击01-pytorch-basics.ipynb开始。

本质上,PyTorch是一个处理张量的库。所以让让我们先简要了解一下张量类型:

因为很多时候我们需要在NumPy数组和PyTorch数组之间进行转换。

提交并上传:

上传的项目也可以用jovian clone下载回来。操作类似git,所以我赢了这里就不重复了。

简单开场后,我们直接进入硬核阶段。

用PyTorch实现线性回归

至于线性回归,相信大家都不陌生。各种机器学习书籍首先要讲的一定是线性回归。

在这一部分,我们先通过最原始的手工操作,了解整个线性回归的原理和操作过程。

然后,我们将介绍PyTorch 的内置函数以自动化的方式实现线性回归。

在线性回归模型中,每个目标变量被估计为输入变量的加权和与偏差。

先看手表:

表格的第一栏是地区,第二栏是日华氏度,第二栏是降水单位毫米,第四栏是湿度,第五栏是苹果产量,第六栏是橘子产量。

以下代码的目的是估计苹果和桔子在不同地区和环境中的产量。

yield_apple=w11 *温度w12 *降雨量w13 *湿度b1yield_orange=w21 *温度w22 *降雨量w23 *湿度b2

将不同的权重(w11、w12、w13)分别加到温度temp、降水量和湿度上,最后加上偏差b1或b2。

通过使用一种称为梯度下降的优化技术,对权重进行几次调整,以获得更准确的预测结果。

培训用数据

在Jupyter笔记本中导入NumPy和PyTorch

将我们的数据训练为用两个矩阵表示,输入和目标,每个观察值占一行,每个变量占一列。

转换为PyTorch的张量接下来:

和变量偏差也用矩阵表示,从随机值开始。

该模型可以表示为

我们这样定义模型:

生成预测

对比原始数据。

发现差距很大,因为一开始我们用的是随机值,所以数据不匹配很正常。

接下来需要通过损失函数来评估我们的模型与实际情况的差距。分三步走。

计算两个矩阵(预测值和目标值)之间的差异

对矩阵的所有元素求平方,以消除负值。

计算结果矩阵中元素的平均值。

最终结果是MSE。

计算梯度:

使用PyTorch自动计算损失的梯度或导数w.r.t的权重和偏差,因为requires_grad设置为True。

计算权重梯度:

重置渐变:

使用梯度下降来调整重量和偏差。

我们将使用梯度下降优化算法来减少损失并改进我们的模型,其步骤如下:

生成预测

计算损失

计算梯度的重量和偏差

通过减去与梯度成比例的少量来调整权重。

将渐变重置为零

让逐步实现以上步骤。

接下来,它们由代码表示:

最终效果:

让让我们看看损失:

它显示了进步。然后我们再重复上面的过程,把损失降到最低。每一个字都被重复,我们成为一个时代。让让我们从100个纪元开始:

再看效果:

损失很低。打印结果:

用PyTorch内置函数实现线性回归

知道了上面的原理,我们就可以利用PyTorch的内置函数来简化我们的工作量。

接下来,我们创建一个TensorDataset和一个数据加载器:

TensorDataset允许我们使用数组索引符号(上面代码中的[0: 3])来访问一小部分训练数据。它返回一个元组(或元组对),其中第一个元素包含所选行的输入变量,第二个元素包含目标。

只需使用for-in循环。

神经网络线性自动初始化

起初,我们手动随机输入初始权重。现在我们可以使用nn.linear来自动完成初始化。

对于我们的线性回归模型,我们有一个权重矩阵和一个偏差矩阵。

接下来我们重复上面的过程,先通过损失函数计算缺口,然后不断减少损失。

并使用内置损失函数mse_loss:

优化时,我们可以使用优化器optim。SGD,而无需手动操作模型的权重和偏差。

SGD代表随机梯度下降。之所以称之为随机,是因为样本是成批选取的(通常是随机抽样),而不是一组。

训练模型,思路上面已经提到了,直接看代码。

以上是一些需要注意的事项:

我们使用前面定义的数据加载器来获取每次迭代的批处理数据。

我们使用opt.step执行更新,并使用opt.zero_grad将梯度重置为零,而不是手动更新参数(权重和偏差)。

我们还添加了日志语句,打印每10个周期最后一批数据的丢失情况,以跟踪训练的进度。Loss.item返回存储在损失张量中的实际值。

请给我100元的

结果:

比较:

提交并上传

现在,您可以将代码上传到我们的笔记本。看了这么多代码,我想你可能已经忘了怎么提交了。

基于PyTorch逻辑回归的图像分类

数据集来自MNIST手写数字数据库。它由手写数字(0到9)的28px乘28px灰度图像和每个图像的标签组成。

进口火炬、火炬视觉和MNIST

看一看长度:

这个数据集有6万张图片,可以用来训练模型。还有一个额外的10,000幅图像的测试集,可以通过将train=False传递给MNIST类来创建。

这个图像是PIL的一个对象。Image类,由一个28x28的图像和一个标签组成。药丸是Python成像库的枕头。

我们可以使用matplotlib在Jupyter中查看图像。matplotlib是Python中数据科学的事实绘制和图形库。

先看数据集中的几张图片:

除了导入matplotlib之外,还增加了一个特殊的声明% %matplotlib inline来告诉Jupyter在Notebook中绘制图形。

如果没有这条语句,Jupyter将在弹出窗口中显示图像。以%开头的语句称为IPython魔术命令,用于配置Jupyter本身的行为。

PyTorch可以直接处理图像,所以需要转换成张量。

PyTorch数据集允许我们指定一个或多个转换函数,这些函数在加载时应用于图像。

Torchvision.transforms包含许多这样的预定义函数,我们将使用ToTensor transform将图像转换为PyTorchtensor。

现在图像被转换成1x28x28的张量。第一维用于跟踪颜色通道。因为MNIST数据集中的图像是灰度的,所以只有一个通道。其他数据集具有彩色图像,在这种情况下有3个通道:红色、绿色和蓝色(RGB)。

让让我们来看看张量中的一些样本值:

0表示黑色,1表示白色,中间值表示不同的灰度。嗯,你也可以用plt.imshow把张量画成图像。

请注意,我们只需要将28x28矩阵传递给plt.imshow,我们不不需要频道大小。

我们还传递了一个彩色地图(cmap=格雷)来表示我们想要查看灰度图像。

以及训练和验证数据集。

当构建真实世界的机器学习模型时,通常将数据集分成3个部分:

训练集:用于训练模型,即计算损失,通过梯度下降调整模型的权重。

验证集:用于在训练过程中评估模型,调整超级参数(学习率等。)并选择模型的最佳版本。

测试集:用于比较不同的模型或不同类型的建模方法,并报告模型的最终准确性。

在MNIST数据集中,有60,000幅训练图像和10,000幅测试图像。测试集是标准化的,以便不同的研究人员可以报告他们的模型对同一组图像的结果。

因为没有预定义的验证集,我们必须手动将60,000幅图像分成训练和验证数据集。

让定义一个函数来随机选择验证集图像的给定部分。

Split _ indices随机打乱数组索引0,1,n-1,并将验证集所需的部分从中分离出来。

在创建验证集之前打乱索引是很重要的,因为训练图像通常按目标标签排序,即0图像、1图像、2图像等等。

如果我们只通过选择最后20%的图像来选择20%的验证集,那么验证集只会包含8s和9s的图像,而训练集不会包含8s和9s的图像,所以不可能训练出好的模型。

我们随机调整了指标,选取了一小部分(20%)作为验证集。

现在,您可以通过使用SubsetRandomSampler为每个组件创建一个PyTorch数据加载器。SubsetRandomSampler从给定的索引列表中随机抽取元素,同时创建批处理数据。

模型

现在我们已经准备好了数据加载器,我们可以定义我们的模型了。

logistic回归模型和线性回归模型几乎一样,也就是有权重和偏差矩阵,通过简单的矩阵运算得到输出(pred=x @ w.t() b)。

就像我们用线性回归一样,我们可以用nn。线性来创建模型,而不是手动定义和初始化矩阵。

As nn。Linear期望每个训练样本是一个张量,每个1x28x28图像张量在转移到模型之前需要展平成大小为784(28 * 28)的张量。

每幅图像的输出是一个大小为10的张量,张量的每个元素代表一个特定目标标记的概率(即0到9)。的图像预测标签只是具有最高概率的标签。

让让我们看看权重和偏差。

虽然我们在这里编程了7850个参数,但大体思路差不多。从我们的数据集,我们得到第一批,其中包含100张图片,并将其传递给模型。

如果输出是直接的,将会报告一个错误。因为我们输入数据的形状不正确。我们的图像形状是1x28x28,但我们需要它们是784的向量。

也就是说,我们需要评估他们。我们将使用张量美国重塑方法,这将使我们有效地查看每个图像都是一个平面向量,而不会真正改变底层数据。

为了在我们的模型中包含这个额外的功能,我们需要通过扩展神经网络来定义一个定制的模型。PyTorch的模块类。

在__init__构造函数方法中,我们使用nn。线性以实例化重量和偏差。

在正向方法中,当我们向模型传递一批输入时,我们展平输入张量,然后将其传递给self.linear

XB . shape(-1,28 * 28)向PyTorch表示我们想要一个xbtensor的二维视图,其中沿着第二维的长度是28 * 28(即784)。

的参数。reshape可以设置为-1(这里是第一维),这样PyTorch就可以根据原始张量的形状自动计算。

请注意,该模型不再具有。体重和。偏差属性(因为它们现在在。linear属性),但是它有一个. parameters方法,该方法返回一个带有权重和偏差的列表,并且可以使用PyTorch优化器。

我们新的定制模型可以像以前一样使用。让让我们看看它是否有效。

对于100个输入图像中的每一个,我们得到10个输出,每个类别一个。如前所述,我们希望这些输出表示概率,但由于这个原因,每个输出行的元素必须在0和1之间,并且加起来等于1,这里显然不是这样。

为了将输出线转换成概率,我们使用softmax函数,其公式如下:

首先,我们用E yi替换输出行中的每个元素Yi,这使得所有元素都是正的。然后我们将每个元素除以所有元素的总和,以确保它们的总和为1。

虽然softmax函数很容易实现,但我们将使用PyTorch中提供的实现,因为它适用于多维张量(在我们的例子中是输出行列表)。

softmax函数包含在torch.nn.functional包中,要求我们指定softmax必须应用的尺寸。

最后,我们可以通过简单地选择每个输出行中概率最高的元素的索引来确定每个图像的预测标签。

这是使用torch.max完成的,它返回最大元素和最大元素在张量的特定维度上的索引。

上面打印的数字是第一批训练图像的预测标签。我们将它们与实际标签进行比较。

显然,预测的标签和实际的标签完全不同。这是因为我们已经开始使用随机初始化的权重和偏差。

我们需要对模型进行训练,也就是使用梯度下降来调整权重,从而做出更好的预测。

评估和损失函数

像线性回归一样,我们需要一种方法来评估模型的实现。一个很自然的方法就是找到正确预测的标签的百分比,也就是预测的准确率。

==运算符对两个形状相同的张量进行元素间比较,返回形状相同的张量,其中不相等元素为0,相等元素为1。

将结果传递给torch.sum将返回正确预测的标签数量。最后,我们除以图像的总数来得到精确度。

让计算当前模型的第一批数据的准确性。显然,我们预计情况会非常糟糕。

尽管准确性是我们(人类)评估模型的好方法,但它不能用作使用梯度下降优化模型的损失函数,原因如下:

这不是一个可区分的函数。Torch.max和==是不连续不可微的,所以我们可以使用精度来计算权重和偏差的梯度。

这不考虑模型预测的实际概率,所以它可以不能为逐步改进提供足够的反馈。

由于这些原因,准确度是一个很好的分类评价指标,但它不是一个很好的损失函数。分类中常用的损失函数是交叉熵,其公式如下:

虽然看起来很复杂,但其实很简单:

对于每条输出线,选择正确标签的预测概率。比如说。如果图像的预测概率是[0.1,0.3,0.2,]并且正确的标签是1,我们选择相应的元素0.3,忽略其余的。

然后,取所选概率的对数。如果概率很高,也就是接近1,那么它的对数就是一个很小的负值,接近0。如果概率很低(接近0),对数就是很大的负值。我们也把结果乘以-1,结果是差预测损失的一个很大的正值。最后,我们得到所有输出行的交叉熵的平均值,从而得到一批数据的总损失。

与精度不同的是,交叉熵是一个连续可微的函数,它也为模型中的逐步改进提供了良好的反馈(正确标注导致损失更低的概率略高)。这使它成为损失函数的一个好选择。

PyTorch作为torch.nn.functionalpackage的一部分,提供了一个有效且张量友好的交叉熵实现。

此外,它还在内部执行softmax,因此我们可以直接传递模型的输出,而无需将它们转换为概率。

由于交叉熵是在所有训练样本上平均的正确标签的预测概率的负对数,所以使用解释结果数量的方法,例如2.23是E-2.23,平均值大约是0.1,作为正确标签的预测概率。减少损失和改进模型。

使最优化

我们将使用optim。SGD优化器在训练时更新权重和偏差,但学习率更高,为1e-3。

训练机器学习模型时需要预先选择批量大小、学习速率等参数,称为超参数。

选择合适的超参数对于在合理的时间内训练准确的模型是非常重要的,并且是一个活跃的研究和实验领域。随意尝试不同的学习速度,看看它是如何影响训练过程的。

培训模式

既然我们已经定义了数据加载器、模型、损失函数和优化器,我们就可以开始训练模型了。

训练过程几乎和线性回归一样。但是,我们将增加我们之前定义的拟合函数,以在每个历元结束时使用验证集来评估模型的准确性和损失。

首先,我们定义一个函数loss_batch:

计算一批数据的损失

如果提供了优化程序,您可以选择执行梯度下降更新步骤。

可选地,使用预测和实际目标来计算度量(例如,准确度)。

Optimizer是一个可选参数,用于确保我们可以重用loss_batch来计算验证集上的损失。

我们还将批处理的长度作为结果的一部分返回,因为它在组合整个数据集的损失/度量时非常有用。

接下来,我们定义一个函数evaluate,它计算验证集的总损失。

如果不清楚这个函数的作用,请尝试在单独的单元格中执行每个语句,然后检查结果。

我们还需要重新定义精度,以便直接操作整个批量输出,这样我们就可以将它用作拟合度量。

注意,我们不不需要对输出应用softmax,因为它不不要改变结果的相对顺序。

这是因为E x是一个增函数,也就是if y1 y2,E Y1E Y2,在取值平均值得到softmax后也是如此。

让让我们看看如何使用初始重量和偏差集在验证集上执行模型。

初始准确率低于10%,这是人们对随机初始化模型的预期(因为它有十分之一的几率通过随机猜测得到标签)。

还请注意,我们使用。格式化方法和消息字符串,以便只打印小数点后的前四位数字。

现在我们可以通过使用loss_batch和evaluate轻松定义拟合函数。

现在我们准备训练模型。让我们训练五个纪元,观察结果。

哦,太好了!有更多的爱

折线图更直观地展示了效果。

从上图可以清楚地看到,模型即使经过长时间的训练,也未必能超过90%的准确率阈值。

一个可能的原因是学习率可能太高。模型的参数可以弹跳在具有最低损失的最佳参数集附近。

你可以试着放慢学习速度,训练几个历元,看看有没有帮助。

更可能的原因是模型不够强大。如果你还记得我们最初的假设,我们假设输出(这种情况下的类概率)是输入(像素强度)的线性函数,它是通过对权重矩阵执行矩阵乘法并加上偏差而获得的。

这是一个相当弱的假设,因为在图像中的像素强度和它所代表的数字之间实际上可能没有线性关系。

虽然它对于像MNIST这样的简单数据集非常有效(使我们能够达到85%的准确率),但我们需要更复杂的模型来捕捉图像像素和标签之间的非线性关系,以便识别复杂的任务,如日常物品和动物。

使用单一图像进行测试。

虽然到目前为止,我们一直在跟踪模型的整体准确性,但在一些样本图像上查看模型的结果也是一个好主意。

让让我们用预定义的10,000幅图像的测试数据集来测试我们的模型。首先,我们使用ToTensor变换来重新创建测试数据集。

数据集中单个图像的样本:

让定义一个辅助函数predict_image,返回单个图像张量的预测标签。

Img.unsqueeze只是在1x28x28张量的开头增加了另一个维度,使其成为1x28x28张量。该模型将其视为包含单个图像的批次。

通过收集更多的训练数据,增加/降低模型的复杂性,以及更改超级参数,它可以帮助我们通过确定我们的模型表现不佳的地方来改进我们的模型。

最后,让让我们看看模型在测试集上的总体损失和准确性。

我们希望这类似于验证集上的准确度/损失。如果不是,我们可能需要一个更好的验证集,具有与测试集相似的数据和分布(通常来自真实世界的数据)。

保存并加载模型。

由于我们已经对模型进行了长时间的训练,并获得了合理的精度,因此将权重和偏移矩阵保存到磁盘是一个好主意,这样我们可以在以后重用模型,避免从头开始重新训练。这里如何保存模型。

的。state_dict方法返回一个OrderedDict,它包含所有映射到模型右侧属性的权重和偏移矩阵。

为了加载模型权重,我们可以实例化一个新的MnistModel类对象,并使用。加载状态字典方法。

就像完整性检查一样,让验证该模型在测试集上具有与之前相同的损失和准确性。

好吧。你我们结束了。它我们的提交会议又开始了。如果你已经忘记如何操作: