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

深度学习篇---LeNet-5网络结构

在 PyTorch 中实现 LeNet-5 其实很简单,就像搭积木一样把各个层按照顺序组合起来。我们可以分步骤来实现,从搭建网络结构到训练模型,每一步都很直观。

一、先明确 LeNet-5 的结构(复习)

在 PyTorch 中实现网络,首先要记住 LeNet-5 的经典结构:

输入(32×32灰度图) → 卷积层1 → 池化层1 → 卷积层2 → 池化层2 → 全连接层1 → 全连接层2 → 输出层(10个数字)

二、PyTorch 实现 LeNet-5 的步骤

步骤 1:导入必要的库

就像做菜需要先准备好厨具,实现模型前要导入 PyTorch 相关的工具:

import torch  # PyTorch的核心库
import torch.nn as nn  # 包含各种神经网络层
import torch.optim as optim  # 包含优化器(比如梯度下降)
from torch.utils.data import DataLoader  # 用于加载数据
from torchvision import datasets, transforms  # 用于处理图像数据

步骤 2:定义 LeNet-5 网络结构

在 PyTorch 中,我们通过创建一个类来定义网络,这个类要继承nn.Module(PyTorch 中所有网络的基类)。
核心是重写两个方法:__init__(定义网络层)和forward(定义数据流向)。

class LeNet5(nn.Module):def __init__(self):super(LeNet5, self).__init__()# 定义卷积层和池化层self.conv_layers = nn.Sequential(# 卷积层1:输入1个通道(灰度图),输出6个通道,卷积核5×5nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5),nn.Tanh(),  # 激活函数,让网络能学习非线性特征# 池化层1:2×2最大池化nn.MaxPool2d(kernel_size=2, stride=2),# 卷积层2:输入6个通道,输出16个通道,卷积核5×5nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5),nn.Tanh(),# 池化层2:2×2最大池化nn.MaxPool2d(kernel_size=2, stride=2))# 定义全连接层self.fc_layers = nn.Sequential(# 全连接层1:输入是16×5×5的特征(池化后的结果),输出120nn.Linear(in_features=16*5*5, out_features=120),nn.Tanh(),# 全连接层2:输入120,输出84nn.Linear(in_features=120, out_features=84),nn.Tanh(),# 输出层:输入84,输出10(对应0-9十个数字)nn.Linear(in_features=84, out_features=10))# 定义数据在网络中的流动过程def forward(self, x):x = self.conv_layers(x)  # 先经过卷积和池化层x = x.view(-1, 16*5*5)  # 把特征图"拉平"成一维向量(为全连接层做准备)x = self.fc_layers(x)   # 再经过全连接层return x

简单解释

  • nn.Sequential:像装积木一样把层按顺序组合起来,数据会按顺序流过每一层。
  • Conv2d:卷积层,kernel_size=5表示用 5×5 的卷积核。
  • MaxPool2d:池化层,stride=2表示每次滑动 2 步(所以尺寸会减半)。
  • Linear:全连接层,把前面的特征汇总后输出结果。

步骤 3:准备数据(以 MNIST 手写数字为例)

LeNet-5 最初就是为识别手写数字设计的,我们用经典的 MNIST 数据集来训练:

# 数据预处理:把图片转换成Tensor,并做标准化
transform = transforms.Compose([transforms.Resize((32, 32)),  # 把MNIST的28×28图片放大到32×32(LeNet-5要求的输入尺寸)transforms.ToTensor(),        # 转换成PyTorch能处理的Tensor格式transforms.Normalize((0.1307,), (0.3081,))  # 标准化(MNIST数据集的均值和标准差)
])# 加载训练集和测试集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform
)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform
)# 用DataLoader批量加载数据(方便训练时按批次处理)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)

步骤 4:初始化模型、损失函数和优化器

  • 模型:就是我们上面定义的 LeNet5。
  • 损失函数:衡量模型预测的错误程度(这里用交叉熵损失,适合分类问题)。
  • 优化器:用来更新模型参数(这里用 SGD,随机梯度下降)。
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  # 用GPU加速(如果有的话)
model = LeNet5().to(device)  # 初始化模型并放到GPU/CPU上criterion = nn.CrossEntropyLoss()  # 交叉熵损失函数(自带Softmax,输出概率)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)  # 优化器,lr是学习率

步骤 5:训练模型

训练过程就像 “学生做题纠错”:模型先猜答案,根据错误调整自己,反复多次直到准确率提高。

def train(model, train_loader, criterion, optimizer, epoch):model.train()  # 切换到训练模式for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)  # 数据放到GPU/CPU上optimizer.zero_grad()  # 清空之前的梯度output = model(data)   # 模型预测loss = criterion(output, target)  # 计算损失(预测和真实答案的差距)loss.backward()        # 反向传播,计算梯度optimizer.step()       # 根据梯度更新模型参数# 打印训练进度if batch_idx % 100 == 0:print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item():.6f}')# 训练5轮(可以根据需要增加)
for epoch in range(1, 6):train(model, train_loader, criterion, optimizer, epoch)

训练的核心逻辑

  1. 模型用当前参数做预测 → 2. 计算预测错误(损失) → 3. 反向算 “怎么调整参数能减少错误”(梯度) → 4. 按梯度调整参数 → 重复。

步骤 6:测试模型效果

训练完后,用测试集看看模型的准确率:

