当前位置: 首页 > news >正文

PyTorch 神经网络工具箱

在深度学习领域,PyTorch 凭借其动态计算图、简洁的 API 设计和强大的灵活性,成为科研与工程领域的热门框架。无论是搭建基础神经网络,还是实现复杂的自定义模块(如残差网络),PyTorch 都能提供高效的支持。本文将基于 PyTorch 核心知识点,从神经网络核心组件模型构建工具三种模型构建方法自定义网络模块完整训练流程,手把手带您掌握 PyTorch 构建神经网络的全流程。

一、神经网络核心组件:理解深度学习的 “积木”

神经网络的本质是通过 “层” 对数据进行变换,结合 “损失函数” 和 “优化器” 实现参数学习。这四大核心组件共同构成了深度学习模型的基础,其关系与作用如下表所示:

核心组件作用与说明
层(Layer)神经网络的基本结构单元,负责将输入张量(如图片、文本特征)通过权重变换为输出张量。常见层包括全连接层(Linear)、卷积层(Conv2d)、池化层(MaxPool2d)等。
模型(Model)由多个 “层” 按特定逻辑组合而成的网络结构,定义了数据从输入到输出的完整变换路径(如 CNN、RNN、Transformer)。
损失函数(Loss Function)参数学习的 “目标标尺”,量化模型预测值(Y')与真实值(Y)的差异。通过最小化损失函数,引导模型调整权重。常见损失函数:CrossEntropyLoss(分类)、MSELoss(回归)。
优化器(Optimizer)实现 “损失最小化” 的工具,通过反向传播计算的梯度,更新模型的可学习参数(如权重、偏置)。常见优化器:SGD(随机梯度下降)、Adam、RMSprop。

组件工作流程示意图

数据在组件间的流转逻辑可概括为:

  1. 输入数据(X)经过的变换,得到模型预测值(Y');
  2. 损失函数计算 Y' 与真实标签(Y)的差异(Loss);
  3. 优化器根据 Loss 的梯度,反向更新 “层” 中的权重参数;
  4. 重复上述步骤,直到 Loss 收敛到最小值。

二、PyTorch 模型构建核心工具:nn.Module vs nn.functional

PyTorch 提供了两种核心工具用于构建网络:nn.Module 和 nn.functional。两者功能有重叠,但设计理念和适用场景差异显著,掌握其区别是高效构建模型的关键。

1. 工具特性对比

对比维度nn.Modulenn.functional
本质面向对象的类(需实例化)纯函数(直接调用)
参数管理自动定义、存储和管理可学习参数(如 weight、bias)需手动定义和传入参数,无自动管理
与容器结合支持与nn.Sequential等容器结合,简化代码不支持与容器结合,需手动串联层
状态切换(如 Dropout)调用model.eval()后自动关闭 Dropout(测试模式)需手动传入training=True/False参数控制状态
适用场景卷积层、全连接层、Dropout 层等需参数管理的组件激活函数(ReLU、Sigmoid)、池化层等无参数组件

2. 代码示例:直观理解差异

以 “全连接层 + ReLU 激活” 为例,对比两种工具的使用方式:

(1)使用nn.Module
import torch
import torch.nn as nn# 1. 实例化层(自动管理weight和bias)
linear = nn.Linear(in_features=10, out_features=5)  # 全连接层
relu = nn.ReLU()  # 激活函数(虽无参数,也可实例化)# 2. 前向传播
x = torch.randn(3, 10)  # 输入:3个样本,每个样本10维特征
output = relu(linear(x))  # 先过全连接层,再过激活函数
print("nn.Module输出形状:", output.shape)  # torch.Size([3, 5])
(2)使用nn.functional
import torch
import torch.nn.functional as F# 1. 手动定义参数(需指定形状,初始化权重)
weight = torch.randn(5, 10)  # 全连接层权重:out_features × in_features
bias = torch.randn(5)        # 偏置:out_features维# 2. 前向传播(手动传入weight和bias)
x = torch.randn(3, 10)
output = F.relu(F.linear(x, weight, bias))  # 先调用linear函数,再调用relu函数
print("nn.functional输出形状:", output.shape)  # torch.Size([3, 5])

三、三种模型构建方法:从简单到灵活

PyTorch 支持多种模型构建方式,可根据模型复杂度选择。以下以 “MNIST 手写数字识别” 的全连接模型(输入 28×28=784 维,隐藏层 300/100 维,输出 10 类)为例,详细讲解三种构建方法。

方法 1:继承nn.Module基类(最灵活,推荐复杂模型)

当模型需要自定义前向传播逻辑(如分支结构、跳连)时,需继承nn.Module基类,并实现__init__(定义层)和forward(定义数据流转)两个方法。

代码实现
import torch
from torch import nn
import torch.nn.functional as F# 定义模型类,继承nn.Module
class MNISTModel(nn.Module):def __init__(self, in_dim=784, n_hidden1=300, n_hidden2=100, out_dim=10):super(MNISTModel, self).__init__()  # 调用父类构造函数# 1. 定义网络层(自动管理参数)self.flatten = nn.Flatten()  # 将28×28图片展平为784维向量self.linear1 = nn.Linear(in_dim, n_hidden1)  # 第1个全连接层self.bn1 = nn.BatchNorm1d(n_hidden1)  # 批量归一化(加速训练)self.linear2 = nn.Linear(n_hidden1, n_hidden2)  # 第2个全连接层self.bn2 = nn.BatchNorm1d(n_hidden2)  # 批量归一化self.out_layer = nn.Linear(n_hidden2, out_dim)  # 输出层(10类)def forward(self, x):# 2. 定义前向传播逻辑(数据流转路径)x = self.flatten(x)          # 展平:(batch, 1, 28, 28) → (batch, 784)x = self.linear1(x)          # 全连接1:(batch, 784) → (batch, 300)x = self.bn1(x)              # 批量归一化x = F.relu(x)                # 激活函数(引入非线性)x = self.linear2(x)          # 全连接2:(batch, 300) → (batch, 100)x = self.bn2(x)              # 批量归一化x = F.relu(x)                # 激活函数x = self.out_layer(x)        # 输出层:(batch, 100) → (batch, 10)x = F.softmax(x, dim=1)      # 输出概率分布(dim=1表示按样本维度归一化)return x# 实例化模型并查看结构
in_dim, n_h1, n_h2, out_dim = 784, 300, 100, 10
model = MNISTModel(in_dim, n_h1, n_h2, out_dim)
print("MNISTModel结构:")
print(model)
运行结果
MNISTModel((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_layer): Linear(in_features=100, out_features=10, bias=True)
)

方法 2:使用nn.Sequential(简单线性模型,层顺序固定)

nn.Sequential是一个 “层容器”,可按顺序堆叠层,自动实现前向传播(无需手动写forward)。适合层顺序固定、无复杂分支的模型。它支持三种初始化方式:

方式 2.1:可变参数(最快,但无法指定层名)

直接将层作为可变参数传入,层名由容器自动分配(如 0、1、2...)。

import torch
from torch import nn# 定义超参数
in_dim, n_h1, n_h2, out_dim = 784, 300, 100, 10# 用可变参数初始化Sequential
model_seq = nn.Sequential(nn.Flatten(),nn.Linear(in_dim, n_h1),nn.BatchNorm1d(n_h1),nn.ReLU(),nn.Linear(n_h1, n_h2),nn.BatchNorm1d(n_h2),nn.ReLU(),nn.Linear(n_h2, out_dim),nn.Softmax(dim=1)
)print("nn.Sequential(可变参数)结构:")
print(model_seq)
方式 2.2:add_module方法(手动指定层名)

通过add_module("层名", 层实例)逐个添加层,适合需要明确层名的场景。

import torch
from torch import nnin_dim, n_h1, n_h2, out_dim = 784, 300, 100, 10# 初始化空的Sequential
model_seq_add = nn.Sequential()
# 逐个添加层并指定名称
model_seq_add.add_module("flatten", nn.Flatten())
model_seq_add.add_module("linear1", nn.Linear(in_dim, n_h1))
model_seq_add.add_module("bn1", nn.BatchNorm1d(n_h1))
model_seq_add.add_module("relu1", nn.ReLU())
model_seq_add.add_module("linear2", nn.Linear(n_h1, n_h2))
model_seq_add.add_module("bn2", nn.BatchNorm1d(n_h2))
model_seq_add.add_module("relu2", nn.ReLU())
model_seq_add.add_module("out_layer", nn.Linear(n_h2, out_dim))
model_seq_add.add_module("softmax", nn.Softmax(dim=1))print("nn.Sequential(add_module)结构:")
print(model_seq_add)
方式 2.3:OrderedDict(有序键值对,层名 + 层实例)

通过collections.OrderedDict将 “层名 - 层实例” 按顺序封装,适合层名较多的场景。

import torch
from torch import nn
from collections import OrderedDictin_dim, n_h1, n_h2, out_dim = 784, 300, 100, 10# 用OrderedDict定义层名和层实例
layers_dict = OrderedDict([("flatten", nn.Flatten()),("linear1", nn.Linear(in_dim, n_h1)),("bn1", nn.BatchNorm1d(n_h1)),("relu1", nn.ReLU()),("linear2", nn.Linear(n_h1, n_h2)),("bn2", nn.BatchNorm1d(n_h2)),("relu2", nn.ReLU()),("out_layer", nn.Linear(n_h2, out_dim)),("softmax", nn.Softmax(dim=1))
])# 初始化Sequential
model_seq_ordered = nn.Sequential(layers_dict)print("nn.Sequential(OrderedDict)结构:")
print(model_seq_ordered)

方法 3:继承nn.Module + 模型容器(兼顾灵活与简洁)

当模型需分 “模块” 管理(如残差网络的 “残差块”)时,可在nn.Module中嵌套模型容器nn.Sequentialnn.ModuleListnn.ModuleDict),既简化代码,又保留自定义逻辑。

容器 1:nn.Sequential(模块内顺序固定)

将模型的某个子模块(如 “全连接 + 批量归一化”)用nn.Sequential封装,减少代码冗余。

import torch
from torch import nn
import torch.nn.functional as Fclass ModelWithSequential(nn.Module):def __init__(self, in_dim=784, n_h1=300, n_h2=100, out_dim=10):super(ModelWithSequential, self).__init__()self.flatten = nn.Flatten()# 用Sequential封装子模块1(linear1 + bn1)self.block1 = nn.Sequential(nn.Linear(in_dim, n_h1),nn.BatchNorm1d(n_h1))# 用Sequential封装子模块2(linear2 + bn2)self.block2 = nn.Sequential(nn.Linear(n_h1, n_h2),nn.BatchNorm1d(n_h2))# 输出层self.out_block = nn.Sequential(nn.Linear(n_h2, out_dim),nn.Softmax(dim=1))def forward(self, x):x = self.flatten(x)x = F.relu(self.block1(x))  # 子模块1 + 激活x = F.relu(self.block2(x))  # 子模块2 + 激活x = self.out_block(x)       # 输出子模块return x# 实例化模型
model_with_seq = ModelWithSequential()
print("ModelWithSequential结构:")
print(model_with_seq)
容器 2:nn.ModuleList(模块列表,支持迭代)

将层或子模块存入列表,支持索引访问和迭代,适合动态调整模块数量的场景(如可变层数的全连接网络)。

import torch
from torch import nn
import torch.nn.functional as Fclass ModelWithModuleList(nn.Module):def __init__(self, in_dim=784, hidden_dims=[300, 100], out_dim=10):super(ModelWithModuleList, self).__init__()self.flatten = nn.Flatten()# 1. 构建模块列表:输入层→隐藏层1→隐藏层2self.layers = nn.ModuleList()prev_dim = in_dim  # 前一层的输出维度for hidden_dim in hidden_dims:self.layers.append(nn.Linear(prev_dim, hidden_dim))  # 全连接层self.layers.append(nn.BatchNorm1d(hidden_dim))       # 批量归一化prev_dim = hidden_dim# 2. 输出层self.layers.append(nn.Linear(prev_dim, out_dim))self.layers.append(nn.Softmax(dim=1))def forward(self, x):x = self.flatten(x)# 迭代模块列表,实现前向传播for layer in self.layers:# 对激活函数的特殊处理(此处简化,实际可单独判断)if isinstance(layer, (nn.Linear, nn.BatchNorm1d, nn.Softmax)):x = layer(x)# 若有ReLU,可在此处添加:x = F.relu(x)return x# 实例化模型(隐藏层维度为[300, 100])
model_with_list = ModelWithModuleList(hidden_dims=[300, 100])
print("ModelWithModuleList结构:")
print(model_with_list)
容器 3:nn.ModuleDict(模块字典,支持键值访问)

将模块用 “键值对” 存储,可通过键名灵活调用模块,适合需要动态选择模块的场景(如多分支模型)。

import torch
from torch import nn
import torch.nn.functional as Fclass ModelWithModuleDict(nn.Module):def __init__(self, in_dim=784, n_h1=300, n_h2=100, out_dim=10):super(ModelWithModuleDict, self).__init__()# 1. 用字典存储模块self.layers_dict = nn.ModuleDict({"flatten": nn.Flatten(),"linear1": nn.Linear(in_dim, n_h1),"bn1": nn.BatchNorm1d(n_h1),"relu": nn.ReLU(),"linear2": nn.Linear(n_h1, n_h2),"bn2": nn.BatchNorm1d(n_h2),"out": nn.Linear(n_h2, out_dim),"softmax": nn.Softmax(dim=1)})def forward(self, x):# 2. 按键名顺序调用模块(自定义流转路径)layer_order = ["flatten", "linear1", "bn1", "relu", "linear2", "bn2", "relu", "out", "softmax"]for layer_name in layer_order:x = self.layers_dict[layer_name](x)return x# 实例化模型
model_with_dict = ModelWithModuleDict()
print("ModelWithModuleDict结构:")
print(model_with_dict)

四、自定义网络模块:实现残差块与 ResNet18

在实际场景中,基础层往往无法满足需求(如深层网络的梯度消失问题)。此时需自定义复杂模块,以残差块(Residual Block)和 ResNet18 为例,讲解自定义模块的实现思路。

1. 残差块的核心思想

残差块通过 “跳连(Skip Connection)” 将输入直接加到输出,解决深层网络的梯度消失问题。根据输入与输出维度是否一致,残差块分为两种:

块 1:基础残差块(RestNetBasicBlock

输入与输出维度一致,直接跳连(无额外参数)。

import torch
import torch.nn as nn
import torch.nn.functional as Fclass RestNetBasicBlock(nn.Module):def __init__(self, in_channels, out_channels, stride=1):super(RestNetBasicBlock, self).__init__()# 残差路径:2个3×3卷积 + 批量归一化self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1  # 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):residual = x  # 跳连:保存输入(残差)# 残差路径计算out = self.conv1(x)out = self.bn1(out)out = F.relu(out)out = self.conv2(out)out = self.bn2(out)# 残差连接:输入 + 残差路径输出out += residualout = F.relu(out)return out
块 2:下采样残差块(RestNetDownBlock

输入与输出维度不一致(如通道数增加、尺寸缩小),需通过 1×1 卷积调整输入维度,再进行跳连。

class RestNetDownBlock(nn.Module):def __init__(self, in_channels, out_channels, stride=[2, 1]):super(RestNetDownBlock, self).__init__()# 残差路径:2个3×3卷积(第1个卷积下采样) + 批量归一化self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride[0], padding=1  # stride=2下采样)self.bn1 = nn.BatchNorm2d(out_channels)self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride[1], padding=1  # stride=1保持尺寸)self.bn2 = nn.BatchNorm2d(out_channels)# 1×1卷积:调整输入维度(通道数+尺寸),适配残差路径输出self.extra_conv = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride[0], padding=0  # 下采样+通道调整),nn.BatchNorm2d(out_channels))def forward(self, x):residual = self.extra_conv(x)  # 1×1卷积调整输入维度# 残差路径计算out = self.conv1(x)out = self.bn1(out)out = F.relu(out)out = self.conv2(out)out = self.bn2(out)# 残差连接:调整后的输入 + 残差路径输出out += residualout = F.relu(out)return out

2. 组合残差块构建 ResNet18

ResNet18 由 “1 个初始卷积层 + 4 个残差组(含 2 个残差块)+1 个全局平均池化 + 1 个全连接层” 构成。

class ResNet18(nn.Module):def __init__(self, num_classes=10):super(ResNet18, self).__init__()# 1. 初始卷积层(将输入图片(3通道)转换为64通道)self.init_conv = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)self.init_bn = nn.BatchNorm2d(64)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)  # 下采样# 2. 残差组(4组,每组2个残差块)self.layer1 = nn.Sequential(RestNetBasicBlock(64, 64, stride=1),   # 输入64→输出64,无下采样RestNetBasicBlock(64, 64, stride=1))self.layer2 = nn.Sequential(RestNetDownBlock(64, 128, stride=[2, 1]),  # 输入64→输出128,下采样RestNetBasicBlock(128, 128, stride=1))self.layer3 = nn.Sequential(RestNetDownBlock(128, 256, stride=[2, 1]),  # 输入128→输出256,下采样RestNetBasicBlock(256, 256, stride=1))self.layer4 = nn.Sequential(RestNetDownBlock(256, 512, stride=[2, 1]),  # 输入256→输出512,下采样RestNetBasicBlock(512, 512, stride=1))# 3. 全局平均池化(将512×7×7特征图转换为512×1×1)self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))# 4. 全连接层(输出类别概率)self.fc = nn.Linear(512, num_classes)def forward(self, x):# 初始卷积+池化out = self.init_conv(x)out = self.init_bn(out)out = F.relu(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(out.shape[0], -1)  # 展平:(batch, 512, 1, 1) → (batch, 512)out = self.fc(out)return out# 实例化ResNet18(假设分类10类)
resnet18 = ResNet18(num_classes=10)
print("ResNet18结构:")
print(resnet18)

五、模型训练全流程:从数据到可视化

掌握模型构建后,需通过训练让模型 “学习” 数据规律。以下以 ResNet18 训练 CIFAR-10 数据集为例,讲解完整训练流程。

步骤 1:加载并预处理数据集

使用torchvision加载公开数据集(如 CIFAR-10),并进行数据增强(如随机翻转、归一化),提升模型泛化能力。

import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms# 1. 数据预处理与增强
transform_train = transforms.Compose([transforms.RandomCrop(32, padding=4),  # 随机裁剪(32×32)transforms.RandomHorizontalFlip(),     # 随机水平翻转transforms.ToTensor(),                 # 转换为张量(0-1)transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))  # 归一化(CIFAR-10均值/标准差)
])transform_test = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])# 2. 加载数据集
train_dataset = datasets.CIFAR10(root="./data", train=True, download=True, transform=transform_train
)
test_dataset = datasets.CIFAR10(root="./data", train=False, download=True, transform=transform_test
)# 3. 构建数据加载器(批量读取数据)
batch_size = 128
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=2)

