首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >基于pytorch框架搭建经典的LeNet-5架构

基于pytorch框架搭建经典的LeNet-5架构

原创
作者头像
一个风轻云淡
发布2026-01-03 11:59:28
发布2026-01-03 11:59:28
1570
举报

LeNet介绍

核心背景

LeNet的提出并非出于纯粹的理论兴趣,而是为了解决一个非常具体且具有巨大商业价值的实际问题:自动识别银行支票上的手写数字

在20世纪80年代末到90年代,银行每天需要处理海量的支票,手工录入数字效率低下、成本高昂且容易出错。因此,如何让机器“看懂”手写数字,实现自动化处理,成为了一个紧迫的工业界需求。

在当时(20世纪90年代),传统方法依赖“人工设计特征”,流程复杂且效果有限。Yann LeCun等人的突破性想法是:让模型自己从数据中学习特征。

为此,他们设计了第一个成功应用的卷积神经网络(CNN)——LeNet。其意义在于:

  1. 确立了核心架构:首次将卷积、池化、反向传播结合,形成“卷积-池化-全连接”的经典范式。
  2. 实现了端到端学习:输入原始像素,输出识别结果,中间所有特征均由模型自动学习,无需人工干预。

因此,LeNet不仅是手写数字识别问题的解决方案,更开创了现代深度学习在计算机视觉领域的先河,为后续所有CNN模型奠定了基础。

LeNet架构

核心流程:输入 → 卷积 → 池化 → 卷积 → 池化 → 全连接 → 输出

输入:32×32 的灰度图像。

C1 - 卷积层

  • 6个5×5的卷积核。
  • 输出:6张28×28的特征图。

S2 - 池化层

  • 2×2的平均池化。
  • 输出:6张14×14的特征图。

C3 - 卷积层

  • 16个5×5的卷积核。
  • 输出:16张10×10的特征图。

S4 - 池化层

  • 2×2的平均池化。
  • 输出:16张5×5的特征图。

C5 - 全连接层

  • 120个神经元。

F6 - 全连接层

  • 84个神经元。

输出层

  • 10个神经元(对应数字0-9)。

代码搭建

model文件

代码语言:python
复制
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.c1=nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5,stride=1,padding=2)
        self.sig=nn.Sigmoid()
        self.s2=nn.AvgPool2d(kernel_size=2,stride=2)
        self.c3=nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5)
        self.s4=nn.AvgPool2d(kernel_size=2,stride=2)
        self.flatten=nn.Flatten()
        self.f5=nn.Linear(16*5*5,120)
        self.f6 = nn.Linear(120, 84)
        self.f7 = nn.Linear(84, 10)
    def forward(self,x):
        x=self.sig(self.c1(x))
        x=self.s2(x)
        x=self.sig(self.c3(x))
        x=self.s4(x)
        x=self.flatten(x)
        x=self.f5(x)
        x=self.f6(x)
        x=self.f7(x)
        return x

卷积层

self.c1: 第一个卷积层,输入通道数为1(灰度图像),输出通道数为6,卷积核大小5×5,步长1,填充2

self.c3: 第二个卷积层,输入通道数为6,输出通道数为16,卷积核大小5×5

激活函数

self.sig: Sigmoid激活函数,用于引入非线性变换

池化层

self.s2: 平均池化层,池化窗口大小2×2,步长2

self.s4: 平均池化层,池化窗口大小2×2,步长2

全连接层

self.f5: 线性层,输入维度为16*5*5=400,输出维度为120

self.f6: 线性层,输入维度为120,输出维度为84

self.f7: 线性层,输入维度为84,输出维度为10(分类数量)

辅助层

self.flatten: 展平层,将多维张量转换为一维向量

验证代码

代码语言:py
复制
if __name__=='__main__':
    device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model=LeNet().to(device)
    print(summary(model,(1,28,28)))

执行结果如下

model_train训练文件

基于FashionMNIST数据处理

代码语言:py
复制
def train_val_data_process():
    train_data = FashionMNIST(root='./data',
                              train=True,
                              download=True,
                              transform=transforms.Compose([transforms.Resize(size=227), transforms.ToTensor()]))

    train_data, val_data = Data.random_split(train_data, [round(0.8 * len(train_data)),round(0.2 * len(train_data))])
    train_dataloader = Data.DataLoader(dataset=train_data,
                                   batch_size=32,
                                   shuffle=True,
                                   num_workers=2)
    val_dataloader = Data.DataLoader(dataset=train_data,
                                   batch_size=32,
                                   shuffle=True,
                                   num_workers=2)
    return train_dataloader,val_dataloader

