深度学习(1)--LeNet

发布于:2022-12-13 ⋅ 阅读:(536) ⋅ 点赞:(0)

目录

一、LeNet

二、LeNet实现CIFAR10图像分类

1、CIFAR10

2、model部分代码

3、train部分代码 

3.1导入库

3.2是否可以使用GPU 

3.3预处理并加载CIFAR10

3.4显示图片

3.5建立模型

3.6训练模型

3.7保存模型

4、predict部分代码

4.1导入库

4.2预处理

4.3读取模型并预测新图片


一、LeNet

        LeNet诞生于1994年,作为最早的深度学习模型之一,被用于训练手写体识别。

        LeNet模型构造如下图:

        下面对上图中矩阵尺寸如何计算进行解释:

         矩阵尺寸计算公式:N=(W-F+2P)/S+1

W 输入图片尺寸W*W
F Filter大小F*F,卷积核大小
P Padding,填充在上下左右的行数,一般对称填充所以乘以2
S Stride,步长

        例如:C1(第一个卷积层):

                输入:32*32 ---W*W

                卷积核大小:5*5 ---F*F

                填充区域:默认为0

                步长:默认为1

                输出:28*28        (32-5+0)/1+1=28    

                     P1(第一个池化层):

                输入:28*28 ---W*W

                采样区域:2*2     默认采样区域等于步长,步长为2

                输出:14*14       28/2=14

LeNet详解参见:LeNet详解_CharlesOAO的博客-CSDN博客_lenet

二、LeNet实现CIFAR10图像分类

1、CIFAR10

        CIFAR-10 是由 Hinton 的学生整理的一个用于识别物体的小型数据集。一共包含 10 个类别的 RGB 彩色图片,图片尺寸为32*32,共500张训练图片,和10000张测试图片。数据集类别为'plane','car','bird','cat','deer','dog','frog','horse','ship','truck'。

2、model部分代码

import torch.nn as nn
import torch.nn.functional as F

class LeNet(nn.Module):
    def __init__(self):                     #矩阵尺寸计算大小:N=(W-F+2P)/S+1
        super(LeNet,self).__init__()
        self.conv1=nn.Conv2d(3,16,5)        #(self,in_channels,out_channels,kernel_size,stride=1,
                                            #padding=0,dilation=1,groups=1,bias=True,padding_mode='zeros')
        self.pool1=nn.MaxPool2d(2,2)        #(self,kernal_size,stride=None,padding=0,dilation=1,
                                            #return_indices=False,ceil_mode=False)
        self.conv2=nn.Conv2d(16,32,5)
        self.pool2=nn.MaxPool2d(2,2)
        self.fc1=nn.Linear(32*5*5,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=F.relu(self.conv1(x))
        x=self.pool1(x)
        x=F.relu(self.conv2(x))
        x=self.pool2(x)
        x=x.view(-1,32*5*5)                #全连接之前进行reshape,行数基于张量长度除以列数决定
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)                      
        return x

if __name__=="__main__":
    import torch
    model=LeNet()
    print(model)                            #输出模型

       初始化部分,后面的注释为该函数的参数顺序。

       前向传播forward部分,每次卷积和全连接都加入激活函数。最后一次全连接不添加softmax层是由于train.py中使用了cross_entropy_loss(损失交叉熵)函数,其中添加了softmax的部分,而前向传播再次加入softmax会导致二次权值差异压缩,模型会向错误的方向更新。

3、train部分代码 

3.1导入库

import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

3.2是否可以使用GPU 

device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")    #device设为有gpu使用gpu
print(device)

        输出cuda:0则可用GPU运行,输出CPU则使用CPU运行。

        在使用GPU和使用CPU替换时,需要在后文修改五处位置,分别在建立模型,正向传播和计算损失交叉熵,准确率计算中添加.to(device)

        如遇到警告,请检查python版本和cuda版本之间关系,cuda版本与电脑显存cuda之间关系,python虚拟环境中若干库与当前pytorch兼容关系,当前pytorch是否为GPU版本(cuda版本)等若干问题。

3.3预处理并加载CIFAR10

transform=transforms.Compose(                              #数据预处理
    [transforms.ToTensor(),                                #将数据转换为tensor结构,原数据PIL Image或numpy结构为[H,W,C],范围为(0,255)
                                                           #转换为[C,H,W],范围为[0,1]
     transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]    #transforms.Normalize(std=(0.5,0.5,0.5),mean=(0.5,0.5,0.5)),则其作用就是先将输入归一化到(0,1),再使用公式"(x-mean)/std",将每个元素分布到(-1,1)
)
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,            #下载路径为当前文件夹下data文件夹,用作train
                                      download=True,transform=transform)   #download为True代表进行下载,下完即可用False,transform使用上面的归一化操作,进行数据预处理
