当前位置:   article > 正文

LeNet-5手写数字识别代码复现笔记(MNIST版)_lenet-minist.py

lenet-minist.py

1.代码复现参考链接

阿里云开发者社区:https://developer.aliyun.com/article/1196254?spm=a2c6h.13148508.setting.14.21a54f0eQRsFIB
微信公众号https://mp.weixin.qq.com/s?__biz=MzIwOTc2MTUyMg==&mid=2247530266&idx=1&sn=7da4a8f0334d3028bbe6b8c020684dd9&chksm=976cd287a01b5b912a20e03c58b09199a8cb4869c09a40320301fc22a494de86fc673475527e&token=431225665&lang=zh_CN#rd
GitHub代码、数据:https://github.com/RedstoneWill/ObjectDetectionLearner/tree/main/LeNet-5
三者皆为同一人所写。

2.代码复现中的笔记

(1)import torch.optim as optim的作用

torch.optim是PyTorch中用于实现各种优化算法的模块,在训练神经网络时非常有用。
它提供了各种类型的优化器,如随机梯度下降、Adam、Adagrad等,并允许我们根据需要自定义优化器。
我们可以使用各种类型的优化器来更新神经网络模型的参数。

(2)from torchvision import datasets,transforms的作用

tochvision主要处理图像数据,包含一些常用的数据集、模型、转换函数等。
torchvision主要包含以下四部分:
torchvision.models: 提供深度学习中各种经典的网络结构、预训练好的模型,如:Alex-Net、VGG、ResNet、Inception等。
torchvision.datasets:提供常用的数据集,设计上继承 torch.utils.data.Dataset,主要包括:MNIST、CIFAR10/100、ImageNet、COCO等。
torchvision.transforms:提供常用的数据预处理操作,主要包括对Tensor及PIL Image对象的操作。
torchvision.utils:工具类,如保存张量作为图像到磁盘,给一个小批量创建一个图像网格。
原文链接:https://blog.csdn.net/qq_45772756/article/details/128651447
torchvision 的 transforms 集成了随机翻转、旋转、增强对比度、
转化为tensor、转化为图像等功能,用于数据增强,非常便捷
原文链接:https://blog.csdn.net/iteapoy/article/details/106121752

(3)在正则化(当模型出现过拟合的情况时,用来降低模型的复杂度) transforms.Normalize((0.1307,),(0.3081,))中

标准化系数就是计算要用到的均值和标准差,在本例中是((0.1307,), (0.3081,)),均值是 0.1307,标准差是 0.3081,这些系数都是数据集提供方计算好的数据。不同数据集就有不同的标准化系数,另外还需要保持train_set、val_set和test_set标准化系数的一致性。

(4)关于x=x.view(-1,1655)

其实在torch里面,view函数就相当于numpy的reshape,执行的操作就是对tensor进行维度转换。这边的这里-1表示一个不确定的数,就是你如果不确定你想要reshape成几行

(5)在执行test.py文件时,出现AttributeError: Can’t get attribute ‘LeNet’ on <module ‘main’ from ‘C:\Code reproduction\LeNet-5\test.py’>报错

解决方法:因为是从model.py文件中调用的LeNet,所以应该在test.py文件中导入model.py文件中对应的LeNet类即可。即from model import LeNet在这里插入图片描述

(6)结果

运行且识别成功。以下面的图片为例:

在这里插入图片描述
识别结果:
在这里插入图片描述

(7)完整代码

文件结构:(data文件运行代码时会自动生成,images文件是放测试识别图片的文件夹,models是存放训练的模型的权重的文件夹)在这里插入图片描述
model.py的代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
"""torch.optim是PyTorch中用于实现各种优化算法的模块,在训练神经网络时非常有用。
它提供了各种类型的优化器,如随机梯度下降、Adam、Adagrad等,并允许我们根据需要自定义优化器。
我们可以使用各种类型的优化器来更新神经网络模型的参数。"""
from torchvision import datasets,transforms
"""tochvision主要处理图像数据,包含一些常用的数据集、模型、转换函数等。
torchvision主要包含以下四部分:
torchvision.models: 提供深度学习中各种经典的网络结构、预训练好的模型,如:Alex-Net、VGG、ResNet、Inception等。
torchvision.datasets:提供常用的数据集,设计上继承 torch.utils.data.Dataset,主要包括:MNIST、CIFAR10/100、ImageNet、COCO等。
torchvision.transforms:提供常用的数据预处理操作,主要包括对Tensor及PIL Image对象的操作。
torchvision.utils:工具类,如保存张量作为图像到磁盘,给一个小批量创建一个图像网格。
原文链接:https://blog.csdn.net/qq_45772756/article/details/128651447
torchvision 的 transforms 集成了随机翻转、旋转、增强对比度、
转化为tensor、转化为图像等功能,用于数据增强,非常便捷
原文链接:https://blog.csdn.net/iteapoy/article/details/106121752"""
import time
from matplotlib import pyplot as plt