加载FashionMNIST训练数据集,设置图像大小为227×227并转换为张量

将数据集按8:2比例随机分割为训练集和验证集

创建训练和验证数据加载器,批量大小为32,启用数据混洗和2个工作进程

具体训练过程

代码语言:py
复制
def train_model_process(model,train_dataloader,val_dataloader,num_epochs):
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    optimizer=torch.optim.Adam(model.parameters(),lr=0.001)
    #交叉熵z失函数
    criterion=nn.CrossEntropyLoss()
    model=model.to(device)
    #复制当前模型的参数
    best_model_wts = copy.deepcopy(model.state_dict())

    best_acc = 0.0

    train_loss_all = []

    val_loss_all = []

    val_acc_all= []

    train_acc_all= []

    since=time.time()

    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)

        train_loss=0.0
        train_correct = 0.0

        val_loss = 0.0
        val_correct = 0.0

        train_num = 0
        val_num = 0



        for step,(b_x,b_y) in enumerate(train_dataloader):
            b_x,b_y=b_x.to(device),b_y.to(device)

            model.train()
            #前向传播
            output=model(b_x)

            #查找概率最大的索引
            pre_lab=torch.argmax(output,dim=1)

            loss = criterion(output, b_y)

            optimizer.zero_grad()

            loss.backward()

            optimizer.step()

            train_loss+=loss.item() * b_x.size(0)

            train_correct+=torch.sum(pre_lab == b_y)

            train_num+=b_x.size(0)



        for step,(b_x,b_y) in enumerate(val_dataloader):
            b_x,b_y=b_x.to(device),b_y.to(device)
            model.eval()
            # 前向传播
            output = model(b_x)
            pre_lab = torch.argmax(output, dim=1)
            loss = criterion(output, b_y)
            # 查找概率最大的索引
            pre_lab = torch.argmax(output, dim=1)

            loss = criterion(output, b_y)

            val_loss += loss.item() * b_x.size(0)

            val_correct += torch.sum(pre_lab == b_y)

            val_num += b_x.size(0)

        #计算并保持每一轮次迭代的loss值
        train_loss_all.append(train_loss / train_num)
        train_acc_all.append(train_correct.double().item() / train_num)

        val_loss_all.append(val_loss / val_num)
        val_acc_all.append(val_correct.double().item() / val_num)



        print('train_loss:{:.4f},train_acc:{:.4f}'.format(train_loss_all[-1],train_acc_all[-1]))
        print('val_loss:{:.4f},val_acc:{:.4f}'.format(val_loss_all[-1],val_acc_all[-1]))

        if val_acc_all[-1] >best_acc :
            #保存当前最高的正确度
            best_acc=val_acc_all[-1]
            #相对应该权重参数
            best_model_wts=copy.deepcopy(model.state_dict())

        time_use=time.time()-since
        #打印耗费的时间
        print('模型训练和验证耗费的时间:{:.0f}m {:.0f}s'.format(time_use // 60, time_use % 60))

    #加载权重参数并保存
    torch.save(best_model_wts,'C:/Users/28604/Desktop/AlexNet/best_model.pth')


    train_process=pd.DataFrame(data={'epoch':range(num_epochs),
                                    'train_loss_all':train_loss_all,
                                     'val_loss_all': val_loss_all,
                                      'train_acc_all':train_acc_all,
                                      'val_acc_all':val_acc_all})
    return train_process

函数首先初始化了设备、优化器(Adam)和损失函数(交叉熵),然后在每个epoch中分别处理训练和验证数据。在训练阶段,模型设置为训练模式(model.train()),进行前向传播、损失计算和反向传播更新参数。在验证阶段,模型切换到评估模式(model.eval()),不进行梯度更新。

matplot直观展示

代码语言:py
复制
def matplot_acc_loss(train_process):
    plt.figure(figsize=(12,4))
    plt.subplot(1,2,1)
    plt.plot(train_process["epoch"],train_process.train_loss_all,'ro-',label='train loss')
    plt.plot(train_process["epoch"],train_process.val_loss_all,'bs-',label='val loss')
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("loss")

    plt.subplot(1, 2, 2)
    plt.plot(train_process["epoch"], train_process.train_acc_all, 'ro-', label='train acc')
    plt.plot(train_process["epoch"], train_process.val_acc_all, 'bs-', label='val acc')
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("loss")
    plt.legend()
    plt.show()

函数创建一个12x4英寸的画布,分为两个子图:左侧子图绘制训练损失和验证损失随epoch的变化曲线,右侧子图绘制训练准确率和验证准确率随epoch的变化曲线,便于监控模型训练效果。

代码语言:python
复制
if __name__ == '__main__':

    LeNet=AlexNet()

    train_dataloader,val_dataloader=train_val_data_process()

    train_process =    train_model_process(LeNet,train_dataloader,val_dataloader,num_epochs=5)

    matplot_acc_loss(train_process)

model_test测试文件

测试数据加载

代码语言:py
复制
def test_val_data_process():
    test_data = FashionMNIST(root='./data',
                              train=False,
                              download=True,
                              transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]))

    test_data, val_data = Data.random_split(test_data, [round(0.8 * len(test_data)),round(0.2 * len(test_data))])
    test_dataloader = Data.DataLoader(dataset=test_data,
                                   batch_size=1,
                                   shuffle=True,
                                   num_workers=0)

    return test_dataloader