trainloader=torch.utils.data.DataLoader(trainset,batch_size=36,            #训练使用trainset训练集,每批次使用36张,shuffle=True为随机打乱
                                        shuffle=True,num_workers=0)        #num_workers为0默认值,值为8,4,2代表的是CPU的进程数,而不是GPU
testset=torchvision.datasets.CIFAR10(root='./data',train=False,
                                     download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset,batch_size=10000,
                                       shuffle=True,num_workers=0)

         testloader中batch_size=10000一批次同时加载10000张图片,在显示图片时记得调整参数

3.4显示图片

test_image,test_label=next(iter(testloader))
#print(test_image,test_label)
classes=('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')

def imshow(img):
    img=img/2+0.5                                #逆转了归一化,但是没有逆转totensor中将255->1的操作,所以像素较低
    npimg=img.numpy()
    plt.imshow(np.transpose(img,(1,2,0)))        #图片输出遵循[H,W,C]需要在逆转回来
    plt.show()
#显示当前画图的label
print(' '.join('%5s'%classes[test_label[j]]for j in range(16)))
#画图,根据test_loader此时batch_size共16张显示图像
imshow(torchvision.utils.make_grid(test_image))  #把若干图片拼成一副图像,并显示

3.5建立模型

net=LeNet()
net.to(device)                                   #用gpu跑,存在device
loss_function=nn.CrossEntropyLoss()              #损失交叉熵
optimizer=optim.Adam(net.parameters(),lr=0.0001) #网络中若干参数,包括权重W,偏置bias

3.6训练模型

for epoch in range(50):
    running_loss=0.0
    for step,data in enumerate(trainloader,start=0):
        inputs,labels=data
        optimizer.zero_grad()                          #清除历史梯度,防止对历史梯度进行累加
        outputs=net(inputs.to(device))                 #正向传播,存在device
        loss=loss_function(outputs,labels.to(device))  #计算损失交叉熵,存在device
        loss.backward()                     #反向传播
        optimizer.step()                    #学习率更新,由于Adam计算方式

        running_loss+=loss.item()           #计算误差损失
        if step%500==499:                   #每500次计算一次误差和acc
            with torch.no_grad():                           #with中的语句不计算误差损失梯度,防止内存崩掉
                outputs=net(test_image.to(device))          #正向传播,存在device
                predict_y=torch.max(outputs,dim=1)[1]       #网络输出最优解的索引,即标签类别
                accuracy=(predict_y==test_label.to(device)).sum().item()/test_label.size(0)  #用最优解的标签类别与真实的标签类别比较,求和,item()返回0或1值,
                                                                                             # 而不返回tensor值,总和除以总测试集数量,存在device

                print('[%d,%5d] train_loss: %.3f test_accuracy: %.3f'%    #迭代epoch和step
                      (epoch+1,step+1,running_loss/500,accuracy))
                running_loss=0
print('Finish Training')

3.7保存模型

save_path='./Lenet.pth'                  #保存模型
torch.save(net.state_dict(),save_path)   

net.state_dict()获取网络中各层参数字典形式,net.parameters()获取网络中参数值,但没有标签,tensor型

4、predict部分代码

4.1导入库

import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNet

4.2预处理

transforms=transforms.Compose(
    [transforms.Resize((32,32)),            #将预测图片转换成32*32的与lenet网络相同的input模型
     transforms.ToTensor(),
     transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]
)

4.3读取模型并预测新图片

net=LeNet()
net.load_state_dict(torch.load('Lenet.pth'))               #读取模型

img=Image.open('plane.jpeg')                                 #打开新图片
img=transforms(img)                                        #对图片进行预处理
im=torch.unsqueeze(img,dim=0)                              #由于模型训练所需的四维张量顺序是[B,C,H,W],而当前顺序为[C,H,W],缺少batch维度。

with torch.no_grad():                                      
    outputs=net(img)                                       #正向传播
    predict=torch.max(outputs,dim=1)[1].data.numpy()       #预测的最大可能性类别索引
    print((torch.softmax(outputs,dim=1).numpy())[0].max()) #输出预测率


classes=('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
print(classes[int(predict)])                               

测试图片为下图:

         输出准确率为0.9999,类别为plane

 参考视频:2.1 pytorch官方demo(Lenet)_哔哩哔哩_bilibili


网站公告

今日签到

点亮在社区的每一天
去签到