在深度学习领域,PyTorch 凭借其灵活的动态计算图特性,成为众多研究者和开发者的首选框架。它提供了丰富的神经网络工具,能帮助我们快速构建、训练和优化各类模型。
一、神经网络核心组件
要构建一个完整的神经网络,首先得了解其核心组成部分,它们就像搭建房屋的砖块,共同支撑起整个网络结构。
|------|-----------------------------------------------------------------------|
| 组件 | 作用 |
| 层 | 神经网络的基本结构,负责将输入张量转换为输出张量,是数据变换的核心单元,比如全连接层、卷积层等 |
| 模型 | 由多个层按照一定逻辑组合而成的网络结构,能够实现特定的学习任务,如分类、回归等 |
| 损失函数 | 参数学习的目标函数,用于衡量模型预测值与真实值之间的差距,常见的有交叉熵损失函数、均方误差损失函数等,我们通过最小化损失函数来优化模型参数 |
| 优化器 | 用于实现损失函数最小化的工具,它会根据损失函数的梯度信息调整模型参数,常用的优化器有 Adam、SGD 等 |
这四大组件紧密配合,层构成模型的基础架构,损失函数提供优化方向,优化器则负责朝着这个方向调整参数,最终让模型具备学习和预测能力。
二、构建神经网络的主要工具
PyTorch 提供了两种核心工具来构建神经网络,分别是nn.Module和nn.functional,它们各有特点,适用于不同的场景。
(一)nn.Module
nn.Module是 PyTorch 中构建神经网络的核心基类,具有以下优势:
- 继承自Module类的网络模型,能够自动提取可学习的参数,无需手动管理,大大简化了代码编写。
- 适用于构建卷积层(nn.Conv2d)、全连接层(nn.Linear)、dropout 层(nn.Dropout)等带有可学习参数的层结构。
- 在模型训练和测试状态切换时非常方便,比如对于 dropout 操作,调用model.eval()后能自动实现状态转换,避免在测试时进行随机失活。
(二)nn.functional
nn.functional更像是一组纯函数的集合,其特点如下:
- 不具备自动管理参数的功能,如果使用nn.functional中的函数实现带有参数的层(如线性层),需要自己定义和管理weight、bias等参数,每次调用时都要手动传入,不利于代码复用。
- 适用于激活函数(如nn.functional.relu)、池化层(如nn.functional.max_pool2d)等无参数或参数无需学习的操作。
- 无法与nn.Sequential(一种按顺序堆叠层的容器)结合使用,在构建顺序性网络结构时灵活性较低。
(三)两者区别对比
|---------------------|--------------------------|-----------------|
| 对比维度 | nn.Module | nn.functional |
| 参数管理 | 自动提取和管理可学习参数 | 需手动定义和传入参数 |
| 使用方式 | 先实例化并传入参数,再以函数调用方式传入数据 | 直接调用函数并传入数据和参数 |
| 与 nn.Sequential 兼容性 | 可很好结合使用 | 无法结合使用 |
| 状态切换 | 调用model.eval()等方法可自动切换状态 | 无自动状态切换功能,需手动处理 |
三、模型构建的三种常用方法
掌握了构建神经网络的核心工具后,我们来看看具体的模型构建方法。PyTorch 提供了多种构建模型的方式,以下是三种常用方法的详细介绍。
(一)继承 nn.Module 基类构建模型
这种方法是最灵活、最常用的模型构建方式,适用于构建复杂的自定义网络结构。具体步骤如下:
- 定义一个继承自nn.Module的模型类。
- 在__init__方法中定义网络的各个层结构,如全连接层、批量归一化层等。
-
实现forward方法,定义数据在网络中的前向传播路径。
import torch
from torch import nn
import torch.nn.functional as F
class Model_Seq(nn.Module):# 通过继承基类nn.Module来构建模型
def init(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Model_Seq, self).init()
self.flatten = nn.Flatten() # 展平层,将二维图像数据展平为一维向量
self.linear1 = nn.Linear(in_dim, n_hidden_1) # 第一个全连接层
self.bn1 = nn.BatchNorm1d(n_hidden_1) # 第一个批量归一化层
self.linear2 = nn.Linear(n_hidden_1, n_hidden_2) # 第二个全连接层
self.bn2 = nn.BatchNorm1d(n_hidden_2) # 第二个批量归一化层
self.out = nn.Linear(n_hidden_2, out_dim) # 输出层
def forward(self, x):定义前向传播过程
x = self.flatten(x) # 展平输入数据
x = self.linear1(x) # 经过第一个全连接层
x = self.bn1(x) # 批量归一化处理
x = F.relu(x) # ReLU激活函数
x = self.linear2(x) # 经过第二个全连接层
x = self.bn2(x) # 批量归一化处理
x = F.relu(x) # ReLU激活函数
x = self.out(x) # 经过输出层
x = F.softmax(x, dim=1) # softmax函数,得到类别概率分布
return x对一些超参数赋值
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10model_seq = Model_Seq(in_dim, n_hidden_1, n_hidden_2, out_dim)
print(model_seq)
运行上述代码,会输出模型的结构信息,清晰展示各层的类型和参数:
Model_Seq(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear1): Linear(in_features=784, out_features=300, bias=True)
(bn1): BatchNorm1d(300, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(linear2): Linear(in_features=300, out_features=100, bias=True)
(bn2): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(out): Linear(in_features=100, out_features=10, bias=True)
)
(二)使用 nn.Sequential 按层顺序构建模型
nn.Sequential是一个按顺序堆叠层的容器,适用于构建结构简单、层与层之间按顺序连接的网络模型。它提供了三种常用的构建方式:
1. 利用可变参数
这种方式直接将各层作为可变参数传入nn.Sequential,代码简洁,但无法给每个层指定名称。
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
Seq_arg = nn.Sequential(
nn.Flatten(),
nn.Linear(in_dim, n_hidden_1),
nn.BatchNorm1d(n_hidden_1),
nn.ReLU(),
nn.Linear(n_hidden_1, n_hidden_2),
nn.BatchNorm1d(n_hidden_2),
nn.ReLU(),
nn.Linear(n_hidden_2, out_dim),
nn.Softmax(dim=1)
)
print(Seq_arg)
2. 使用 add_module 方法
如果需要给每个层指定名称,可以使用add_module方法,通过 "名称 - 层" 的键值对形式添加层。
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
Seq_module = nn.Sequential()
Seq_module.add_module("flatten", nn.Flatten())
Seq_module.add_module("linear1", nn.Linear(in_dim, n_hidden_1))
Seq_module.add_module("bn1", nn.BatchNorm1d(n_hidden_1))
Seq_module.add_module("relu1", nn.ReLU())
Seq_module.add_module("linear2", nn.Linear(n_hidden_1, n_hidden_2))
Seq_module.add_module("bn2", nn.BatchNorm1d(n_hidden_2))
Seq_module.add_module("relu2", nn.ReLU())
Seq_module.add_module("out", nn.Linear(n_hidden_2, out_dim))
Seq_module.add_module("softmax", nn.Softmax(dim=1))
print(Seq_module)
3. 使用 OrderedDict 方法
借助collections.OrderedDict可以创建有序的字典,将层名称和层对象作为键值对传入,既能指定层名称,又能保证层的顺序。
from collections import OrderedDict
import torch
from torch import nn
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
layers = OrderedDict([
("flatten", nn.Flatten()),
("linear1", nn.Linear(in_dim, n_hidden_1)),
("bn1", nn.BatchNorm1d(n_hidden_1)),
("relu1", nn.ReLU()),
("linear2", nn.Linear(n_hidden_1, n_hidden_2)),
("bn2", nn.BatchNorm1d(n_hidden_2)),
("relu2", nn.ReLU()),
("out", nn.Linear(n_hidden_2, out_dim)),
("softmax", nn.Softmax(dim=1))
])
Seq_ordered = nn.Sequential(layers)
print(Seq_ordered)
上述三种方式构建的模型结构相同,以add_module方法为例,输出结果如下:
Sequential(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear1): Linear(in_features=784, out_features=300, bias=True)
(bn1): BatchNorm1d(300, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU()
(linear2): Linear(in_features=300, out_features=100, bias=True)
(bn2): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU()
(out): Linear(in_features=100, out_features=10, bias=True)
(softmax): Softmax(dim=1)
)
(三)继承 nn.Module 基类并应用模型容器构建模型
这种方法结合了nn.Module的灵活性和模型容器的便捷性,适用于构建复杂且具有一定模块化结构的网络。常用的模型容器除了nn.Sequential,还有nn.ModuleList和nn.ModuleDict。
1. 使用 nn.Sequential 模型容器
在继承nn.Module的模型类中,将部分层组合成nn.Sequential模块,使模型结构更清晰,便于管理和维护。
import torch
from torch import nn
import torch.nn.functional as F
class Model_lay(nn.Module):
# 使用sequential构建网络,Sequential()函数的功能是将网络的层组合到一起
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Model_lay, self).__init__()
self.flatten = nn.Flatten()
# 将全连接层和批量归一化层组合成一个Sequential模块
self.layer1 = nn.Sequential(nn.Linear(in_dim, n_hidden_1), nn.BatchNorm1d(n_hidden_1))
self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2), nn.BatchNorm1d(n_hidden_2))
self.out = nn.Sequential(nn.Linear(n_hidden_2, out_dim))
def forward(self, x):
x = self.flatten(x)
x = F.relu(self.layer1(x)) # 对layer1的输出应用ReLU激活函数
x = F.relu(self.layer2(x)) # 对layer2的输出应用ReLU激活函数
x = F.softmax(self.out(x), dim=1) # 对输出层的结果应用softmax函数
return x
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
model_lay = Model_lay(in_dim, n_hidden_1, n_hidden_2, out_dim)
print(model_lay)
运行结果展示了模型的模块化结构:
Model_lay(
(flatten): Flatten(start_dim=1, end_dim=-1)
(layer1): Sequential(
(0): Linear(in_features=784, out_features=300, bias=True)(1): BatchNorm1d(300, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(layer2): Sequential(
(0): Linear(in_features=300, out_features=100, bias=True)
(1): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(out): Sequential(
(0): Linear(in_features=100, out_features=10, bias=True)
)
)
2. 使用 nn.ModuleList 模型容器
nn.ModuleList是一个存储模块的列表,支持索引访问,适用于构建层结构相似但数量不确定的网络。在forward方法中,可以通过循环遍历nn.ModuleList中的层进行前向传播。
import torch
from torch import nn
import torch.nn.functional as F
class Model_lst(nn.Module):
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Model_lst, self).__init__()
# 将各层存储在ModuleList中
self.layers = nn.ModuleList([
nn.Flatten(),
nn.Linear(in_dim, n_hidden_1),
nn.BatchNorm1d(n_hidden_1),
nn.ReLU(),
nn.Linear(n_hidden_1, n_hidden_2),
nn.BatchNorm1d(n_hidden_2),
nn.ReLU(),
nn.Linear(n_hidden_2, out_dim),
nn.Softmax(dim=1)
])
def forward(self, x):
# 循环遍历ModuleList中的层,进行前向传播
for layer in self.layers:
x = layer(x)
return x
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
model_lst = Model_lst(in_dim, n_hidden_1, n_hidden_2, out_dim)
print(model_lst)
输出结果展示了nn.ModuleList中存储的各层信息:
Model_lst((layers): ModuleList(
(0): Flatten(start_dim=1, end_dim=-1)
(1): Linear(in_features=784, out_features=300, bias=True)
(2): BatchNorm1d(300, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(3): ReLU()
(4): Linear(in_features=300, out_features=100, bias=True)
(5): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU()
(7): Linear(in_features=100, out_features=10, bias=True)
(8): Softmax(dim=1)
)
3. 使用 nn.ModuleDict 模型容器
nn.ModuleDict是一个存储模块的字典,通过键值对的形式管理层,适用于需要根据条件动态选择不同层路径的网络。在forward方法中,根据预先定义的层顺序列表,通过键名获取对应的层进行前向传播。
import torch
from torch import nn
import torch.nn.functional as F
class Model_dict(nn.Module):
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Model_dict, self).__init__()# 将各层以键值对的形式存储在ModuleDict中
.layers_dict = nn.ModuleDict({
"flatten": nn.Flatten(),
"linear1": nn.Linear(in_dim, n_hidden_1),
"bn1": nn.BatchNorm1d(n_hidden_1),
"relu": nn.ReLU(),
"linear2": nn.Linear(n_hidden_1, n_hidden_2),
"bn2": nn.BatchNorm1d(n_hidden_2),
"out": nn.Linear(n_hidden_2, out_dim),
"softmax": nn.Softmax(dim=1)
})
def forward(self, x):
# 定义层的执行顺序
layers = ["flatten", "linear1", "bn1", "relu", "linear2", "bn2", "relu", "out", "softmax"]
# 根据顺序从ModuleDict中获取层并进行前向传播
for layer in layers:
x = self.layers_dict[layer](x)
return x
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
model_dict = Model_dict(in_dim, n_hidden_1, n_hidden_2, out_dim)
print(model_dict)
运行结果展示了nn.ModuleDict中的层结构:
Model_dict(
(layers_dict): ModuleDict(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear1): Linear(in_features=784, out_features=300, bias=True)
(bn1): BatchNorm1d(300, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU()
(linear2): Linear(in_features=300, out_features=100, bias=True)
(bn2): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(out): Linear(in_features=100, out_features=10, bias=True)
(softmax): Softmax(dim=1)
)
)
四、自定义网络模块
在实际应用中,标准的网络层可能无法满足特定需求,这时就需要自定义网络模块。以残差网络(ResNet)中的残差块为例,我们来介绍如何实现自定义网络模块。
(一)基本残差块(RestNetBasicBlock)
基本残差块适用于输入和输出特征图尺寸、通道数相同的情况,它将输入与经过两层卷积和批量归一化处理后的输出相加,然后应用 ReLU 激活函数,能够有效缓解深层网络中的梯度消失问题。
import torch
import torch.nn as nn
from torch.nn import functional as F
class RestNetBasicBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride):
super(RestNetBasicBlock, self).__init__()
# 第一个卷积层
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
self.bn1 = nn.BatchNorm2d(out_channels) # 批量归一化层
# 第二个卷积层
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride, padding=1)
self.bn2 = nn.BatchNorm2d(out_channels) # 批量归一化层
def forward(self, x):
output = self.conv1(x)
output = F.relu(self.bn1(output)) # 卷积后进行批量归一化和激活
output = self.conv2(output)
output = self.bn2(output) # 卷积后进行批量归一化
return F.relu(x + output) # 输入与输出相加后应用激活函数
(二)下采样残差块(RestNetDownBlock)
当下采样操作导致输入和输出特征图尺寸或通道数不同时,需要使用下采样残差块。它通过 1×1 卷积层调整输入的通道数和分辨率,使输入与经过两层卷积处理后的输出能够进行相加操作。
import torch
import torch.nn as nn
from torch.nn import functional as F
class RestNetDownBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride):
super(RestNetDownBlock, self).__init__()
# 第一个卷积层,用于下采样
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride[0], padding=1)
self.bn1 = nn.BatchNorm2d(out_channels) # 批量归一化层
# 第二个卷积层
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride[1], padding=1)
self.bn2 = nn.BatchNorm2d(out_channels) # 批量归一化层
# 1×1卷积层,用于调整输入的通道数和分辨率
self.extra = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride[0], padding=0),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
extra_x = self.extra(x) # 对输入进行调整output = self.conv1(x)
out = F.relu(self.bn1(output)) # 卷积后进行批量归一化和激活
out = self.conv2(out)
out = self.bn2(out) # 卷积后进行批量归一化
return F.relu(extra_x + out) # 调整后的输入与输出相加后应用激活函数
(三)组合残差块构建 ResNet18
通过组合基本残差块和下采样残差块,可以构建出经典的 ResNet18 网络结构。ResNet18 包含一个初始卷积层、四个残差块组(每个组由多个残差块组成)、一个平均池化层和一个全连接层。
import torch
import torch.nn as nn
from torch.nn import functional as F
class RestNet18(nn.Module):
def __init__(self):
super(RestNet18, self).__init__()
# 初始卷积层
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
self.bn1 = nn.BatchNorm2d(64) # 批量归一化层
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # 最大池化层
# 第一个残差块组,包含两个基本残差块
self.layer1 = nn.Sequential(
RestNetBasicBlock(64, 64, 1),
RestNetBasicBlock(64, 64, 1)
)
# 第二个残差块组,包含一个下采样残差块和一个基本残差块
self.layer2 = nn.Sequential(
RestNetDownBlock(64, 128, [2, 1]),
RestNetBasicBlock(128, 128, 1)
)
# 第三个残差块组,包含一个下采样残差块和一个基本残差块
self.layer3 = nn.Sequential(
RestNetDownBlock(128, 256, [2, 1]),
RestNetBasicBlock(256, 256, 1)
)
# 第四个残差块组,包含一个下采样残差块和一个基本残差块
self.layer4 = nn.Sequential(
RestNetDownBlock(256, 512, [2, 1]),
RestNetBasicBlock(512, 512, 1)
)
self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1)) # 自适应平均池化层
self.fc = nn.Linear(512, 10) # 全连接层,输出10个类别
def forward(self, x):
out = self.conv1(x)
out = self.bn1(out) # 批量归一化
out = self.maxpool(out) # 最大池化
out = self.layer1(out) # 经过第一个残差块组
out = self.layer2(out) # 经过第二个残差块组
out = self.layer3(out) # 经过第三个残差块组
out = self.layer4(out) # 经过第四个残差块组
out = self.avgpool(out) # 自适应平均池化
out = out.reshape(x.shape[0], -1) # 展平输出
out = self.fc(out) # 经过全连接层
return out
五、模型训练完整流程
构建好模型后,就进入模型训练阶段。模型训练是一个迭代优化的过程,主要包括以下六个步骤:
(一)加载预处理数据集
首先需要加载已经预处理好的数据集,通常会将数据集分为训练集和测试集(或验证集),并使用DataLoader进行批量加载,方便后续的迭代训练。
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 定义数据预处理变换
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.5,), (0.5,)) # 标准化处理
])
# 加载训练集和测试集
train_dataset = datasets.MNIST(
root='./data', train=True, download=True, transform=transform
)
test_dataset = datasets.MNIST(
root='./data', train=False, download=True, transform=transform
)
# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
(二)定义损失函数
根据具体的任务类型选择合适的损失函数。例如,分类任务常用交叉熵损失函数(nn.CrossEntropyLoss),回归任务常用均方误差损失函数(nn.MSELoss)。
import torch.nn as nn
# 定义交叉熵损失函数,适用于分类任务
criterion = nn.CrossEntropyLoss()
(三)定义优化方法
选择合适的优化器来更新模型参数,常用的优化器有 Adam、SGD 等。这里以 Adam 优化器为例,需要传入模型的可学习参数和学习率等超参数。
import torch.optim as optim
# 假设我们使用之前定义的Model_Seq模型
in_dim, n_hidden_1, n_hidden_2, out_dim = 28 * 28, 300, 100, 10
model = Model_Seq(in_dim, n_hidden_1, n_hidden_2, out_dim)
# 定义Adam优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)
(四)循环训练模型
在训练循环中,每次从训练集中获取一个批次的数据,将数据输入模型进行前向传播得到预测结果,计算损失函数值,然后通过反向传播计算梯度,最后使用优化器更新模型参数。
# 设置训练轮数
epochs = 5
# 循环训练
for epoch in range(epochs):
model.train() # 将模型设置为训练模式running_loss = 0.0 # 记录累计损失
for i, (inputs, labels) in enumerate(train_loader, 0):
# 清零梯度optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()# 累计损失
running_loss += loss.item()
# 每100个批次打印一次损失信息
if i % 100 == 99:
print(f'[{epoch + 1}, {i + 1}] loss: {running_loss / 100:.3f}')
running_loss = 0.0
print('Finished Training')
(五)循环测试或验证模型
在每个训练轮次结束后,需要在测试集(或验证集)上评估模型的性能,检查模型是否过拟合或欠拟合。在测试过程中,要将模型设置为评估模式(model.eval()),关闭 dropout 等随机操作。
# 在测试集上评估模型
model.eval() # 将模型设置为评估模式
correct = 0 # 记录正确预测的数量
total = 0 # 记录总样本数量# 关闭梯度计算,节省内存和计算资源
with torch.no_grad():for inputs, labels in test_loader:
outputs = model(inputs)
# 获取预测结果(概率最大的类别)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
# 计算测试集准确率
accuracy = 100 * correct / total
print(f'Accuracy of the network on the 10000 test images: {accuracy:.2f}%')
(六)可视化结果
为了更直观地了解模型的训练过程和性能,通常会对训练损失、测试准确率等指标进行可视化。可以使用 Matplotlib 等绘图库绘制训练损失曲线和测试准确率曲线。
import matplotlib.pyplot as plt
import numpy as np
# 假设我们在训练过程中记录了每轮的训练损失和测试准确率
train_losses = [1.2, 0.8, 0.5, 0.3, 0.2] # 示例训练损失数据
test_accs = [65.2, 78.5, 85.3, 90.1, 92.5] # 示例测试准确率数据
epochs_range = np.arange(1, len(train_losses) + 1)
# 绘制训练损失曲线
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, train_losses, 'b-', label='Training Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.legend()
# 绘制测试准确率曲线
plt.subplot(1, 2, 2)
plt.plot(epochs_range, test_accs, 'r-', label='Test Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy (%)')
plt.title('Test Accuracy Curve')
plt.legend()
plt.tight_layout()
plt.savefig('training_results.png', dpi=300)
plt.close()
六、总结
本文详细介绍了 PyTorch 神经网络工具箱的核心内容,从神经网络的四大核心组件,到nn.Module和nn.functional两种构建工具,再到三种常用的模型构建方法,以及自定义网络模块的实现和完整的模型训练流程。
在实际应用中,我们需要根据具体的任务需求选择合适的模型构建方法和网络结构。对于简单的顺序性网络,nn.Sequential是不错的选择;对于复杂的自定义网络,继承nn.Module基类并结合模型容器能提供更高的灵活性;而当需要解决深层网络梯度消失问题时,残差块等自定义模块则能发挥重要作用。
通过掌握这些知识,相信大家能够熟练使用 PyTorch 构建和训练各类神经网络模型,为深度学习项目的开发打下坚实的基础。