LeNet

LeNet的结构如图所示,其网状结构比较简单,如果不包括输入,它一共是7层,输入层图像为32*32

卷积神经网络3D可视化传送门

  • C1 卷积层1

卷积核(过滤器):6个5*5

特征图:6个28*28

训练参数:1 *(5 * 5) * 6+6=156

原图经过卷积核后生成的特征图大小计算:
(输入-卷积核+加边像素)/步长+1 →(32-5+0)/1+1

训练参数的计算方法:原图数量 * (卷积核大小) * 卷积核个数 +偏置项,其中池化层不需要训练参数,一个卷积核对应一个偏置项

此外,我们发现,即便步长为1,经过卷积后特征图片的尺寸变小了。但是由于过滤器在移动到边缘的时候就结束了,中间的像素点比边缘的像素点参与计算的次数要多。因此越是边缘的点,对输出的影响就越小,就有可能丢失边缘的信息。Padding,在图片外围补充一些像素点,这些像素点的值初始化为0

  • S2 池化层1

池化窗口:6个2*2

特征图:6个14*14

池化的目的是降低数据的维度,过程很简单就是向下采样,一般有两种生成池化特征的方式,分别为最大值池化平均值池化

  • C3 卷积层2

卷积核:16个5*5

特征图:16个10*10

训练参数:6 * (5 * 5) * 16+16 =2416

如图所示,C3与S2的连接组合方式并不是固定的,C3的每一张特征图可以连接S2层中的全部或者部分特征图

  • S4 池化层2

池化窗口:16个2*2

特征图:16个5*5

  • C5 全连接层1

神经元结点:120个

训练参数:16 * (5 * 5)*120 +120=48120

每个神经元结点与S4层的16张特征图进行全连接

  • F6 全连接层2

神经元结点:84个

训练参数:120 * 84+84=10164

每个神经元与F5的120神经元全连接

  • OUTPUT 输出层

神经元结点:10个

训练参数:84 * 10+10=850

把输出神经元节点数设为10,代表0~9的分值

每个神经元与F6的84个神经元全连接


  • 1.构建LeNet

import torch
from torch import nn,optim
from torchvision import datasets,transforms
import torch.nn.functional as F
#datasets是加载图像数据的方法,transforms是图像数据预处理的方法

#定义数据预处理的方法
transform=transforms.Compose([
    transforms.ToTensor(),
    #将数据转化为Tensor对象
    transforms.Normalize((0.1307,),(0.3081,))
    #将数据进行归一化处理
])

