参与Panda

PyTorch进阶之路(二):如何实现线性回归

PyTorch 是 Facebook 开发和维护的一个开源的神经网络库,近来的发展势头相当强劲,也有越来越多的开发者为其撰写教程,本文也是其中之一。这是「PyTorch: Zero to GANs」系列教程的第二篇,介绍了在 PyTorch 中实现线性回归和梯度下降的基本方法。

这篇文章将讨论机器学习的一大基本算法:线性回归。我们将创建一个模型,使其能根据一个区域的平均温度、降雨量和湿度(输入变量或特征)预测苹果和橙子的作物产量(目标变量)。训练数据如下:

线性回归模型中,每个目标变量的估计方式都是作为输入变量的一个加权和,另外还会有某个常量偏移(也被称为偏置量):

yield_apple = w11 * temp + w12 * rainfall + w13 * humidity + b1

yield_orange = w21 * temp + w22 * rainfall + w23 * humidity + b2 

可视化地看,这意味着苹果或橙子的产量是温度、降雨量或湿度的线性函数或平面函数:

因为我们只能展示三个维度,所以此处没有给出湿度

线性回归的「学习」部分是指通过检视训练数据找到一组权重(w11、w12…w23)和偏置 b1 和 b2),从而能根据新数据得到准确的预测结果(即使用一个新地区的平均温度、降雨量和湿度预测苹果和橙子的产量)。为了得到更好的结果,这个过程会对权重进行许多次调整,其中会用到一种名为「梯度下降」的优化技术。

系统设置

如果你想一边阅读一边运行代码,你可以通过下面的链接找到本教程的 Jupyter Notebook:

https://jvn.io/aakashns/e556978bda9343f3b30b3a9fd2a25012

你可以克隆这个笔记,安装必要的依赖包,然后通过在终端运行以下命令来启动 Jupyter

$ pip install jovian --upgrade # Install the jovian library 
$ jovian clone e556978bda9343f3b30b3a9fd2a25012 # Download notebook & dependencies
$ cd 02-linear-regression # Enter the created directory 
$ conda env update # Install the dependencies
$ conda activate 02-linear-regression # Activate virtual environment
$ jupyter notebook # Start Jupyter

如果你的 conda 版本更旧一些,你也许需要运行 source activate 02-linear-regression 来激活环境。对以上步骤的更详细的解释可参阅本教程的前一篇文章

首先我们导入 Numpy 和 PyTorch:

训练数据

训练数据可以使用两个矩阵表示:输入矩阵和目标矩阵;其中每个矩阵的每一行都表示一个观察结果,每一列都表示一个变量。

我们已经分开了输入变量和目标变量,因为我们将分别操作它们。另外,我们创建的是 numpy 数组,因为这是常用的操作训练数据的方式:将某些 CSV 文件读取成 numpy 数组,进行一些处理,然后再将它们转换成 PyTorch 张量,如下所示:

从头开始构建线性回归模型

权重和偏置(w11、w12…w23、b1 和 b2)也可表示成矩阵,并初始化为随机值。w 的第一行和 b 的第一个元素用于预测第一个目标变量,即苹果的产量;对应的第二个则用于预测橙子的产量。

torch.randn 会创建一个给定形状的张量,其中的元素随机选取自一个均值为 0 且标准差为 1 的正态分布。

该模型实际上就是一个简单的函数:执行输入 x 和权重 w 的矩阵乘法,再加上偏置 b(每个观察都会重复该计算)。

我们可将该模型定义为:

@ 表示 PyTorch 中的矩阵乘法,.t 方法会返回一个张量的转置。

通过将输入数据传入模型而得到的矩阵是目标变量的一组预测结果。

接下来比较我们的模型的预测结果与实际的目标。

可以看到,我们的模型的预测结果与目标变量的实际值之间差距巨大。很显然,这是由于我们的模型的初始化使用了随机权重和偏置,我们可不能期望这些随机值就刚好合适。

损失函数

在我们改进我们的模型之前,我们需要一种评估模型表现优劣的方法。我们可以使用以下方法比较模型预测和实际目标:

  • 计算两个矩阵(preds 和 targets)之间的差异;

  • 求这个差异矩阵的所有元素的平方以消除其中的负值;

  • 计算所得矩阵中元素的平均值。

结果是一个数值,称为均方误差(MSE)。

torch.sum 返回一个张量中所有元素的和,.numel 方法则返回一个张量中元素的数量。我们来计算一下我们模型的当前预测的均方误差:

