生成模型层
神经网络是按层连接的神经元的集合。 每个神经元都是一个小的计算单元,执行简单的计算来共同解决问题。
神经元分为 3 种类型的层:输入层、隐藏层和输出层。
隐藏层和输出层包含许多神经元。
神经网络模仿人脑处理信息的方式。
-
神经网络的组成部分
- 激活函数决定神经元是否应该被激活。
神经网络中发生的计算包括应用激活函数。
如果神经元激活,则意味着输入很重要。
有不同种类的激活函数。 选择使用哪个激活函数取决于您想要的输出。
激活函数的另一个重要作用是为模型添加非线性。 - 权重影响我们网络的输出与预期输出值的接近程度。
当输入进入神经元时,它会乘以权重值,所得输出要么被观察,要么被传递到神经网络中的下一层。
一层中所有神经元的权重被组织成一个张量。 - 偏差弥补了激活函数的输出与其预期输出之间的差异。
低偏差值表明网络对输出形式做出更多假设,而高偏差值对输出形式做出更少假设。
- 激活函数决定神经元是否应该被激活。
-
构建神经网络
神经网络由对数据执行操作的层和模块组成。
torch.nn
命名空间提供了构建您自己的神经网络所需的所有构建块。
PyTorch 中的每个模块都是nn.Module
的子类。 神经网络本身就是一个由其他模块(层)组成的模块。 这种嵌套结构允许轻松构建和管理复杂的架构。
构建一个神经网络来FashionMNIST 数据集中的图像进行分类。
import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
-
获取训练设备
-
定义类
我们通过子类化nn.Module
来定义神经网络,并在__init__ 中初始化神经网络层。
每个nn.Module
子类都在 forward 方法中实现对输入数据的操作。
-
我们创建一个 NeuralNetwork 实例
将其移动到设备上并打印其结构。
-
权重与偏差
nn.Linear
模块随机初始化每层的权重和偏差,并在内部将值存储在张量中。 -
nn.Flatten
我们初始化nn.Flatten
层,将每个 2D 28x28 图像转换为 784 个像素值的连续数组,即维持小批量维度(dim=0 时)。 每个像素都被传递到神经网络的输入层。
-
nn.Sequential
是模块的有序容器。 数据按照定义的顺序通过所有模块。 您可以使用顺序容器来组合一个快速网络,例如 seq_modules。
使用 torch.autograd
自动微分
在训练神经网络时,最常用的算法是反向传播。
在该算法中,根据损失函数相对于给定参数的梯度来调整参数(模型权重)。
损失函数计算神经网络产生的预期输出和实际输出之间的差异。 目标是使损失函数的结果尽可能接近于零。
该算法通过神经网络向后遍历来调整权重和偏差来重新训练模型。 这就是为什么它被称为反向传播。
随着时间的推移重新训练模型以将损失减少到 0 的这种前后过程称为梯度下降。
为了计算这些梯度,PyTorch 有一个内置的微分引擎,称为 torch.autograd
。 它支持任何计算图的梯度自动计算。
考虑最简单的一层神经网络,具有输入 x、参数 w 和 b 以及一些损失函数。 它可以通过以下方式在 PyTorch 中定义:
我们应用于张量来构造计算图的函数是 Function
类的对象。 该对象知道如何向前计算函数,以及如何在向后传播步骤中计算其导数。 对反向传播函数的引用存储在张量的 grad_fn
属性中。
- 计算梯度
我们只能获取将
requires_grad
属性设置为True的计算图的叶节点的grad属性。 对于我们图中的所有其他节点,梯度将不可用。 此外,出于性能原因,我们只能在给定的图上使用一次后向来执行梯度计算。 如果我们需要在同一个图上进行多次向后调用,我们需要将retain_graph=True
传递给向后调用。
- 禁用梯度跟踪
您可能想要禁用梯度跟踪的原因有:
将神经网络中的某些参数标记为冻结参数。 这是微调预训练网络的非常常见的场景。
当您仅进行前向传递时加快计算速度,因为对不跟踪梯度的张量的计算效率更高。
默认情况下,所有 require_grad=True
的张量都会跟踪其计算历史并支持梯度计算。 然而,有些情况下我们不需要这样做,例如,当我们训练完模型而只想将其应用到一些输入数据时,即我们只想通过网络进行前向计算。
我们可以通过用 torch.no_grad()
块包围我们的计算代码来停止跟踪计算:
实现相同结果的另一种方法是在张量上使用 detach() 方法:
- 有关计算图的更多信息
从概念上讲, 在这个 DAG 中,叶子是输入张量,根是输出张量。 通过从根到叶追踪该图,您可以使用链式法则自动计算梯度。
在前向传递中,autograd 同时执行两件事:
运行请求的操作来计算结果张量,
并且在 DAG 中维护操作的梯度函数。
当在 DAG 根上调用 .backward() 时,向后传递开始。 然后Autograd:
计算每个 .grad_fn
的梯度,
将它们累积到各自张量的 .grad
属性中
使用链式法则,一直传播到叶张量。
PyTorch 中的 DAG 是动态的
需要注意的重要一点是,该图是从头开始重新创建的; 每次 .backward() 调用后,autograd 开始填充新图表。 这正是允许您在模型中使用控制流语句的原因; 如果需要,您可以在每次迭代时更改形状、大小和操作。
优化
-
优化模型参数
现在我们有了模型和数据,是时候通过优化数据参数来训练、验证和测试我们的模型了。
训练模型是一个迭代的过程;在每一次迭代中,模型都会对输出进行猜测,计算猜测的误差(损失),收集误差相对于其参数的导数,并使用梯度下降法优化这些参数。- 为训练定义了以下超参数:
- Number of Epochs - 在数据集上迭代的次数
- Batch Size - 参数更新前通过网络传播的数据样本数量
- Learning Rate - 每个batch/epoch更新模型参数的数量。数值越小,学习速度越慢,而数值过大则可能导致训练过程中出现不可预测的行为。
- 为训练定义了以下超参数:
-
优化循环
设置好超参数后,我们就可以通过优化循环来训练和优化模型。优化循环的每次迭代称为一个epoch。
每个epoch由两个主要部分组成:
-
训练循环(Train Loop)-- 迭代训练数据集,并尝试收敛到最佳参数。
- 在训练循环中,优化分三步进行:
- 调用
optimizer.zero_grad()
重置模型参数的梯度。梯度默认是累加的;为了防止重复计算,我们在每次迭代时都会明确地将梯度清零。 - 调用
loss.backward()
反向传播预测损失。PyTorch 会存入每个参数的损失梯度。 - 获得梯度后,我们调用
optimizer.step()
根据后向传递中收集的梯度调整参数。
-
验证/测试循环(Validation/Test Loop)-- 迭代测试数据集,检查模型性能是否在提高。
-
损失函数
常见的损失函数包括用于回归任务的nn.MSELoss
(均方误差)和用于分类的nn.NLLLoss
(负对数似然)。