【PyTorch】PyTorch如何构建和实验神经网络

作者 | Tirthajyoti Sarkar

来源 | Medium

编辑 | 代码医生团队

介绍

在本文中,将展示一个简单的分步过程,以在PyTorch中构建2层神经网络分类器(密集连接),从而阐明一些关键功能和样式。

PyTorch为程序员提供了极大的灵活性,使其可以在张量流过网络时创建,组合和处理张量……

核心组成

用于构建神经分类器的PyTorch的核心组件是

  • 张量(在PyTorch中央数据结构)
  • Tensor 的Autograd功能
  • nn.Module 类,用来建立任何其他神经类分类
  • 优化器
  • 损失函数

使用这些组件,将通过五个简单的步骤构建分类器

  • 将神经网络构造为自定义类(从该类继承nn.Module),其中包含隐藏层张量以及forward通过各种层和激活函数传播输入张量的方法
  • 使用此forward方法通过网络传播特征(从数据集)张量-得到一个output张量
  • 计算了loss通过比较output在地上真相,并使用内置的损失函数
  • 传播的梯度loss使用自动分化能力(Autograd)与backward方法
  • 使用损耗的梯度来更新网络的权重(这是通过执行所谓的优化器的一个步骤来实现的)optimizer.step()。

这个五步过程构成了一个完整的训练时期。只重复一遍,以降低损失并获得较高的分类精度。

包含五个核心组件的五步过程

在PyTorch,定义了一个神经网络作为一个自定义类,从而可以收获的全部好处Ø bject-Orineted编程(OOP)范例。

张量

torch.Tensor是一个多维矩阵,其中包含单个数据类型的元素。它是框架的中央数据结构。可以从Numpy数组或列表创建Tensor,并执行各种操作,例如索引,数学,线性代数。

张量支持一些其他增强功能,从而使其具有独特性。除CPU外,它们还可以加载到GPU中(只需极其简单的代码更改)即可进行更快的计算。并且它们支持形成一个向后图,该图跟踪使用动态计算图(DCG)应用于它们的每个操作以计算梯度。

Autograd

对于复杂的神经网络,都不擅长微积分。高维空间使头脑混乱。幸运的是有Autograd。

要处理14维空间中的超平面,请可视化3D空间并大声对自己说“十四”。每个人都做得到– Geoffrey Hinton

Tensor对象支持神奇的Autograd功能,即自动区分,这是通过跟踪和存储在Tensor流经网络时执行的所有操作来实现的。

nn.Module类

在PyTorch中,通过将其定义为自定义类来构建神经网络。然而不是从原来的Python派生object从该类继承nn.Module类。这为神经网络类注入了有用的属性和强大的方法。将在本文中看到此类定义的完整示例。

损失函数

损失函数定义了神经网络的预测与地面真实情况之间的距离,而损失的定量度量则帮助驱动网络更接近对给定数据集进行最佳分类的配置。

PyTorch提供了用于分类和回归任务的所有常见损失函数

  • 二元和多类交叉熵,
  • mean squared and mean absolute errors
  • smooth L1 loss
  • neg log-likelihood loss
  • Kullback-Leibler divergence

优化器

权重的优化以实现最低的损失是用于训练神经网络的反向传播算法的核心。PyTorch提供了大量的优化器来完成这项工作,这些优化器通过torch.optim模块公开

  • 随机梯度下降(SGD),
  • Adam, Adadelta, Adagrad, SpareAdam,
  • L-BFGS,
  • RMSprop

“五步过程构成了完整的训练时期。只重复一遍。”

神经网络类与训练

数据

对于此示例任务,首先使用Scikit-learn函数使用二进制类创建一些合成数据。在以下图表中,数据类别通过颜色区分。显然,数据集无法通过简单的线性分类器进行分离,而神经网络是解决此问题的合适机器学习工具。

用于分类示例的综合数据集

架构

选择了一个简单的完全连接的2隐藏层体系结构。如下图所示

类的定义

n_input = X.shape[1] # Must match the shape of the input features  n_hidden1 = 8 # Number of neurons in the 1st hidden layer  n_hidden2 = 4 # Number of neurons in the 2nd hidden layer  n_output = 1 # Number of output units (for example 1 for binary classification)

定义与该架构相对应的变量,然后定义主类。神经网络类定义如下所示。如前所述,它从nn.Module基类继承。

该代码几乎没有解释,带有添加的注释。在方法的定义中,forward,与Keras对模型的定义有很强的相似性。

另外,请注意使用内置线性代数运算nn.Linear(如在各层之间)和激活函数(如nn.ReLU和nn.Sigmoid在各层的输出处)。

如果实例化一个模型对象并打印它,将看到结构(与Keras的model.summary()方法平行)。