步骤 2:定义损失函数与优化器

  • 损失函数:CIFAR-10 是分类任务,使用CrossEntropyLoss(含 Softmax,无需手动添加)。
  • 优化器:使用Adam(自适应学习率,收敛更快),并添加权重衰减(L2 正则化,防止过拟合)。
import torch.optim as optim# 1. 定义模型(ResNet18)
model = ResNet18(num_classes=10)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 优先使用GPU
model.to(device)  # 将模型移动到GPU/CPU# 2. 定义损失函数
criterion = nn.CrossEntropyLoss()# 3. 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001,  # 初始学习率weight_decay=5e-4  # 权重衰减(L2正则化)
)# 4. 学习率调度器(可选,按epoch衰减学习率)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)  # 每20个epoch学习率减半

步骤 3:循环训练模型

训练流程核心是 “前向传播计算损失→反向传播计算梯度→优化器更新参数”,每个 epoch 包含 “训练阶段” 和 “验证阶段”。

import time# 超参数
epochs = 50  # 训练轮次
best_acc = 0.0  # 最佳测试准确率
train_losses = []  # 记录训练损失
test_accs = []     # 记录测试准确率for epoch in range(epochs):# -------------------------- 训练阶段 --------------------------model.train()  # 切换到训练模式(启用Dropout、BatchNorm更新)train_loss = 0.0start_time = time.time()for batch_idx, (data, targets) in enumerate(train_loader):# 1. 数据移动到设备(GPU/CPU)data, targets = data.to(device), targets.to(device)# 2. 清零梯度(防止梯度累积)optimizer.zero_grad()# 3. 前向传播:计算预测值outputs = model(data)# 4. 计算损失loss = criterion(outputs, targets)# 5. 反向传播:计算梯度loss.backward()# 6. 优化器更新参数optimizer.step()# 7. 累加损失train_loss += loss.item() * data.size(0)  # 乘以batch_size,得到总损失# 计算每个epoch的平均训练损失avg_train_loss = train_loss / len(train_dataset)train_losses.append(avg_train_loss)# -------------------------- 测试阶段 --------------------------model.eval()  # 切换到测试模式(关闭Dropout、固定BatchNorm)test_acc = 0.0with torch.no_grad():  # 禁用梯度计算,加速测试for data, targets in test_loader:data, targets = data.to(device), targets.to(device)outputs = model(data)_, predicted = torch.max(outputs, dim=1)  # 取概率最大的类别test_acc += (predicted == targets).sum().item()  # 累加正确预测数# 计算测试准确率avg_test_acc = test_acc / len(test_dataset)test_accs.append(avg_test_acc)# 更新最佳准确率并保存模型if avg_test_acc > best_acc:best_acc = avg_test_acctorch.save(model.state_dict(), "resnet18_best.pth")  # 保存模型权重# 学习率调度scheduler.step()# 打印日志end_time = time.time()print(f"Epoch [{epoch+1}/{epochs}], "f"Train Loss: {avg_train_loss:.4f}, "f"Test Acc: {avg_test_acc:.4f}, "f"Best Acc: {best_acc:.4f}, "f"Time: {end_time - start_time:.2f}s")

