• Pytorch之LeNet-5图像分类


    • 💂 个人主页:风间琉璃
    • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
    • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)订阅专栏

    目录

    前言 

    一、LeNet-5

    二、LeNet-5网络实现

    1.定义LeNet-5模型

    2.加载数据集

    3.训练模型

    4.测试模型

    三、实现图像分类


    前言 

     LeNet-5是一个经典的深度卷积神经网络,由Yann LeCun在1998年提出,旨在解决手写数字识别问题,被认为是卷积神经网络的开创性工作之一。该网络是第一个被广泛应用于数字图像识别的神经网络之一,也是深度学习领域的里程碑之一。

    一、LeNet-5

    下图是 LeNet-5 的网络结构图,它 接受32×32大小的数字、字符图片,经过第一个卷积层得到[b,6, 28,28]形状的张量,经过一个向下采样层,张量尺寸缩小到[b,6,14,14],经过第二个卷积层,得到[b,16,10,10]形状的张量,同样经过下采样层,张量尺寸缩小到[b,16, 5,5],在进入全连接层之前,先将张量 打成[b,16*5*5 ]的张量,送入输出节点数分别为 120、84 的 2 个全连接层,得到[b,84]的张量,最后通过Gaussian connections层,最终输出[b,10]

    LeNet-5的基本结构包括7层网络结构(不含输入层),其中包括2个卷积层、2个降采样层(池化层)、2个全连接层和输出层。LeNet-5 网络层数较少(2 个卷积层和 2 个全连接层),参数量较少,计算代价较低,尤其在现代GPU的加持下,数分钟即可训练好 LeNet-5 网络。 

    这里网络结构只给了进行卷积核池化前后的特征图的大小,那么如果确定卷积核的尺寸和通道数呢?

    1.输入特征层的channel与卷积核的channel相同

    2.输出特征层的channel与卷积核个数相同

    经过卷积后的矩阵尺寸大小计算公式为:

    N = (W - F + 2P) /  S  +1

    ①输入图片大小WxW

    ②卷积核Filter大小FxF

    ③步长S

    ④panding填充值P

    比如输入层接收大小为 32×32 的手写数字图像,卷积层C1包括6个卷积核,每个卷积核的大小为 5×5 ,步长为1,填充为0。因此,每个卷积核会产生一个大小为 28×28 的特征图(输出通道数为6)。

    N(28) = (32-5+0)/1 + 1 =27 + 1 = 28

    采样层S2采用最大池化(max-pooling)操作,每个窗口的大小为 2×2 ,步长为2。因此,每个池化操作会从4个相邻的特征图中选择最大值,产生一个大小为 14×14 的特征图(输出通道数为6)。这样可以减少特征图的大小,提高计算效率,并且对于轻微的位置变化可以保持一定的不变性。其他的网络层也是一样的,可以相互推算。

    二、LeNet-5网络实现

    1.定义LeNet-5模型

    根据上面网络模型使用Pytorch实现LeNet-5网络模型的搭建

    1. import torch.nn as nn
    2. import torch.nn.functional as F
    3. class LeNet(nn.Module):
    4. def __init__(self):
    5. super(LeNet, self).__init__()
    6. self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)
    7. self.pool1 = nn.MaxPool2d(2, 2)
    8. self.conv2 = nn.Conv2d(6, 16, 5)
    9. self.pool2 = nn.MaxPool2d(2, 2)
    10. self.fc1 = nn.Linear(16*5*5, 120)
    11. self.fc2 = nn.Linear(120, 84)
    12. self.fc3 = nn.Linear(84, 10)
    13. def forward(self, x):
    14. x = F.relu(self.conv1(x)) # input(3, 32, 32) output(6, 28, 28)
    15. x = self.pool1(x) # output(6, 14, 14)
    16. x = F.relu(self.conv2(x)) # output(16, 10, 10)
    17. x = self.pool2(x) # output(16, 5, 5)
    18. x = x.view(-1, 16*5*5) # output(16*5*5)
    19. x = F.relu(self.fc1(x)) # output(120)
    20. x = F.relu(self.fc2(x)) # output(84)
    21. x = self.fc3(x) # output(10)
    22. return x
    23. if __name__ == '__main__':
    24. net = LeNet()
    25. print(net)

    2.加载数据集

    使用CIFAR10数据集,加载数据集后还需要对数据集进行预处理,如图像格式转换(Tensor)、归一化、标准化等处理。然后使用DataLoader分批次加载数据集,用于训练和测试。

    1. # 预处理
    2. transform = transforms.Compose(
    3. [transforms.ToTensor(), # 将图像转化为tensor,并做归一化:[0,1] 数据类型转换 + 标准化
    4. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 输入数据的数值范围标准化为特定的均值和标准差
    5. ]
    6. )
    7. # 加载训练集
    8. train_set = torchvision.datasets.CIFAR10(root='./data', train=True, transform=transform, download=True)
    9. train_loader = torch.utils.data.DataLoader(train_set, batch_size=36, shuffle=True, num_workers=0)
    10. # 加载测试集
    11. val_set = torchvision.datasets.CIFAR10(root='./data', train=False, transform=transform, download=True)
    12. val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000, num_workers=0)
    13. # 使用next函数从val_data_iter迭代器中获取下一个批次的数据
    14. val_data_iter = iter(val_loader)
    15. val_image, val_label = next(val_data_iter)

    3.训练模型

    实例化网络模型,并进行网络模型的训练。

    1. net = LeNet()
    2. loss_function = nn.CrossEntropyLoss()
    3. optimizer = optim.Adam(net.parameters(), lr=0.001)
    4. for epoch in range(10): # 训练次数
    5. # 每次训练的损失值
    6. running_loss = 0.0
    7. # 获取批次的索引 step 和数据 data
    8. for step, data in enumerate(train_loader, start=0):
    9. # 获取images,labels; data是一个列表[images, labels]
    10. images, labels = data
    11. # 将优化器的梯度缓冲区清零
    12. optimizer.zero_grad()
    13. # forward + backward + optimize
    14. # 前向传播,得到模型的输出
    15. outputs = net(images)
    16. # 计算模型的输出和真实标签 labels 之间的损失(误差)
    17. loss = loss_function(outputs, labels)
    18. # 通过反向传播算法计算损失对模型参数的梯度
    19. loss.backward()
    20. # 根据梯度更新模型参数,这是优化器的一次参数更新步骤
    21. optimizer.step()

    4.测试模型

    在每训练到500次时,进行一次测试。

    1. # 测试
    2. running_loss += loss.item()
    3. if step % 500 == 499:
    4. # 关闭梯度计算。因为在验证或测试时不需要计算梯度,所以可以提高运行效率
    5. with torch.no_grad():
    6. outputs = net(val_image) # [batch, 10]
    7. # 选择输出中概率最高的类别作为预测结果,并且是在第一个维度[batch,10]
    8. # max 返回找到最大的值以及该值所在的位置(索引),是一个元组(val ,index)
    9. predict_y = torch.max(outputs, dim=1)[1]
    10. accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)
    11. print('[%d, %5d] train_loss: %.3f test_accuracy: %.3f' %
    12. (epoch + 1, step + 1, running_loss / 500, accuracy))
    13. running_loss = 0.0

    在网络训练完成后,记得保存网络模型,用于后续的部署和使用。

    1. save_path = './Lenet.pth'
    2. torch.save(net.state_dict(), save_path)

    三、实现图像分类

    将上面保存的模型用来测试其他图片,检验模型训练的效果。

    1. import torch
    2. import torchvision.transforms as transforms
    3. from PIL import Image, ImageDraw
    4. from model import LeNet
    5. def main():
    6. # 图片预处理
    7. transform = transforms.Compose(
    8. [transforms.Resize((32, 32)),
    9. transforms.ToTensor(),
    10. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    11. # 分类标签
    12. classes = ('plane', 'car', 'bird', 'cat',
    13. 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
    14. # s实例化网络
    15. net = LeNet()
    16. # 加载网络模型
    17. net.load_state_dict(torch.load('Lenet.pth'))
    18. img = Image.open('dog.jpg')
    19. # [H, W, C] --> [C, H, W]
    20. image = transform(img)
    21. # 增加维度:[N, C, H, W],使满足网络的输入维度要求
    22. image = torch.unsqueeze(image, dim=0)
    23. with torch.no_grad():
    24. # 得到预测结果
    25. outputs = net(image)
    26. # 得到分类标签
    27. predict = torch.max(outputs, dim=1)[1].numpy()
    28. print(classes[int(predict)])
    29. draw = ImageDraw.Draw(img)
    30. text = classes[int(predict)]
    31. # 文本的左上角位置
    32. position = (10, 10)
    33. # fill 指定文本颜色
    34. draw.text(position, text, fill='red')
    35. img.show()
    36. if __name__ == '__main__':
    37. main()

    预测结果:

    结束语
    感谢你观看我的文章呐~本次航班到这里就结束啦 🛬

    希望本篇文章有对你带来帮助 🎉,有学习到一点知识~

    躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。

    最后,博主要一下你们的三连呀(点赞、评论、收藏),不要钱的还是可以搞一搞的嘛~

    不知道评论啥的,即使扣个666也是对博主的鼓舞吖 💞 感谢 💐

  • 相关阅读:
    数据分析Pandas专栏---第三章<Pandas合并list和字典>
    使用dom4j解析XML
    马士兵-郑金维—并发编程—4.阻塞队列
    文本生成中的采样策略
    【PyTorch】深度学习实践之 RNN基础篇——实现RNN
    vscode路径别名文件跳转解决办法
    什么是APS系统?导入APS要注意什么?值得反复观看
    如何在不损失质量的情况下调整图像大小
    多商户商城系统功能拆解29讲-平台端营销-会员签到
    深度学习(18):RuntimeWarning: overflow encountered in exp学习笔记
  • 原文地址:https://blog.csdn.net/qq_53144843/article/details/133215497