通过示例学习:连续 XOR
如果我们想在 PyTorch 中构建神经网络,可以使用 (with) 指定所有参数(权重矩阵、偏差向量),让 PyTorch 计算梯度,然后调整参数。但是,如果我们有很多参数,事情很快就会变得繁琐。在 PyTorch 中,有一个名为 Package,它使构建神经网络更加方便。Tensors
requires_grad=True
torch.nn
将介绍在 PyTorch 中训练神经网络可能需要的库和所有其他部分,并在一个简单但众所周知的示例 XOR 上使用一个简单的示例分类器。给定两个二进制输入x1和x2,则要预测的标签为1如果x1或x2是1而另一个是0,或者标签为0在所有其他情况下。这个例子因单个神经元(即线性分类器)无法学习这个简单的函数而出名。 因此,将学习如何构建一个可以学习此函数的小型神经网络。 为了更有趣,我们将 XOR 移动到连续空间,并在二进制输入上引入一些高斯噪声。期望的 XOR 数据集分离如下所示:
该包定义了一系列有用的类,如线性网络层、激活函数、损失函数等。完整列表可在此处找到。如果您需要某个网络层,请先查看包的文档,然后再自己编写该层,因为该包可能已经包含其代码。我们在下面导入它:torch.nn
[1]:
import torch.nn as nn
此外,还有 。它包含在网络层中使用的函数。这与定义它们的方式形成鲜明对比(更多内容见下文),并且实际上使用了 中的许多功能。因此,功能包在许多情况下都很有用,因此我们在这里也导入它。torch.nn
torch.nn.functional
torch.nn
nn.Modules
torch.nn
torch.nn.functional
[2]:
import torch.nn.functional as F
nn.模块
在 PyTorch 中,神经网络是由模块构建的。模块可以包含其他模块,神经网络本身也被视为一个模块。模块的基本模板如下:
[3]:
class MyModule(nn.Module):def __init__(self):super().__init__()# Some init for my moduledef forward(self, x):# Function for performing the calculation of the module.pass
forward 函数是进行模块计算的地方,并在您调用 module () 时执行。在 init 函数中,我们通常使用 来创建模块的参数,或者定义 forward 函数中使用的其他模块。向后计算是自动完成的,但如果需要,也可以被覆盖。nn = MyModule(); nn(x)
nn.Parameter
简单分类器
我们现在可以使用包中的预定义模块,并定义我们自己的小型神经网络。我们将使用一个最小网络,其中包含一个输入层、一个以 tanh 作为激活函数的隐藏层和一个输出层。换句话说,我们的网络应该看起来像这样:torch.nn
输入神经元以蓝色显示,表示坐标和数据点。包括 tanh 激活在内的隐藏神经元显示为白色,输出神经元显示为红色。在 PyTorch 中,我们可以按如下方式定义它:x1x2
[4]:
class SimpleClassifier(nn.Module):def __init__(self, num_inputs, num_hidden, num_outputs):super().__init__()# Initialize the modules we need to build the networkself.linear1 = nn.Linear(num_inputs, num_hidden)self.act_fn = nn.Tanh()self.linear2 = nn.Linear(num_hidden, num_outputs)def forward(self, x):# Perform the calculation of the model to determine the predictionx = self.linear1(x)x = self.act_fn(x)x = self.linear2(x)return x
对于本笔记本中的示例,我们将使用一个具有两个输入神经元和四个隐藏神经元的微型神经网络。当我们执行二元分类时,我们将使用单个输出神经元。请注意,我们还没有对输出应用 sigmoid。这是因为其他函数(尤其是 loss)在原始输出上计算时比在 sigmoid 输出上计算时更有效、更精确。我们稍后会讨论详细原因。
[5]:
model = SimpleClassifier(num_inputs=2, num_hidden=4, num_outputs=1)
# Printing a module shows all its submodules
print(model)
SimpleClassifier((linear1): Linear(in_features=2, out_features=4, bias=True)(act_fn): Tanh()(linear2): Linear(in_features=4, out_features=1, bias=True)
)
打印模型会列出它包含的所有子模块。可以使用模块的函数获取模块的参数,也可以获取每个参数对象的名称。对于我们的小型神经网络,我们有以下参数:parameters()
named_parameters()
[6]:
for name, param in model.named_parameters():print(f"Parameter {name}, shape {param.shape}")
Parameter linear1.weight, shape torch.Size([4, 2])
Parameter linear1.bias, shape torch.Size([4])
Parameter linear2.weight, shape torch.Size([1, 4])
Parameter linear2.bias, shape torch.Size([1])
每个线性层都有一个形状为 的权重矩阵和一个形状为 的偏置。tanh 激活函数没有任何参数。请注意,仅为作为直接对象属性的对象注册参数,即 .如果定义模块列表,则这些模块的参数不会注册到外部模块,并且在尝试优化模块时可能会导致一些问题。有一些替代方案,如 , 和 ,允许您拥有不同的模块数据结构。我们将在后面的一些教程中使用它们,并在那里解释它们。[output, input]
[output]
nn.Module
self.a = ...
nn.ModuleList
nn.ModuleDict
nn.Sequential