我们解读一下这个结果:平均而言,预测结果中每个元素与实际目标之间的差距大约为 215(46194 的平方根)。考虑到我们所要预测的数值的范围本身只有 50-200,所以这个结果实在相当糟糕。我们称这个结果为损失(loss),因为它指示了模型在预测目标变量方面的糟糕程度。损失越低,模型越好。

计算梯度

使用 PyTorch,我们可以根据权重和偏置自动计算 loss 的梯度和导数,因为它们已将 requires_grad 设置为 True。

这些梯度存储在各自张量的 .grad 属性中。注意,根据权重矩阵求得的 loss 的导数本身也是一个矩阵,且具有相同的维度。

这个损失是我们的权重和偏置的一个二次函数,而我们的目标是找到能使得损失最低的权重集。如果我们绘制出任意单个权重或偏置元素下的损失的图表,我们会得到类似下图的结果。通过微积分,我们可以了解到梯度表示损失的变化率,即与权重和偏置相关的损失函数的斜率。

如果梯度元素为正数,则:

  • 稍微增大元素的值会增大损失。

  • 稍微减小元素的值会降低损失。

作为权重的函数的 MSE 损失(蓝线表示梯度)

如果梯度元素为负数,则:

  • 稍微增大元素的值会降低损失。

  • 稍微减小元素的值会增大损失。

作为权重的函数的 MSE 损失(绿线表示梯度)

通过改变一个权重元素而造成的损失的增减正比于该元素的损失的梯度值。这就是我们用来提升我们的模型的优化算法的基础。

在我们继续之前,我们通过调用 .zero() 方法将梯度重置为零。我们需要这么做的原因是 PyTorch 会累积梯度,也就是说,我们下一次在损失上调用 .backward 时,新的梯度值会被加到已有的梯度值上,这可能会导致意外结果出现。

使用梯度下降调整权重和偏置

我们将使用梯度下降优化算法来降低损失和改善我们的模型,步骤如下:

  1. 生成预测

  2. 计算损失

  3. 根据权重和偏置计算梯度

  4. 按比例减去少量梯度来调整权重

  5. 将梯度重置为零

下面我们一步步地实现:

注意,这里的预测结果和之前的一样,因为我们还未对我们的模型做出任何修改。损失和梯度也是如此。

最后,使用上面计算得到的梯度更新权重和偏置。

上面需要注意的几点:

  • 我们使用 torch.no_grad 指示 PyTorch 我们在更新权重和偏置时不应该跟踪、计算或修改梯度。

  • 我们为梯度乘上了一个非常小的数值(这个案例中为 10^-5),以确保我们不会改变权重太多,因为我们只想在梯度的下降方向上迈出一小步。这个数值是这个算法的学习率(learning rate)。

  • 在更新权重之后,我们将梯度重置为零,以免影响后续计算。

现在我们来看看新的权重和偏置:

使用新的权重和偏置,模型的损失应更低。

只是简单地使用梯度下降来稍微调整权重和偏置,我们就已经实现了损失的显著下降。

多次训练

为了进一步降低损失,我们可以多次使用梯度重复调整权重和偏置的过程。一次迭代被称为一个 epoch。我们训练模型 100 epoch 看看。

再次验证,现在损失应该会更低:

可以看到,现在的损失比我们开始时低了很多。我们看看模型的预测结果,并将其与目标比较一下。

现在的预测结果已非常接近目标变量;而且通过训练模型更多 epoch,我们还能得到甚至更好的结果。

使用 PyTorch 内置的线性回归

上面的模型和训练过程是使用基本的矩阵运算实现的。但因为这是一种非常常用的模式,所以 PyTorch 配备了几个内置函数和类,让人能很轻松地创建和训练模型。

首先从 PyTorch 导入 torch.nn 软件包,其中包含了用于创建神经网络的效用程序类。

和之前一样,我们将输入和目标表示成矩阵形式。

我们这一次使用 15 个训练样本,以演示如何以小批量的形式处理大数据集。

数据集和数据加载器

我们将创建一个 TensorDataset,这让我们可以读取 inputs 和 targets 的行作为元组,并提供了 PyTorch 中用于处理许多不同类型的数据集的标准 API。

TensorDataset 让我们可以使用数组索引表示法(上面代码中的 [0:3])读取一小部分训练数据。它会返回一个元组(或配对),其中第一个元素包含所选行的输入变量,第二个元素包含目标,

我们还将创建一个 DataLoader,它可以在训练时将数据分成预定义大小的批次。它还能提供其它效用程序,如数据的混洗和随机采样。

数据加载器通常搭配 for-in 循环使用。举个例子:

在每次迭代中,数据加载器都会返回一批给定批大小的数据。如果 shuffle 设为 True,则在创建批之前会对训练数据进行混洗。混洗能帮助优化算法的输入随机化,这能实现损失的更快下降。

