构建神经网络#
神经网络由执行数据运算的层/模块组成。PyTorch 的 torch.nn
命名空间提供了构建自定义神经网络所需的所有基本构件。在 PyTorch 中,每个模块都是 torch.nn.Module
类的子类。神经网络本身就是模块,它包含其他模块(层)。这种嵌套结构使得构建和管理复杂的架构变得简单。
在接下来的部分中,将构建神经网络来对 FashionMNIST 数据集中的图像进行分类。
import torch
from torch import nn
获取训练设备#
希望能够在硬件加速器上训练模型,例如 GPU 或 MPS(如果可用的话)。检查torch.cuda 或 torch.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, *args, **kwargs):
super().__init__(*args, **kwargs)
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 张尺寸为
input_image = torch.rand(3,28,28)
print(input_image.size())
torch.Size([3, 28, 28])
torch.nn.Flatten
#
初始化了 torch.nn.Flatten
层,将每个 2D 的 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.4968, -0.1883, 0.3967, -0.3151, 0.1528, 0.0195, -0.3309, -0.0788,
-0.3464, 0.0711, -0.4967, -0.0456, -0.0560, -0.1457, -0.1733, -0.0953,
-0.0309, -0.5995, 0.2890, -0.0185],
[ 0.3921, -0.2639, 0.6610, -0.4670, 0.0660, -0.0015, -0.1707, -0.0268,
-0.2920, -0.0329, -0.5934, 0.0480, -0.4621, 0.3773, 0.2170, 0.4392,
0.1168, -0.7666, 0.1065, 0.1997],
[ 0.3515, -0.2565, 0.1049, -0.7943, 0.4229, 0.4230, -0.0615, 0.1683,
0.0265, -0.0217, -0.3115, -0.1737, -0.8239, -0.0935, 0.1612, 0.1222,
0.0991, -0.7683, 0.0792, -0.0179]], grad_fn=<AddmmBackward0>)
After ReLU: tensor([[0.4968, 0.0000, 0.3967, 0.0000, 0.1528, 0.0195, 0.0000, 0.0000, 0.0000,
0.0711, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.2890, 0.0000],
[0.3921, 0.0000, 0.6610, 0.0000, 0.0660, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0480, 0.0000, 0.3773, 0.2170, 0.4392, 0.1168, 0.0000,
0.1065, 0.1997],
[0.3515, 0.0000, 0.1049, 0.0000, 0.4229, 0.4230, 0.0000, 0.1683, 0.0265,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1612, 0.1222, 0.0991, 0.0000,
0.0792, 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
原始值在
这些值被传递到
torch.nn.Softmax
模块。Logits 被缩放到 的值,代表了模型预测的每个类别的概率。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")
Show 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.0009, 0.0338, 0.0005, ..., 0.0281, 0.0350, -0.0257],
[ 0.0243, 0.0071, -0.0264, ..., -0.0101, 0.0237, -0.0287]],
device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0017, 0.0055], device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[ 0.0172, -0.0336, 0.0070, ..., -0.0030, -0.0105, -0.0290],
[-0.0240, -0.0079, 0.0390, ..., 0.0356, 0.0065, -0.0325]],
device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([-0.0094, -0.0016], device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[-0.0058, -0.0389, 0.0117, ..., -0.0051, 0.0418, -0.0430],
[-0.0390, -0.0202, 0.0230, ..., 0.0231, 0.0339, 0.0055]],
device='cuda:0', grad_fn=<SliceBackward0>)
Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([ 0.0438, -0.0161], device='cuda:0', grad_fn=<SliceBackward0>)