#使用datasets.MNIST()函数分别下载训练数据集和测试数据集
trainset=datasets.MNIST('data',train=True,download=True,transform=transform)
testset=datasets.MNIST('data',train=False,download=True,transform=transform)
#第一个参数表示数据集下载并存储的目标文件夹,第二个参数True表示加载训练数据集,False表示加载测试数据集
#第三个参数表示自动下载,第四个参数表示使用刚才定义的数据预处理的方法

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        #c1,c3定义卷积层,nn.Conv2d()函数为我们简化了卷积层的搭建
        #第一个参数表示输入一张灰度图,第二个参数表示输出六张特征图
        #第三个参数(5,5)可以简化成5,表示大小为5*5的卷积核过滤器
        self.c1=nn.Conv2d(1,6,(5,5))
        self.c3=nn.Conv2d(6,16,5)
        #fc1,fc2,fc3定义连接层
        #输入特征为16*4*4,输出为120
        self.fc1=nn.Linear(16*4*4,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=F.max_pool2d(F.relu(self.c1(x)),2)
        #对c1特征图进行池化,池化核大小2*2,可以简化参数为2
        #c1卷积之后,使用relu()函数增加网络的非线性拟合能力
        x=F.max_pool2d(F.relu(self.c3(x)),2)
        x=x.view(-1,self.num_flat_features(x))
        #view函数将x的形状转化成1维的向量
        #在函数的参数中经常可以看到 - 1
        #例如x.view(-1, 4)这里 - 1,表示一个不确定的数,就是你如果不确定你想要reshape成几行,但是你很肯定要reshape成4列,那不确定的地方就可以写成 - 1例如一个长度的16向量x,
        #x.view(-1, 4)等价于x.view(4, 4)
        x=F.relu(self.fc1(x))
        #增加全连接后的非线性拟合能力
        x=F.relu(self.fc2(x))
        ##输出结果一般不使用relu激活
        x=self.fc3(x)
        return x

    # 自定义num_flat_features()用于计算特征点的总数
    #假设批量输入4张图片,信息为[4,16,4,4],表示经过view函数之前的是x是4维的,转化成特征图16张,每张4*4
    #因此计算特征点总数只需要后面的三维度size=x.size()[1:],即[16,4,4],16*4*4=256
    def num_flat_features(self, x):
        size = x.size()[1:]
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

CUDA=torch.cuda.is_available()
if CUDA:
    lenet=LeNet().cuda()
else:
    lenet=LeNet()

criterion=nn.CrossEntropyLoss()
#momentum=0.9,动量0.9抱歉,目前不晓得干啥用的
optimizer=optim.SGD(lenet.parameters(),lr=0.001,momentum=0.9)

#随后使用PyTorch的数据加载工具DataLoad来加载训练数据
trainloader=torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=0)
#其中batch_size表示一次性加载的数据量,shuffle=True遍历不同批次的数据时打乱顺序,num_workers=0表示使用0个子进程加载数据(2为报错点)

#下面开始训练LeNet,完全遍历训练数据2次
def train(model,criterion,optimizer,epochs=1):
    for epoch in range(epochs):
        running_loss=0.0
        for i,data in enumerate(trainloader,0):
            #enumerate表示从第0项开始枚举对trainloader中的数据进行枚举,返回i是序号,data是我们需要的数据
            inputs,labels=data
            if CUDA:
                inputs,labels=inputs.cuda(),labels.cuda()

            optimizer.zero_grad()
            outputs=model(inputs)
            loss=criterion(outputs,labels)
            loss.backward()
            optimizer.step()

            running_loss+=loss.item()
            #对输入每一个训练数据后的loss值进行累加,每训练1000次打印一次loss的均值
            if i%1000==999:
                print('[Epoch:%d,Batch:%5d] loss:%.3f' % (epoch+1,i+1,running_loss/1000))
                running_loss=0.0
    print('Finished Training')

train(lenet,criterion,optimizer,epochs=2)
  • 2.训练

[Epoch:1,Batch: 1000] loss:1.301
[Epoch:1,Batch: 2000] loss:0.340
[Epoch:1,Batch: 3000] loss:0.214
[Epoch:1,Batch: 4000] loss:0.166
[Epoch:1,Batch: 5000] loss:0.151
[Epoch:1,Batch: 6000] loss:0.132
[Epoch:1,Batch: 7000] loss:0.129
[Epoch:1,Batch: 8000] loss:0.108
[Epoch:1,Batch: 9000] loss:0.112
[Epoch:1,Batch:10000] loss:0.111
[Epoch:1,Batch:11000] loss:0.084
[Epoch:1,Batch:12000] loss:0.086
[Epoch:1,Batch:13000] loss:0.085
[Epoch:1,Batch:14000] loss:0.085
[Epoch:1,Batch:15000] loss:0.075
[Epoch:2,Batch: 1000] loss:0.061
[Epoch:2,Batch: 2000] loss:0.049
[Epoch:2,Batch: 3000] loss:0.072
[Epoch:2,Batch: 4000] loss:0.066
[Epoch:2,Batch: 5000] loss:0.070
[Epoch:2,Batch: 6000] loss:0.066
[Epoch:2,Batch: 7000] loss:0.072
[Epoch:2,Batch: 8000] loss:0.057
[Epoch:2,Batch: 9000] loss:0.061
[Epoch:2,Batch:10000] loss:0.050
[Epoch:2,Batch:11000] loss:0.059
[Epoch:2,Batch:12000] loss:0.059
[Epoch:2,Batch:13000] loss:0.044
[Epoch:2,Batch:14000] loss:0.059
[Epoch:2,Batch:15000] loss:0.056
Finished Training

