购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1 用PyTorch构建深度神经网络

本节将介绍如何使用PyTorch构建深度神经网络需要的模块,如nn.Module、nn.functional等,以及构建深度神经网络的主要步骤。

2.1.1 神经网络的核心组件

神经网络看起来很复杂,节点多、层数多、参数更多,但核心部分或组件不多。把这些组件确定后,这个神经网络基本就确定了。这些核心组件包括如下几个部分。

● 层:神经网络的基本结构,将输入张量转换为输出张量。

● 模型:层构成的网络。

● 损失函数:参数学习的目标函数,通过最小化损失函数来学习各种参数。

● 优化器:如何使损失函数最小,这就涉及优化器。

当然,这些核心组件不是独立的,它们之间、它们与神经网络其他组件之间有着密切关系。为便于大家理解,我们把这些关键组件及相互关系用图2-1表示。

图2-1 神经网络关键组件及相互关系示意图

多个层链接在一起构成一个模型或网络,输入数据通过这个模型转换为预测值。预测值与真实值共同构成损失函数的输入,损失函数输出损失值(损失值可以是距离、概率值等),该损失值用于衡量预测值与目标结果的匹配或相似程度。优化器利用损失值更新权重参数,目标是使损失值越来越小。这是一个循环过程,当损失值达到一个阈值或循环次数达到指定次数时,循环结束。

接下来利用PyTorch的nn工具箱,构建一个神经网络实例。nn工具箱中对这些组件都有现成的包或类,可以直接使用,非常方便。

2.1.2 构建神经网络的主要工具

使用PyTorch构建神经网络的主要工具(或类)及相互关系如图2-2所示。

图2-2 PyTorch实现神经网络的主要工具及相互关系

从图2-2可知,可以基于Module类或函数(nn.functional)构建网络层。nn中的大多数层(layer)在functional中都有与之对应的函数。nn.functional中的函数与nn.Module中的layer的主要区别是后者继承自Module类,可自动提取可学习的参数,而nn.functional更像是纯函数。

(1)nn.Module

前面我们使用autograd及Tensor实现机器学习实例时,需要做不少设置,如对叶子节点的参数requires_grad设置为True,然后调用backward,再从grad属性中提取梯度。对于大规模的网络,autograd过于底层和烦琐。为了简单、有效地解决这个问题,nn是一个有效工具。它是专门为深度学习设计的一个模块,而nn.Module是nn的一个核心数据结构。nn.Module可以是神经网络的某个层,也可以是包含多层的神经网络。在实际使用中,最常见的做法是继承nn.Module,生成自己的网络或层。nn中已实现了绝大多数层,包括全连接层、损失层、激活层、卷积层、循环层等。这些层都是nn.Module的子类,能够自动检测到自己的参数,并将其作为学习参数,且针对GPU运行进行了CuDNN优化。

(2)nn.functional

nn中的层,一类是继承了nn.Module,其命名一般为nn.Xxx(第一个字母是大写),如nn.Linear、nn.Conv2d、nn.CrossEntropyLoss等。另一类是nn.functional中的函数,其名称一般为nn.functional.xxx,如nn.functional.linear、nn.functional.conv2d、nn.functional.cross_entropy等。从功能来说,两者相当,基于nn.Module能实现的层也可以基于nn.functional实现,反之亦然,而且两者性能方面也没有太大差异。

不过在具体使用时,两者还是有区别的,具体如下。

1)nn.Xxx继承于nn.Module, nn.Xxx需要先实例化并传入参数,然后以函数调用的方式调用实例化的对象并传入输入数据。它能够很好地与nn.Sequential结合使用,而nn.functional.xxx无法与nn.Sequential结合使用。

2)nn.Xxx不需要自己定义和管理weight、bias参数;而nn.functional.xxx需要自己定义weight、bias参数,每次调用的时候都需要手动传入weight、bias等参数,不利于代码复用。

3)dropout操作在训练和测试阶段是有区别的,使用nn.Xxx方式定义dropout,在调用model.eval()之后,自动实现状态的转换,而使用nn.functional.xxx却无此功能。

总的来说,两种功能都是相同的,但PyTorch官方推荐:有学习参数的(如conv2d、linear、batch_norm、dropout等)情况采用nn.Xxx方式;没有学习参数的(如maxpool、loss func、activation func等)情况采用nn.functional.xxx或者nn.Xxx方式。对于激活函数,我们采用无学习参数的F.relu方式来实现,即nn.functional.xxx方式。

2.1.3 构建模型

使用PyTorch实现神经网络的关键就是选择网络层,构建模型,然后选择损失函数和优化器。在nn工具箱中,可以直接引用的网络很多,有全连接层、卷积层、循环层、归一化层、激活层等。

PyTorch构建模型大致有以下两种方式。

1)继承nn.Module基类来构建模型。

2)使用PyTorch Lightning来构建模型。

2.1.4 训练模型

构建模型(假设为model)后,接下来就是训练模型。PyTorch训练模型主要包括加载和预处理数据集、定义损失函数、定义优化算法、循环训练模型、循环测试或验证模型、可视化结果等步骤。

(1)加载和预处理数据集

加载和预处理数据集可以使用PyTorch的数据处理工具,如torch.utils和torchvision等,这些工具将在第4章中详细介绍。

(2)定义损失函数

定义损失函数可以通过自定义方法或使用PyTorch内置的损失函数,如回归使用的nn.MSELoss()、分类使用的nn.BCELoss等损失函数,更多内容可参考2.9节。

(3)定义优化算法

PyTorch常用的优化算法都封装在torch.optim中,其设计灵活,可以扩展为自定义的优化算法。所有的优化算法都是继承了基类optim.Optimizer,并实现了自己的优化步骤。

最常用的优化算法就是梯度下降法及其变种,具体将在2.10节详细介绍,这些优化算法大多使用梯度更新参数。

如使用SGD优化器时,可设置为optimizer=torch.optim.SGD(params,lr=0.001)。

(4)循环训练模型

1)设置为训练模式:model.train()。调用model.train()会把所有的module设置为训练模式。

2)梯度清零:optimizer. zero_grad()。在默认情况下,梯度是累加的,需要手工把梯度初始化或清零,调用optimizer.zero_grad()即可。

3)求损失值:y_prev=model(x),loss=loss_fun(y_prev,y_true)。

4)自动求导,实现梯度的反向传播:loss.backward()。

5)更新参数:optimizer.step()。

(5)循环测试或验证模型

1)设置为测试或验证模式:model.eval()。调用model.eval()会把所有的training属性设置为False。

2)在不跟踪梯度模式下计算损失值、预测值等:with.torch.no_grad()。

(6)可视化结果

下面通过实例来说明如何使用nn来构建网络模型、训练模型。

说明:如果模型中有BN(Batch Normalization,批归一化)层和dropout层,需要在训练时添加model.train(),在测试时添加model.eval()。其中,model.train()用于确保BN层使用每一批数据的均值和方差进行训练,而model.eval()用于确保BN使用全部训练数据的均值和方差进行评估;而对于dropout层,model.train()用于随机取一部分网络连接来训练更新参数,而model.eval()则利用到了所有网络连接进行评估。 gej6H5ArdCHZ1aUB3iAqfgvObM6s3ffdAI/8nRM/Cumlb6/n0Zqh5DY/isi2jjTI

点击中间区域
呼出菜单
上一章
目录
下一章
×