构建神经网络#

神经网络由执行数据运算的层/模块组成。PyTorch 的 torch.nn 命名空间提供了构建自定义神经网络所需的所有基本构件。在 PyTorch 中,每个模块都是 torch.nn.Module 类的子类。神经网络本身就是模块,它包含其他模块(层)。这种嵌套结构使得构建和管理复杂的架构变得简单。

在接下来的部分中,将构建神经网络来对 FashionMNIST 数据集中的图像进行分类。

import torch
from torch import nn

获取训练设备#

希望能够在硬件加速器上训练模型,例如 GPU 或 MPS(如果可用的话)。检查torch.cudatorch.backends.mps 是否可用,否则将使用 CPU。

device = (
    "cuda"
    if torch.cuda.is_available()
    else "mps"
    if torch.backends.mps.is_available()
    else "cpu"
)
print(f"Using {device} device")
Using cuda device

定义模型类#

通过子类化 torch.nn.Module 定义神经网络,并在 __init__ 中初始化神经网络层。每个 torch.nn.Module 子类都在 forward() 方法中实现对输入数据的操作。

class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

实例化 NeuralNetwork 对象,并将其转移到指定的设备上,然后打印出它的结构。

model = NeuralNetwork().to(device)
print(model)
NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)

为了使用这个模型,向其传递输入数据。这会执行模型的 forward 方法以及一些后台操作。请勿直接调用 model.forward()

对输入调用模型会返回二维张量,其中 dim=0 对应每个类别的 10 个原始预测值,而 dim=1 对应每个输出的个别值。通过将结果传递给 torch.nn.Softmax 模块的实例,可以获得预测概率。

X = torch.rand(1, 28, 28, device=device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")
Predicted class: tensor([3], device='cuda:0')

模型层#

详细解析 FashionMNIST 模型中的各层结构。为了说明这一点,取包含 3 张尺寸为 \(28 \times 28\) 的图片的样本小批量,观察它在通过网络传递时的变化情况。

input_image = torch.rand(3,28,28)
print(input_image.size())
torch.Size([3, 28, 28])

torch.nn.Flatten#

初始化了 torch.nn.Flatten 层,将每个 2D 的 \(28 \times 28\) 图像转换为连续的 \(784\) 像素值数组(保持小批量维度(在 dim=0 处))。

flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
torch.Size([3, 784])

torch.nn.Linear#

torch.nn.Linear 类是模块,它通过使用其存储的权重和偏置对输入执行线性变换。

layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
torch.Size([3, 20])

torch.nn.ReLU#

非线性激活函数是创建模型输入和输出之间复杂映射的关键。它们在线性变换后被应用,以引入 非线性,帮助神经网络学习各种现象。

在这个模型中,在线性层之间使用了torch.nn.ReLU,但在你的模型中也可以使用其他激活函数来引入非线性。

print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
Before ReLU: tensor([[-0.0085, -0.4126, -0.6079,  0.5879,  0.0750,  0.1361, -0.3046, -0.1737,
         -0.0712,  0.5034, -0.0387, -0.2278,  0.1566,  0.3071,  0.2590,  0.1370,
          0.5073, -0.2476,  0.1308, -0.2432],
        [-0.0887, -0.2951, -0.4139,  0.7143,  0.1112,  0.0950, -0.1483, -0.2337,
          0.4364,  0.2873, -0.2216,  0.1861,  0.0769,  0.6134,  0.0090, -0.1608,
          0.2328, -0.1586,  0.1625, -0.1524],
        [-0.2031, -0.4172, -0.0024,  0.6745,  0.0174,  0.0600, -0.3286, -0.3755,
         -0.0468,  0.2978,  0.1457,  0.0322,  0.1065,  0.2916,  0.2393,  0.1474,
          0.2674, -0.0331, -0.1958, -0.2776]], grad_fn=<AddmmBackward0>)


After ReLU: tensor([[0.0000, 0.0000, 0.0000, 0.5879, 0.0750, 0.1361, 0.0000, 0.0000, 0.0000,
         0.5034, 0.0000, 0.0000, 0.1566, 0.3071, 0.2590, 0.1370, 0.5073, 0.0000,
         0.1308, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.7143, 0.1112, 0.0950, 0.0000, 0.0000, 0.4364,
         0.2873, 0.0000, 0.1861, 0.0769, 0.6134, 0.0090, 0.0000, 0.2328, 0.0000,
         0.1625, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.6745, 0.0174, 0.0600, 0.0000, 0.0000, 0.0000,
         0.2978, 0.1457, 0.0322, 0.1065, 0.2916, 0.2393, 0.1474, 0.2674, 0.0000,
         0.0000, 0.0000]], grad_fn=<ReluBackward0>)

torch.nn.Sequential#

torch.nn.Sequential 是有序的模块容器。数据按照定义的顺序通过所有模块。你可以使用顺序容器来快速构建网络,例如 seq_modules

seq_modules = nn.Sequential(
    flatten,
    layer1,
    nn.ReLU(),
    nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)

torch.nn.Softmax#

神经网络的最后一层线性层返回的是 logits

  • 原始值在 \([-\infty, \infty]\)

  • 这些值被传递到 torch.nn.Softmax 模块。Logits 被缩放到 \([0, 1]\) 的值,代表了模型预测的每个类别的概率。dim 参数 指明了沿着哪个维度的值必须加起来等于1。

softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)

模型参数#

神经网络内部的许多层是 参数化的,即它们具有在训练过程中优化的权重和偏差。继承自 torch.nn.Module 会自动跟踪在模型对象内定义的所有字段,并允许通过模型的 torch.nn.Module.parameters()torch.nn.Module.named_parameters() 方法访问所有参数。

在这个例子中,遍历每个参数,并打印其大小和值的预览。

print(f"Model structure: {model}\n\n")

for name, param in model.named_parameters():
    print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
Hide code cell output
Model structure: NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)


Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[-0.0125, -0.0153, -0.0050,  ...,  0.0256,  0.0327, -0.0276],
        [-0.0291, -0.0186,  0.0210,  ...,  0.0051, -0.0063, -0.0190]],
       device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0187,  0.0044], device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[ 0.0139,  0.0424,  0.0399,  ...,  0.0227, -0.0321,  0.0262],
        [ 0.0315,  0.0093,  0.0373,  ...,  0.0438, -0.0198, -0.0038]],
       device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([0.0072, 0.0110], device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[-0.0253,  0.0345, -0.0368,  ..., -0.0376,  0.0236,  0.0162],
        [-0.0369, -0.0032, -0.0409,  ...,  0.0346,  0.0208, -0.0115]],
       device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([-0.0218, -0.0049], device='cuda:0', grad_fn=<SliceBackward0>)