从结果可以看出,我们的训练是有效的,损失值loss的平均值从1.301逐渐优化下降到0.056

  • 3.存储与加载

训练完神经网络之后,需要存储训练好的参数,以方便以后使用。
存储方式有两种,第一种是:存储与加载模型。第二种是:存储和加载模型参数

方法一:

//存储
torch.save(lenet,'model.pkl')
//加载
lenet=torch.load('model.pkl')

方法二:(更推荐)

//存储
torch.save(lenet.state_dict(),'model.pkl')
//加载
lenet.load_state_dict(torch.load('model.pkl'))

利用os包的exists()方法检查是否存在模型参数文件。构造load_param()和save_param()方便随时调用

def load_param(model,path):
    if os.path.exists(path):
        model.load_state_dict(torch.load(path))
def save_param(model,path):
    torch.save(model.state_dict(),path)
  • 4.测试

神经网络模型经过长时间的训练之后,能在训练集的数据上表现得很好,但是不一定在测试集上表现得很好,为此,设置train=False准备测试集,并设置testloader对测试集进行加载

testset=datasets.MNIST('data',train=False,download=True,transform=transform)

下面对训练完成的神经网络添加测试模块

def test(testloader,model):
    corret=0
    total=0
    for data in testloader:
        images,labels=data
        if CUDA:
            images=images.cuda()
            labels=labels.cuda()
        outputs=model(images)
        #找出每一组10个输出值中最大的那个值,0是每列的最大值,1是每行的最大值,并将该值所在的序号保存在predicted中
        _,predicted=torch.max(outputs.data,1)
        #total用于累计labels的总数
        total+=labels.size(0)
        #corret用于累计正确的结果
        corret+=(predicted==labels).sum()
    print('Accuracy on the test set: %d %%' %(100*corret/total))
  • 5.测试结果

Finished Training
Accuracy on the test set: 98 %
  • 6.完整程序

import torch
import os
from torch import nn,optim
from torchvision import datasets,transforms
import torch.nn.functional as F
#datasets是加载图像数据的方法,transforms是图像数据预处理的方法

#定义数据预处理的方法
transform=transforms.Compose([
    transforms.ToTensor(),
    #将数据转化为Tensor对象
    transforms.Normalize((0.1307,),(0.3081,))
    #将数据进行归一化处理
])