步骤 4:可视化训练结果

使用matplotlib绘制 “训练损失曲线” 和 “测试准确率曲线”,直观分析模型训练趋势(如是否过拟合、是否收敛)。

import matplotlib.pyplot as plt# 设置中文字体
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False# 创建画布
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))# 1. 绘制训练损失曲线
ax1.plot(range(1, epochs+1), train_losses, label='训练损失', color='blue')
ax1.set_xlabel('Epoch(训练轮次)')
ax1.set_ylabel('损失值')
ax1.set_title('ResNet18训练损失曲线')
ax1.legend()
ax1.grid(True, alpha=0.3)# 2. 绘制测试准确率曲线
ax2.plot(range(1, epochs+1), test_accs, label='测试准确率', color='red')
ax2.set_xlabel('Epoch(训练轮次)')
ax2.set_ylabel('准确率')
ax2.set_title('ResNet18测试准确率曲线')
ax2.legend()
ax2.grid(True, alpha=0.3)# 保存图片
plt.tight_layout()
plt.savefig('resnet18_training_curve.png', dpi=300, bbox_inches='tight')
plt.show()

六、总结

本文从 PyTorch 神经网络的核心组件出发,详细讲解了三种模型构建方法(继承nn.Modulenn.SequentialModule+ 容器)、自定义残差模块的实现,以及完整的模型训练流程。通过本文的学习,您可以掌握:

  1. nn.Modulenn.functional的核心差异与适用场景;
  2. 根据模型复杂度选择合适的构建方法;
  3. 自定义复杂模块(如残差块)解决实际问题;
  4. 端到端的模型训练与结果可视化。