model = Network()  print(model)    Network(    (hidden1): Linear(in_features=5, out_features=8, bias=True)    (hidden2): Linear(in_features=8, out_features=4, bias=True)    (relu): ReLU()    (output): Linear(in_features=4, out_features=1, bias=True)    (sigmoid): Sigmoid()  )

损失函数,优化器和训练

为此任务选择二进制交叉熵损失,并将其定义如下(按照惯例,损失函数通常criterion在PyTorch中调用)

criterion = nn.BCELoss() # Binary cross-entropy loss

在这一点上,通过定义的神经网络模型运行输入数据集,即一次向前通过并计算输出概率。由于权重已初始化为随机,因此将看到随机输出概率(大多数接近0.5)。该网络尚未训练。

logits = model.forward(X) # Output of the forward pass (logits i.e. probabilities)

如果打印出前10个概率,将得到类似的结果,

tensor([[0.5926],[0.5854],[0.5369],[0.5802],[0.5905],[0.6010],[0.5723],[0.5842],[0.5971],[0.5883]], grad_fn=<SliceBackward>)

所有输出概率看起来都接近0.5,

平均损失的计算方法很简单,

loss = criterion(logits,y)

对于优化程序,选择简单的随机梯度下降(SGD),并将学习率指定为0.1,

from torch import optim  optimizer = optim.SGD(model.parameters(),lr=0.1)

现在进行训练。再次遵循五个步骤

  • 将渐变重置为零(以防止渐变累积)
  • 将张量向前穿过层
  • 计算损失张量
  • 计算损失的梯度
  • 通过将优化器增加一级(沿负梯度的方向)来更新权重

令人惊讶的是,如果阅读了上面的五个步骤,这正是在神经网络的所有理论讨论(以及所有教科书)中看到的。而且借助PyTorch,可以一步一步使用看似简单的代码来实现此过程。

没有任何东西隐藏或抽象。会感到用五行Python代码实施神经网络训练过程的原始力量和兴奋!

# Resets the gradients i.e. do not accumulate over passes  optimizer.zero_grad()  # Forward pass  output = model.forward(X)  # Calculate loss  loss = criterion(output,y)  # Backward pass (AutoGrad)  loss.backward()  # One step of the optimizer  optimizer.step()

训练多个时期

那只是一个时期。现在很清楚一个时期不会削减它,是吗?要运行多个时期,只需使用循环即可。

epochs = 10  for i,e in enumerate(range(epochs)):      optimizer.zero_grad() # Reset the grads      output = model.forward(X) # Forward pass      loss = criterion(output.view(output.shape[0]),y) # Calculate loss      print(f"Epoch - {i+1}, Loss - {round(loss.item(),3)}")  # Print loss      loss.backward() # Backpropagation      optimizer.step() # Optimizer one step

当运行1000个时期时,可以轻松生成所有熟悉的损耗曲线。

想看看概率随时间变化吗?

PyTorch能够进行实验,探查,弄碎和晃动物品。

只是为了好玩,如果想检查输出层概率在多个时期内如何演变,只需对前面的代码进行简单的修改就可以了,

显然未经训练的网络输出都接近1,即在正类别和负类别之间没有区别。随着训练的继续,概率彼此分离,通过调整网络的权重逐渐尝试匹配地面真理的分布。

PyTorch使您能够进行实验,探查,弄碎和晃动物品。

还有其他流行的想法吗?试试

PyTorch从其早期版本开始就非常受欢迎,尤其是在学术研究人员和初创公司中。这背后的原因很简单-它可以通过简单的代码重构尝试疯狂的想法。实验是任何科学领域新思想发展的核心,当然,深度学习也不例外。

与两个激活功能混在一起吗?

只是为了(有点)疯狂,假设想将其与两个不同的激活函数-ReLU和Hyperbolic tangent(tanh)混合在一起。想将张量分成两个平行的部分,分别对它们应用这些激活,添加结果张量,然后正常地传播它。

看起来复杂吗?实现所期望的代码。将输入张量(例如X)传递通过第一个隐藏层,然后通过使结果张量流经单独的激活函数来创建两个张量X1和X2 。只需将结果张量加起来,然后使其通过第二个隐藏层即可。

可以执行此类实验性工作,并使用PyTorch轻松更改网络的体系结构。

实验是任何科学领域新思想发展的核心,当然,深度学习也不例外。

尝试自己的自定义损失函数?

可能会想尝试自己的自定义损失函数。自高中时代起,都使用均方误差。尝试对回归问题进行四次方次幂运算怎么样?

只需定义功能…

然后在代码中使用它(请注意reg_model,可以通过在Network类输出中关闭S型激活来构造新模型。

现在,有这种感觉吗?

结论

可以在Github存储库中找到此演示的所有代码。

https://github.com/tirthajyoti/PyTorch_Machine_Learning

在本文中,总结了一些关键步骤,可以遵循这些关键步骤来快速构建用于分类或回归任务的神经网络。还展示了如何使用此框架轻松地尝试巧妙的想法。