pipline_train=transforms.Compose([
    #随机旋转图片
    transforms.RandomHorizontalFlip(),
    #将图片尺寸resize到32*32
    transforms.Resize((32,32)),
    #将图片转化为Tensor格式
    transforms.ToTensor(),
    #正则化(当模型出现过拟合的情况时,用来降低模型的复杂度)
    transforms.Normalize((0.1307,),(0.3081,))
])
"""标准化系数就是计算要用到的均值和标准差,在本例中是((0.1307,), (0.3081,)),
    均值是 0.1307,标准差是 0.3081,这些系数都是数据集提供方计算好的数据。
    不同数据集就有不同的标准化系数,另外还需要保持train_set、val_set和test_set标准化系数的一致性"""
pipline_test=transforms.Compose([
    # 将图片尺寸resize到32*32
    transforms.Resize((32, 32)),
    transforms.ToTensor(),
    transforms.Normalize((0.1307,),(0.3081,))
])
#下载数据集
train_set=datasets.MNIST(root="./data",train=True,download=True,transform=pipline_train)
test_set=datasets.MNIST(root="./data",train=False,download=True,transform=pipline_test)
#加载数据集
trainloader=torch.utils.data.DataLoader(train_set,batch_size=64,shuffle=True)
testloader=torch.utils.data.DataLoader(test_set,batch_size=32,shuffle=False)


#搭建LeNet-5网络
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        #定义激活函数,方便下面的使用
        self.relu=nn.ReLU()
        # 创建卷积和池化层
        # 创建第一个卷积层
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)  # kernel_size=5代表5*5的卷积核
        self.maxpool1 = nn.MaxPool2d(kernel_size=2, stride=2)  # 在池化层中步长和池化的窗口大小一致
        # 尺寸的逻辑:池化层未改变通道数;当前通道数为6
        # 创建第二个卷积层
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
        self.maxpool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        #创建三个全连接层
        #第一个全连接层的输出神经元个数为120, 第三个全连接层输出神经元个数为分类标签的类别数
        self.fc1 = nn.Linear(in_features=16*5*5, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=84)
        self.fc3 = nn.Linear(in_features=84, out_features=10)

    def forward(self, x):
        x=self.conv1(x)
        x=self.relu(x)
        x=self.maxpool1(x)
        x=self.conv2(x)
        x=self.relu(x)
        x=self.maxpool2(x)
        x=x.view(-1,16*5*5) # 使用view函数展平成一维向量
        """其实在torch里面,view函数就相当于numpy的reshape,
        执行的操作就是对tensor进行维度转换。这边的这里-1代表自动调整这个维度上的元素个数,以保证元素的总数不变"""
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        output=F.log_softmax(x,dim=1)
        """对softmax的结果进行log,log_softmax解决函数overflow和underflow,加快运算速度,提高数据稳性定。防止溢出.
        dim=0:对每一列的所有元素进行softmax运算,并使得每一列所有元素和为1。
        dim=1:对每一行的所有元素进行softmax运算,并使得每一行所有元素和为1。"""
        return output


#创建模型,部署gpu
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
model=LeNet().to(device)
#定义优化器
optimizer=optim.Adam(model.parameters(),lr=0.001)