PyTorch 的灵活性在于其支持从简单到复杂的各类模型,建议您结合具体任务(如分类、检测、生成)实践本文代码,进一步加深理解。

http://www.dtcms.com/a/395258.html

相关文章:

  • 机器学习-多因子线性回归
  • 国产化Excel开发组件Spire.XLS教程:Python 写入 Excel 文件,数据写入自动化实用指南
  • 08 - spring security基于jdbc的账号密码
  • 解决SSL证书导致源站IP被泄露的问题
  • Worst Western Hotel: 1靶场渗透
  • 电子电气架构 --- 软件开发与产品系统集成流程(上)
  • 运维安全08,日志检测和 tcpdump (抓包) 的介绍以及使用
  • DSC 归档配置相关
  • 彭博社-BloombergGPT金融大模型
  • GPT5 Codex简单快速上手
  • Linux配置白名单限制访问_ipset+iptables
  • 多元化通证经济模型:DAO的神经和血液
  • 高系分十六:web应用
  • 【LeetCode热题100(27/100)】合并两个有序链表
  • 嵌入式(SOC+FreeRTOS)汽车仪表盘接口参数安全:规范遵循与防护实践
  • Maven 完整教程
  • 数据驱动下的用户画像系统:从0到1的技术实战与避坑指南
  • 同一个灰色,POI取出来却是白色:一次Excel颜色解析的踩坑记录
  • Excel——常用函数一
  • 立项依据不足会给项目带来哪些风险
  • 从 0 到 1 精通 SkyWalking:分布式系统的 “透视镜“ 技术全解析
  • SkyWalking 核心概念与智能探针工作原理深度揭秘(下)
  • Dockerfile入门指南
  • iOS 原生开发全流程解析,iOS 应用开发步骤、Xcode 开发环境配置、ipa 文件打包上传与 App Store 上架实战经验
  • 数据分析报告的写作流程
  • 当你的断点在说谎:深入解析RTOS中的“幽灵”Bug
  • [BUG]MarkupSafe==3.0.2
  • 机器学习笔试选择题:题组1
  • 79-数据可视化-地图可视化
  • python全栈-数据可视化