1)加载FashionMNIST测试数据集,进行尺寸调整和张量转换;

2)将数据集按8:2比例随机分割为测试集和验证集;

3)创建测试数据的数据加载器,设置批次大小为1并启用随机打乱;

4)返回测试数据加载器。

具体测试过程

代码语言:py
复制
def test_model_process(model,test_dataloader):
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model=model.to(device)
    test_correct = 0.0
    test_num = 0

    #测试不进行梯度计算
    with torch.no_grad():
        for test_data_x, test_data_y in test_dataloader:
            test_data_x,test_data_y=test_data_x.to(device),test_data_y.to(device)
            model.eval()
            test_output = model(test_data_x)
            test_lab = torch.argmax(test_output, dim=1)

            #计算正确几率
            test_correct += torch.sum(test_lab == test_data_y)
            test_num += test_data_x.size(0)
        print('test_acc:{:.4f}'.format(test_correct.double().item() / test_num))


def test_model_process1(model,test_dataloader):
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model=model.to(device)
    test_correct = 0.0
    test_num = 0
    asse=[u'T恤/上衣' , u'裤子' , u'套衫' , u' Dress' , u'外套' , u'凉鞋' , u'衬衫' , u'运动鞋' , u'包' , u'短靴']
    #测试不进行梯度计算
    with torch.no_grad():
        for test_data_x, test_data_y in test_dataloader:
            test_data_x,test_data_y=test_data_x.to(device),test_data_y.to(device)
            model.eval()
            test_output = model(test_data_x)
            test_lab = torch.argmax(test_output, dim=1)
            result=test_lab.item()
            label=test_data_y.item()

            print('预测结果为:{},真实结果为:{}'.format(asse[result],asse[label]))

            #计算正确几率
            test_correct += torch.sum(test_lab == test_data_y)
            test_num += test_data_x.size(0)
        print('test_acc:{:.4f}'.format(test_correct.double().item() / test_num))

test_model_process 函数

这个函数用于对训练好的模型进行测试,计算准确率。首先设置设备(自动选择GPU或CPU),然后将模型移动到指定设备并设置为评估模式。在 with torch.no_grad(): 上下文中遍历测试数据集,将输入数据和标签移动到设备,获取模型预测结果,并计算预测准确率,最后打印整体测试准确率。

test_model_process1 函数

这个函数在基础测试功能上增加了预测结果的可视化输出。除了与基础版本相同的准确率统计功能外,还定义了FashionMNIST数据集的10个类别名称列表 asse,包括T恤/上衣、裤子、套衫、Dress、外套、凉鞋、衬衫、运动鞋、包和短靴。函数会输出每张图片的预测结果和真实标签,便于查看模型的预测效果。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • LeNet介绍
    • 核心背景
    • LeNet架构
  • 代码搭建
    • model文件
    • model_train训练文件
      • 基于FashionMNIST数据处理
      • 具体训练过程
      • matplot直观展示
      • model_test测试文件
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档