#定义训练过程
def train_runner(model,device,trainloader,optimizer,epoch):
    #训练模型,启用BatchNormalization和Dropout,将BatchNormalization和Dropout置为True
    model.train()
    total=0
    correct=0.0

    #enumerate迭代已加载的数据集,同时获取数据和数据下标
    for i,data in enumerate(trainloader,0):  #train_loader是一个数据加载器,0是一个可选参数,用于指定枚举对象的起始索引。
        """enumerate()用于可迭代、可遍历的数据对象组合为一个索引序列,同时列出数据和数据下标,
        上面代码的0表示从索引从0开始,假如为1的话,那索引就从1开始.
        data里面包含图像数据(inputs)(tensor类型的)和标签(labels)(tensor类型)。"""
        inputs,labels=data
        #把模型部署到device上
        inputs,labels=inputs.to(device),labels.to(device)
        #初始化梯度,将优化器的梯度清零,以便在下一次迭代中计算新的梯度。
        optimizer.zero_grad()
        #保存训练结果
        outputs=model(inputs)
        #计算损失和
        #多分类情况通常使用cross_entropy(交叉熵损失函数), 而对于二分类问题, 通常使用sigmoid
        loss=F.cross_entropy(outputs,labels)
        #获取最大概率的预测结果
        #dim=1表示返回每一行的最大值对应的下标
        predict=outputs.argmax(dim=1)
        #累加总样本数和正确预测的样本数。
        total+=labels.size(0)   #size()主要是用来统计矩阵元素个数,或矩阵某一维上的元素个数的函数。
        correct+=(predict==labels).sum().item()
        #反向传播
        loss.backward()
        #使用优化器更新模型参数
        optimizer.step()
        if i%1000==0:
            # loss.item()表示当前loss的数值
            print("Train Epoch{} \t Loss:{:.6f},accuracy:{:.6f}%".format(epoch,loss.item(),100*(correct/total)))
            Loss.append(loss.item())
            Accuracy.append(correct/total)
    return loss.item(),correct/total


#定义测试过程
def test_runner(model,device,testloader):
    # 模型验证, 必须要写, 否则只要有输入数据, 即使不训练, 它也会改变权值
    # 因为调用eval()将不启用 BatchNormalization 和 Dropout, BatchNormalization和Dropout置为False
    model.eval()
    #统计模型正确率,设置初始值
    correct=0.0
    test_loss=0.0
    total=0
    #torch.no_grad将不会计算梯度,也不会进行反向传播
    with torch.no_grad():
        for data,label in testloader:
            data,label=data.to(device),label.to(device)
            output=model(data)
            test_loss+=F.cross_entropy(output,label).item()
            predict=output.argmax(dim=1)
            #计算正确数量
            total+=label.size(0)
            correct+=(predict==label).sum().item()
        #计算损失值
        print("test_avarage_loss:{:.6f},accuracy: {:.6f}%".format(test_loss/total, 100*(correct/total)))


#开始运行
epoch=5
Loss=[]
Accuracy=[]
for epoch in range(1,epoch+1):
    print("start_time",time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
    loss,acc=train_runner(model,device,trainloader,optimizer,epoch)
    Loss.append(loss)
    Accuracy.append(acc)
    test_runner(model,device,testloader)
    print("end_time: ",time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())),'\n')

print('Finished Training')

plt.subplot(2,1,1)
"""plt.subplot(2, 1, 1)是matplotlib.pyplot库中的一个函数,用于在一张图中创建多个子图。这个函数的参数表示子图的布局和当前激活的子图。
在这个例子中,`2` 表示子图的行数,`1` 表示子图的列数,`1` 表示当前激活的子图索引。因此,这行代码将创建一个包含两行一列子图的图,并激活第一个子图"""
plt.plot(Loss)
plt.title('Loss')
plt.show()
plt.subplot(2,1,2)
plt.plot(Accuracy)
plt.title('Accuracy')
plt.show()

#保存模型
print(model)
torch.save(model,'./models/models-mnist.pth')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186

test.py的代码如下:

import cv2
import matplotlib.pyplot as plt
import torch
import torch.nn.functional as F
from torchvision import datasets,transforms
from model import LeNet

if __name__=='__main__':
    device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model=torch.load('./models/models-mnist.pth')
    model=model.to(device)
    model.eval()   #把模型转为test模式

    #读取要预测的图片
    img=cv2.imread("./images/5_5.jpg")
    img=cv2.resize(img,dsize=(32,32),interpolation=cv2.INTER_NEAREST)
    plt.axis('off')#不显示坐标
    plt.show()

    #导入图片,图片扩展后为[1,1,32,32]
    trans=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])
    img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#图片转为灰度图,因为mnist数据集都是灰度图
    img=trans(img)
    img=img.to(device)
    img=img.unsqueeze(0) #图片扩展多一维,因为输入到保存的模型中是4维的[batch_size,通道,长,宽],而普通图片只有三维,[通道,长,宽]

    #预测
    output=model(img)
    prob=F.softmax(output,dim=1)#prob是10个分类的概率
    print("概率:",prob)
    value,predicted=torch.max(output.data,1)
    predict=output.argmax(dim=1)
    print("预测类别:",predict.item())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

(8)至此,深度学习中的Hello World完成。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/625318
推荐阅读
相关标签
  

闽ICP备14008679号