PyTorch
中的 torch.nn
包提供了很多与实现神经网络中的具体功能相关的类,这些类涵盖了深度神经网络模型在搭建和参数优化过程中的常用内容,比如神经网络中的卷积层、池化层、全连接层这类层次构造的方法、防止过拟合的参数归一化方法、Dropout
方法,还有激活函数部分的线性激活函数、非线性激活函数相关的方法,等等。
下面使用 PyTorch
的 torch.nn
包来简化我们之前的代码,开始部分的代码变化不大,如下所示:
import torch as t
batch_n = 100
input_data = 10000
hidden_layer = 100
output_data = 10
x = t.randn(batch_n, input_data, requires_grad=False)
y = t.randn(batch_n, output_data, requires_grad=False)
这里仅定义了输入和输出的变量,之前定义神经网络模型中的权重参数的代码被删减了,这和我们之后在代码中使用的 torch.nn
包中的类有关,因为这个类能够帮助我们自动生成和初始化对应维度的权重参数。模型搭建的代码如下:
models = t.nn.Sequential(
t.nn.Linear(input_data, hidden_layer),
t.nn.ReLU(),
t.nn.Linear(hidden_layer, output_data)
)
torch.nn.Sequential
括号内的内容就是我们搭建的神经网络模型的具体结: *
torch.nn.Linear(input_data,hidden_layer)
完成从输入层到隐藏层的线性变换;torch.nn.ReLU()
为激活函数;torch.nn.Linear(hidden_layer, output_data)
完成从隐藏层到输出层的线性变换;下面分别对在以上代码中使用的 torch.nn.Sequential
、torch.nn.Linear
和 torch.nn.RelU
这三个类进行详细介绍。
torch.nn.Sequential
类是 torch.nn
中的一种序列容器,通过在容器中嵌套各种实现神经网络中具体功能相关的类,来完成对神经网络模型的搭建,最主要的是,参数会按照我们定义好的序列自动传递下去。
我们可以将嵌套在容器中的各个部分看作各种不同的模块,这些模块可以自由组合。模块的加入一般有两种方式,一种是在以上代码中使用的直接嵌套,另一种是以 orderdict
有序字典的方式进行传入,这两种方式的唯一区别是:
orderdict
搭建的模型的每个模块都有我们自定义的名字;下面通过示例来直观地看一下使用这两种方式搭建的模型之间的区别。
首先,使用直接嵌套搭建的模型代码如下:
import torch as t
batch_n = 100
input_data = 10000
hidden_layer = 100
output_data = 10
x = t.randn(batch_n, input_data, requires_grad=False)
y = t.randn(batch_n, output_data, requires_grad=False)
models = t.nn.Sequential(
t.nn.Linear(input_data, hidden_layer),
t.nn.ReLU(),
t.nn.Linear(hidden_layer, output_data)
)
print(models)
对该模型的结构进行打印输出,结果如下:
Sequential(
(0): Linear(in_features=10000, out_features=100, bias=True)
(1): ReLU()
(2): Linear(in_features=100, out_features=10, bias=True)
)
使用 orderdict
有序字典进行传入来搭建的模型代码如下:
import torch as t
from collections import OrderedDict
batch_n = 100
input_data = 10000
hidden_layer = 100
output_data = 10
x = t.randn(batch_n, input_data, requires_grad=False)
y = t.randn(batch_n, output_data, requires_grad=False)
models = t.nn.Sequential(OrderedDict([
("Line1", t.nn.Linear(input_data, hidden_layer)),
("ReLU", t.nn.ReLU()),
("Line2",t.nn.Linear(hidden_layer, output_data))
]))
print(models)
输出结果如下:
Sequential(
(Line1): Linear(in_features=10000, out_features=100, bias=True)
(ReLU): ReLU()
(Line2): Linear(in_features=100, out_features=10, bias=True)
)
通过对这两种方式进行比较,我们会发现,对模块使用自定义的名称可让我们更便捷地找到模型中相应的模块并进行操作。
torch.nn.Linear
类用于定义模型的线性层,即完成前面提到的不同的层之间的线性变换。
torch.nn.Linear
类接收的参数有三个,分别是输入特征数、输出特征数和是否使用偏置,设置是否使用偏置的参数是一个布尔值,默认为 True
,即使用偏置。
在实际使用的过程中,我们只需将输入的特征数和输出的特征数传递给 torch.nn.Linear
类,就会自动生成对应维度的权重参数和偏置,对于生成的权重参数和偏置,我们的模型默认使用了一种比之前的简单随机方式更好的参数初始化方法。
根据我们搭建模型的输入、输出和层次结构需求,它的输入是在一个批次中包含 100 个特征数为 1000 的数据,最后得到 100 个特征数为 10 的输出数据,中间需要经过两次线性变换,所以要使用两个线性层,两个线性层的代码分别是
torch.nn.Linear(input_data,hidden_layer)
torch.nn.Linear(hidden_layer, output_data)
可看到,其代替了之前使用矩阵乘法方式的实现,代码更精炼、简洁。
torch.nn.ReLU
类属于非线性激活分类,在定义时默认不需要传入参数。
当然,在 torch.nn
包中还有许多非线性激活函数类可供选择,比如之前讲到的 PReLU
、LeakyReLU
、
Tanh
、Sigmoid
、Softmax
等。
在掌握 torch.nn.Sequential
、torch.nn.Linear
和 torch.nn.RelU
的使用方法后,快速搭建更复杂的多层神经网络模型变为可能,而且在整个模型的搭建过程中不需要对在模型中使用到的权重参数和偏置进行任何定义和初始化说明,因为参数已经完成了自动生成。