nn.Linear

除了人工地实现权重和偏置的初始化,我们还可以使用 PyTorch 的 nn.Linear 类来定义模型,这能自动完成初始化。

PyTorch 模型还有一个很有用的 .parameters 方法,这能返回一个列表,其中包含了模型中所有的权重和偏置矩阵。对于我们的线性回归模型,我们有一个权重矩阵和一个偏置矩阵。

我们可以使用之前一样的方式来得到模型的预测结果:

损失函数

除了手动定义损失函数,我们也可使用内置的损失函数 mse_loss:

nn.functional 软件包包含很多有用的损失函数和其它几个效用程序。

我们计算一下我们模型的当前预测的损失。

优化器

除了以人工方式使用梯度操作模型的权重和偏置,我们也可使用优化器 optim.SGD。SGD 表示「随机梯度下降」。之所以是「随机」,原因是样本是以批的形式选择(通常会用到随机混洗),而不是作为单独一个数据组。

注意,这里的 model.parameters() 是 optim.SGD 的一个参数,这样优化器才知道在训练步骤中应该修改哪些矩阵。另外,我们还可以指定一个学习率来控制参数每次的修改量。

训练模型

我们现在已经准备好训练模型了。我们将遵循实现梯度下降的同一过程:

  • 生成预测

  • 计算损失

  • 根据权重和偏置计算梯度

  • 按比例减去少量梯度来调整权重

  • 将梯度重置为零

唯一变化的是我们操作的是分批的数据,而不是在每次迭代中都处理整个训练数据集。我们定义一个效用函数 fit,可训练模型给定的 epoch 数量。

上面需要注意的几点:

  • 我们使用之前定义的数据加载器来为每个迭代获取数据批次

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

  • 我们还添加了一个日志语句,能够显示每第 10 个 epoch 的最后一批数据的损失,从而可让我们跟踪训练进程。loss.item 会返回存储在损失张量中的实际值。

训练模型 100 epoch。

接下来使用我们的模型生成预测结果,再验证它们与目标的接近程度。

实际上,这个预测结果非常接近我们的目标。现在,我们有了一个相当好的预测模型,可以根据一个地区的平均温度、降雨量和湿度预测苹果和橙子的产量。

进阶阅读

本教程覆盖了很多基础内容,包括线性回归梯度下降优化算法。如果你想进一步探索这些主题,可参考以下资源:

  • 导数梯度下降的更详尽的解释可参考这些 Udacity 课程笔记

  • https://storage.googleapis.com/supplemental_media/udacityu/315142919/Gradient%20Descent.pdf

  • 线性回归工作方式的可视化动画

  • https://hackernoon.com/visualizing-linear-regression-with-pytorch-9261f49edb09

  • 想从数学方面理解矩阵微积分线性回归梯度下降?那你不能错过吴恩达的斯坦福大学 CS229 的课程笔记

  • https://github.com/Cleo-Stanford-CS/CS229_Notes/blob/master/lectures/cs229-notes1.pdf


原文链接:https://medium.com/jovian-io/linear-regression-with-pytorch-3dde91d60b50

扩展阅读

PyTorch 进阶之路(一):张量与梯度

工程PyTorch
4
相关数据
吴恩达人物

斯坦福大学教授,人工智能著名学者,机器学习教育者。2011年,吴恩达在谷歌创建了谷歌大脑项目,以通过分布式集群计算机开发超大规模的人工神经网络。2014年5月16日,吴恩达加入百度,负责“百度大脑”计划,并担任百度公司首席科学家。2017年3月20日,吴恩达宣布从百度辞职。2017年12月,吴恩达宣布成立人工智能公司Landing.ai,并担任公司的首席执行官。2018年1月,吴恩达成立了投资机构AI Fund。

权重技术

线性模型中特征的系数,或深度网络中的边。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则相应的特征对模型来说没有任何贡献。

机器学习技术

机器学习是人工智能的一个分支,是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、计算复杂性理论等多门学科。机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。因为学习算法中涉及了大量的统计学理论,机器学习与推断统计学联系尤为密切,也被称为统计学习理论。算法设计方面,机器学习理论关注可以实现的,行之有效的学习算法。

参数技术

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

学习率技术

在使用不同优化器(例如随机梯度下降,Adam)神经网络相关训练中,学习速率作为一个超参数控制了权重更新的幅度,以及训练的速度和精度。学习速率太大容易导致目标(代价)函数波动较大从而难以找到最优,而弱学习速率设置太小,则会导致收敛过慢耗时太长

损失函数技术