#使用datasets.MNIST()函数分别下载训练数据集和测试数据集
trainset=datasets.MNIST('data',train=True,download=True,transform=transform)
testset=datasets.MNIST('data',train=False,download=True,transform=transform)
#第一个参数表示数据集下载并存储的目标文件夹,第二个参数True表示加载训练数据集,False表示加载测试数据集
#第三个参数表示自动下载,第四个参数表示使用刚才定义的数据预处理的方法

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        #c1,c3定义卷积层,nn.Conv2d()函数为我们简化了卷积层的搭建
        #第一个参数表示输入一张灰度图,第二个参数表示输出六张特征图
        #第三个参数(5,5)可以简化成5,表示大小为5*5的卷积核过滤器
        self.c1=nn.Conv2d(1,6,(5,5))
        self.c3=nn.Conv2d(6,16,5)
        #fc1,fc2,fc3定义连接层
        #输入特征为16*4*4,输出为120
        self.fc1=nn.Linear(16*4*4,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=F.max_pool2d(F.relu(self.c1(x)),2)
        #对c1特征图进行池化,池化核大小2*2,可以简化参数为2
        #c1卷积之后,使用relu()函数增加网络的非线性拟合能力
        x=F.max_pool2d(F.relu(self.c3(x)),2)
        x=x.view(-1,self.num_flat_features(x))
        #view函数将x的形状转化成1维的向量
        #在函数的参数中经常可以看到 - 1
        #例如x.view(-1, 4)这里 - 1,表示一个不确定的数,就是你如果不确定你想要reshape成几行,但是你很肯定要reshape成4列,那不确定的地方就可以写成 - 1例如一个长度的16向量x,
        #x.view(-1, 4)等价于x.view(4, 4)
        x=F.relu(self.fc1(x))
        #增加全连接后的非线性拟合能力
        x=F.relu(self.fc2(x))
        ##输出结果一般不使用relu激活
        x=self.fc3(x)
        return x

    # 自定义num_flat_features()用于计算特征点的总数
    #假设批量输入4张图片,信息为[4,16,4,4],表示经过view函数之前的是x是4维的,转化成特征图16张,每张4*4
    #因此计算特征点总数只需要后面的三维度size=x.size()[1:],即[16,4,4],16*4*4=256
    def num_flat_features(self, x):
        size = x.size()[1:]
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

CUDA=torch.cuda.is_available()
if CUDA:
    lenet=LeNet().cuda()
else:
    lenet=LeNet()

criterion=nn.CrossEntropyLoss()
#momentum=0.9,动量0.9抱歉,目前不晓得干啥用的
optimizer=optim.SGD(lenet.parameters(),lr=0.001,momentum=0.9)

#随后使用PyTorch的数据加载工具DataLoad来加载训练数据
trainloader=torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=0)
#其中batch_size表示一次性加载的数据量,shuffle=True遍历不同批次的数据时打乱顺序,num_workers=0表示使用0个子进程加载数据(2为报错点)

testloader=torch.utils.data.DataLoader(testset,batch_size=4,shuffle=False)

#下面开始训练LeNet,完全遍历训练数据2次
def train(model,criterion,optimizer,epochs=1):
    for epoch in range(epochs):
        running_loss=0.0
        for i,data in enumerate(trainloader,0):
            #enumerate表示从第0项开始枚举对trainloader中的数据进行枚举,返回i是序号,data是我们需要的数据
            inputs,labels=data
            if CUDA:
                inputs,labels=inputs.cuda(),labels.cuda()

            optimizer.zero_grad()
            outputs=model(inputs)
            loss=criterion(outputs,labels)
            loss.backward()
            optimizer.step()

            running_loss+=loss.item()
            #对输入每一个训练数据后的loss值进行累加,每训练1000次打印一次loss的均值
            if i%1000==999:
                print('[Epoch:%d,Batch:%5d] loss:%.3f' % (epoch+1,i+1,running_loss/1000))
                running_loss=0.0
    print('Finished Training')

#存储与加载
def load_param(model,path):
    if os.path.exists(path):
        model.load_state_dict(torch.load(path))
def save_param(model,path):
    torch.save(model.state_dict(),path)


#下面对训练完成的神经网络添加测试模块
def test(testloader,model):
    corret=0
    total=0
    for data in testloader:
        images,labels=data
        if CUDA:
            images=images.cuda()
            labels=labels.cuda()
        outputs=model(images)
        #找出每一组10个输出值中最大的那个值,0是每列的最大值,1是每行的最大值,并将该值所在的序号保存在predicted中
        _,predicted=torch.max(outputs.data,1)
        #total用于累计labels的总数
        total+=labels.size(0)
        #corret用于累计正确的结果
        corret+=(predicted==labels).sum()
    print('Accuracy on the test set: %d %%' %(100*corret/total))

load_param(lenet,'model.pkl')
train(lenet,criterion,optimizer,epochs=2)
save_param(lenet,'model.pkl')
test(testloader,lenet)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值