PyTorch - 25 - CNN训练与代码示例 - 神经网络编程课程

Training: What We Do After The Forward Pass

到目前为止,在本系列中,我们了解了Tensors,并且了解了有关PyTorch神经网络的所有知识。 现在,我们准备开始培训过程。

  1. 准备数据
  2. 建立模型
  3. 训练模型
    a. 计算Loss,梯度并更新权重
  4. 分析模型的结果

在训练过程中,我们进行了前传,但是那又如何呢?我们假设我们得到了一个批次,并将其通过网络转发。一旦获得输出,我们就将预测输出与实际标签进行比较,并且一旦我们知道预测标签与实际标签的距离有多近,就可以调整网络内部的权重,以使网络预测的值更加接近到真实值(标签)。

所有这些都是针对单个批次的,我们将对每个批次重复此过程,直到涵盖训练集中的每个样本为止。在完成所有批次的此过程并传递训练集中的每个样本后,我们说一个纪元已经完成。我们使用“ epoch”一词来表示涵盖整个训练集的时间段。

在整个培训过程中,我们会根据需要执行尽可能多的时间,以达到我们期望的准确性水平。这样,我们可以执行以下步骤:

  1. 从训练集中获取批次。
  2. 将批次传递到网络。
  3. 计算损失(预测值和真实值之间的差)。
  4. 用网络权重计算损失函数的梯度。
  5. 使用梯度更新权重以减少损失。
  6. 重复步骤1-5,直到完成一个纪元。
  7. 重复步骤1-6,以达到达到最小损失所需的次数。

我们已经完全知道如何执行步骤1和2。如果您已经介绍了深度学习基础知识系列,那么您知道我们使用损失函数来执行步骤3,并且您知道我们使用反向传播和优化算法来执行执行步骤4和5。步骤6和7只是标准的Python循环(训练循环)。让我们看看如何在代码中完成此操作。

The Training Process

由于我们在上一集中禁用了PyTorch的渐变跟踪功能,因此我们需要确保将其重新打开(默认情况下处于打开状态)。

> torch.set_grad_enabled(True)
<torch.autograd.grad_mode.set_grad_enabled at 0x15b22d012b0>

Preparing For The Forward Pass

我们已经知道如何获取批处理并将其通过网络转发。 让我们看看前进过程完成后的操作。

我们将从以下内容开始:

  1. 创建我们的Network类的实例。
  2. 创建一个数据加载器,该数据加载器可从我们的培训集中提供大小为100的批次。
  3. 从这些批次之一中解包图像和标签。
> network = Network()

> train_loader = torch.utils.data.DataLoader(train_set, batch_size=100)
> batch = next(iter(train_loader)) # Getting a batch
> images, labels = batch

接下来,我们准备将我们的一批图像通过网络转发并获得输出预测。 一旦有了预测张量,就可以使用预测和真实标签来计算损失。

Calculating The Loss

为此,我们将使用PyTorch的nn.functional API中可用的cross_entropy()损失函数。一旦我们有了损失,我们就可以打印它,并使用我们创建的上一个帖子的功能检查正确的预测数。

> preds = network(images)
> loss = F.cross_entropy(preds, labels) # Calculating the loss

> loss.item()
2.307542085647583

> get_num_correct(preds, labels)
9

cross_entropy()函数返回标量值的男高音,因此我们使用item()方法将损失打印为Python数字。在100个正确的样本中,我们得到9个,并且由于我们有10个预测类别,因此我们可以通过随机猜测来预期。

Calculating The Gradients

使用PyTorch可以很容易地计算出梯度。由于我们的网络是PyTorch nn.Module,因此PyTorch在后台创建了一个计算图。随着我们的张量流过我们的网络,所有计算都添加到图中。然后,PyTorch使用计算图来计算损失函数相对于网络权重的梯度。

在计算梯度之前,让我们验证一下conv1层内部当前没有梯度。渐变是可在每一层的权重张量的grad(渐变的缩写)属性中访问的张量。

> network.conv1.weight.grad
None

为了计算梯度,我们在损耗张量上调用backward()方法,如下所示:

loss.backward() # Calculating the gradients

现在,损失函数的梯度已存储在权重张量内部。

> network.conv1.weight.grad.shape
torch.Size([6, 1, 5, 5])

优化器使用这些梯度来更新各个权重。要创建优化器,我们使用torch.optim包,其中包含许多可以使用的优化算法实现。我们将以亚当为例。

Updating The Weights

对于Adam类构造函数,我们传递网络参数(这是优化器访问梯度的方式),并且传递学习率。

最后,我们要做的就是更新权重,就是告诉优化器使用梯度来逐步实现损失函数的最小值。

optimizer = optim.Adam(network.parameters(), lr=0.01)
optimizer.step() # Updating the weights

调用step()函数时,优化程序将使用存储在网络参数中的渐变来更新权重。这意味着,如果我们再次通过网络传递同一批次,我们应该期望减少损失。检查这一点,我们可以看到确实如此:

> preds = network(images)
> loss.item()

> loss = F.cross_entropy(preds, labels)
2.262690782546997

> get_num_correct(preds, labels)
15

Train Using A Single Batch

我们可以通过以下方式总结用于单批训练的代码:

network = Network()

train_loader = torch.utils.data.DataLoader(train_set, batch_size=100)
optimizer = optim.Adam(network.parameters(), lr=0.01)

batch = next(iter(train_loader)) # Get Batch
images, labels = batch

preds = network(images) # Pass Batch
loss = F.cross_entropy(preds, labels) # Calculate Loss

loss.backward() # Calculate Gradients
optimizer.step() # Update Weights

print('loss1:', loss.item())
preds = network(images)
loss = F.cross_entropy(preds, labels)
print('loss2:', loss.item())

输出:

loss1: 2.3034827709198
loss2: 2.2825052738189697

Building The Training Loop Is Next

我们现在应该对培训过程有一个很好的了解。 在下一个情节中,我们将通过构建训练循环来完成这些过程,以了解如何扩展这些想法。 下一个见!

猜你喜欢

转载自blog.csdn.net/weixin_48367136/article/details/112547194