def test(model, test_loader):model.eval()  # 切换到评估模式(关闭 dropout 等训练特有的操作)correct = 0total = 0with torch.no_grad():  # 测试时不计算梯度(节省内存)for data, target in test_loader:data, target = data.to(device), target.to(device)output = model(data)_, predicted = torch.max(output.data, 1)  # 取概率最大的那个数字作为预测结果total += target.size(0)correct += (predicted == target).sum().item()print(f'Test Accuracy: {100 * correct / total:.2f}%')# 测试模型
test(model, test_loader)

运行后,你会看到类似这样的结果(训练 5 轮后):
Test Accuracy: 98.50%(准确率会因训练轮数等略有差异)

三、完整代码总结

把上面的步骤整合起来,就是一个可直接运行的 LeNet-5 实现:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms# 1. 定义LeNet-5网络结构
class LeNet5(nn.Module):def __init__(self):super(LeNet5, self).__init__()# 卷积和池化层self.conv_layers = nn.Sequential(nn.Conv2d(1, 6, kernel_size=5),  # 卷积层1:1→6通道,5×5卷积核nn.Tanh(),nn.MaxPool2d(kernel_size=2, stride=2),  # 池化层1:2×2,步长2nn.Conv2d(6, 16, kernel_size=5),  # 卷积层2:6→16通道,5×5卷积核nn.Tanh(),nn.MaxPool2d(kernel_size=2, stride=2)  # 池化层2:2×2,步长2)# 全连接层self.fc_layers = nn.Sequential(nn.Linear(16*5*5, 120),  # 全连接层1:输入16×5×5,输出120nn.Tanh(),nn.Linear(120, 84),      # 全连接层2:输入120,输出84nn.Tanh(),nn.Linear(84, 10)        # 输出层:输入84,输出10(0-9))def forward(self, x):x = self.conv_layers(x)          # 经过卷积和池化x = x.view(-1, 16*5*5)           # 拉平特征图x = self.fc_layers(x)            # 经过全连接层return x# 2. 准备数据
transform = transforms.Compose([transforms.Resize((32, 32)),       # 调整为32×32transforms.ToTensor(),             # 转为Tensortransforms.Normalize((0.1307,), (0.3081,))  # 标准化
])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=1000, shuffle=False)# 3. 初始化模型、损失函数和优化器
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = LeNet5().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)# 4. 训练函数
def train(model, train_loader, criterion, optimizer, epoch):model.train()for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()   # 清空梯度output = model(data)    # 模型预测loss = criterion(output, target)  # 计算损失loss.backward()         # 反向传播optimizer.step()        # 更新参数if batch_idx % 100 == 0:print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item():.6f}')# 5. 测试函数
def test(model, test_loader):model.eval()correct = 0total = 0with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)output = model(data)_, predicted = torch.max(output.data, 1)total += target.size(0)correct += (predicted == target).sum().item()print(f'Test Accuracy: {100 * correct / total:.2f}%')# 6. 开始训练和测试
for epoch in range(1, 6):train(model, train_loader, criterion, optimizer, epoch)
test(model, test_loader)

四、关键知识点回顾

  1. 网络结构:卷积层负责提取特征(线条、圆圈),池化层压缩数据,全连接层做最终判断。
  2. 训练流程:通过 “预测→算错→调参” 的循环,让模型逐渐学会识别数字。
  3. PyTorch 技巧:用nn.Sequential简化层的组合,用DataLoader方便数据加载,用to(device)实现 GPU 加速。

运行这段代码后,你会得到一个能识别手写数字的 LeNet-5 模型,准确率通常能达到 98% 以上,和 LeCun 当年的结果很接近!

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

相关文章:

  • iOS 开发中的 UIStackView 使用详解
  • Linux-服务器初始化
  • RHEL8.6环境下批量验证服务器凭据并配置Ansible免密管理全流程
  • 用wp_trim_words函数实现WordPress截断部分内容并保持英文单词完整性
  • Ansible 文件管理与 Jinja2 模板全解析:从模块应用到动态配置生成
  • Ansible核心技巧:循环条件与错误处理
  • nginx代理 flink Dashboard、sentinel dashboard的问题
  • HarmonyOS之深入了解装饰器
  • 服务器初始化流程***
  • Rust 符号体系全解析:分类、应用与设计意图
  • CentOS 7 升级 OpenSSL 3.5.1 的详细教程
  • 【Linux】Socket编程——TCP版
  • 【Python】shutil.make_archive() 方法详解
  • 支持向量机(SVM)核心原理与应用解析
  • SOME/IP-SD规范中,对 服务(Service) 和 实例(Instance)的理解
  • 多模态RAG架构:下一代跨模态智能检索系统的设计与实践
  • 机器视觉学习-day03-灰度化实验-二值化和自适应二值化
  • 使用C++与Qt6,在windows上打造MacOS风格桌面应用窗口
  • PDF文件中的相邻页面合并成一页,例如将第1页和第2页合并,第3页和第4页合并
  • Mac测试端口连接的几种方式
  • 如何将视频从安卓设备传输到Mac?
  • Mac安装mitmproxy及操作对监控的请求
  • 少儿舞蹈小程序详细设计文档
  • Mac中修改Word的Normal.dotm文件
  • 使用Uniapp开发小程序,如何引入插件组件!
  • 三电平buckboost电路出现上下母线不平衡是什么原因
  • Linux驱动开发笔记(八)——按键输入实验
  • 滚珠导轨如何定义半导体制造精度?
  • 【LeetCode 热题 100】75. 颜色分类——双指针
  • 算法题打卡力扣第209题:长度最小的子数组(mid)