在数学优化,统计学,计量经济学,决策理论,机器学习和计算神经科学等领域,损失函数或成本函数是将一或多个变量的一个事件或值映射为可以直观地表示某种与之相关“成本”的实数的函数。

导数技术

导数(Derivative)是微积分中的重要基础概念。当函数y=f(x)的自变量x在一点x_0上产生一个增量Δx时,函数输出值的增量Δy与自变量增量Δx的比值在Δx趋于0时的极限a如果存在,a即为在x0处的导数,记作f'(x_0) 或 df(x_0)/dx。

张量技术

张量是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数,这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 维空间内,有 个分量的一种量,其中每个分量都是坐标的函数,而在坐标变换时,这些分量也依照某些规则作线性变换。称为该张量的秩或阶(与矩阵的秩和阶均无关系)。 在数学里,张量是一种几何实体,或者说广义上的“数量”。张量概念包括标量、矢量和线性算子。张量可以用坐标系统来表达,记作标量的数组,但它是定义为“不依赖于参照系的选择的”。张量在物理和工程学中很重要。例如在扩散张量成像中,表达器官对于水的在各个方向的微分透性的张量可以用来产生大脑的扫描图。工程上最重要的例子可能就是应力张量和应变张量了,它们都是二阶张量,对于一般线性材料他们之间的关系由一个四阶弹性张量来决定。

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

线性回归技术

在现实世界中,存在着大量这样的情况:两个变量例如X和Y有一些依赖关系。由X可以部分地决定Y的值,但这种决定往往不很确切。常常用来说明这种依赖关系的最简单、直观的例子是体重与身高,用Y表示他的体重。众所周知,一般说来,当X大时,Y也倾向于大,但由X不能严格地决定Y。又如,城市生活用电量Y与气温X有很大的关系。在夏天气温很高或冬天气温很低时,由于室内空调、冰箱等家用电器的使用,可能用电就高,相反,在春秋季节气温不高也不低,用电量就可能少。但我们不能由气温X准确地决定用电量Y。类似的例子还很多,变量之间的这种关系称为“相关关系”,回归模型就是研究相关关系的一个有力工具。

梯度下降技术

梯度下降是用于查找函数最小值的一阶迭代优化算法。 要使用梯度下降找到函数的局部最小值,可以采用与当前点的函数梯度(或近似梯度)的负值成比例的步骤。 如果采取的步骤与梯度的正值成比例,则接近该函数的局部最大值,被称为梯度上升。

随机梯度下降技术

梯度下降(Gradient Descent)是遵循成本函数的梯度来最小化一个函数的过程。这个过程涉及到对成本形式以及其衍生形式的认知,使得我们可以从已知的给定点朝既定方向移动。比如向下朝最小值移动。 在机器学习中,我们可以利用随机梯度下降的方法来最小化训练模型中的误差,即每次迭代时完成一次评估和更新。 这种优化算法的工作原理是模型每看到一个训练实例,就对其作出预测,并重复迭代该过程到一定的次数。这个流程可以用于找出能导致训练数据最小误差的模型的系数。

大数据技术

大数据,又称为巨量资料,指的是传统数据处理应用软件不足以处理它们的大或复杂的数据集的术语。

微积分技术

微积分(Calculus)是高等数学中研究函数的微分(Differentiation)、积分(Integration)以及有关概念和应用的数学分支。它是数学的一个基础学科。内容主要包括极限、微分学、积分学及其应用。微分学包括求导数的运算,是一套关于变化率的理论。它使得函数、速度、加速度和曲线的斜率等均可用一套通用的符号进行讨论。积分学,包括求积分的运算,为定义和计算面积、体积等提供一套通用的方法 。

批次技术

模型训练的一次迭代(即一次梯度更新)中使用的样本集。

Jupyter技术

Jupyter Notebook(此前被称为 IPython notebook)是一个交互式笔记本,支持运行 40 多种编程语言。 Jupyter Notebook 的本质是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码,数学方程,可视化和 markdown。 用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等 。

优化器技术

优化器基类提供了计算梯度loss的方法,并可以将梯度应用于变量。优化器里包含了实现了经典的优化算法,如梯度下降和Adagrad。 优化器是提供了一个可以使用各种优化算法的接口,可以让用户直接调用一些经典的优化算法,如梯度下降法等等。优化器(optimizers)类的基类。这个类定义了在训练模型的时候添加一个操作的API。用户基本上不会直接使用这个类,但是你会用到他的子类比如GradientDescentOptimizer, AdagradOptimizer, MomentumOptimizer(tensorflow下的优化器包)等等这些算法。

暂无评论
暂无评论~