Pytorch从入门到放弃(1)——实现经典神经网络模型LeNet5

        由于一些原因,需要使用Pytorch深度学习框架。先后也接触了Caffe、TesorFlow、Pytorch这三个深度学习框架,以后也许会更多,个人觉得这些框架大同小异不必全都掌握,精通一个即可,没有办法上面要求没办法,让你用啥你就得用啥。个人觉得这三个框架安装起来要数Caffe安装起来最麻烦,但是在网络模型的修改与扩展上,个人觉得Caffe最舒服,Caffe有专门的网络定义文件,在上面修改一下即可,简单粗暴,Caffe一开始用起来有点麻烦,需要改几个文件,但是用了几次后就熟悉了,说白了就是越用越舒服。TensorFlow安装起来容易,但用起来个人感觉定义网络结构的时候有点儿麻烦,而且TensorFlow更新的快,许多当前版本的函数与方法,在后续的版本就被取消了,有点儿难受。个人感觉Pytorch安装像TensorFlow一样简单,而且定义网络结构时也比TensorFlow简单。

        接触任何一个深度学习框架,入门的第一个网络结构肯定是LeNet5,这篇博客记录了基于Pytorch的LeNet5的实现。

1.准备mnist手写数字数据集

下载地址:http://yann.lecun.com/exdb/mnist/

Pytorch从入门到放弃(1)——实现经典神经网络模型LeNet5_第1张图片

2. LeNet5网络模型

Pytorch从入门到放弃(1)——实现经典神经网络模型LeNet5_第2张图片

 

        第一层:卷积层

        这一层的输入就是原始的图像像素32*32*1。第一个卷积层过滤器尺寸为5*5,深度为6,偏置亦为6,不使用全0填充,长和宽的步长为1。所以这一层的输出:28*28*6,卷积层共有5*5*1*6+6=156个参数。

         第二层:池化层

        这一层的输入为第一层的输出,是一个28*28*6的节点矩阵。本层采用的过滤器大小为2*2,长和宽的步长均为2,所以本层的输出矩阵大小为14*14*6。

         第三层:卷积层

        本层的输入矩阵大小为14*14*6,使用的过滤器大小为5*5,深度为16,偏置为16。本层不使用全0填充,长和宽的步长为1。本层的输出矩阵大小为10*10*16。本层有5*5*6*16+16=2416个参数。

         第四层:池化层

        本层的输入矩阵大小10*10*16。本层采用的过滤器大小为2*2,长和宽的步长均为2,所以本层的输出矩阵大小为5*5*16。

         第五层:全连接层

        本层的输入矩阵大小为5*5*16,在LeNet-5论文中将这一层称为卷积层,但是因为过滤器的大小就是5*5,所以和全连接层没有区别。如果将5*5*16矩阵中的节点拉成一个向量,那么这一层和全连接层就一样了。本层的输出节点个数为120,总共有5*5*16*120+120=48120个参数。

        第六层:全连接层

        本层的输入节点个数为120个,输出节点个数为84个,偏置亦为84,总共参数为120*84+84=10164个。

        第七层:全连接层

        本层的输入节点个数为84个,输出节点个数为10个,偏置亦为10,总共参数为84*10+10=850。

3.基于Pytorch实现的LeNet5模型

软件环境:Pytorch1.0

初学Pytorch,个人认为主要有三个主要步骤:

1.定义网络的结构及前向传播过程。

 lenet.py

import torch.nn as nn
from collections import OrderedDict


class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        # 定义网络的前半部分卷积、池化、激活函数
        # (1, 6, kernel_size=(5, 5))),
        # 1:输入数据的通道数
        # 6:卷积核个数
        # (1, 6, kernel_size=(5, 5)))卷积核的尺寸
        # stride=2步长,默认为1
        # nn.Sequential,将若干个功能层的功能组合到一起
        self.convnet = nn.Sequential(OrderedDict([
            ('c1', nn.Conv2d(1, 6, kernel_size=(5, 5))),
            ('relu1', nn.ReLU()),
            ('s2', nn.MaxPool2d(kernel_size=(2, 2), stride=2)),
            ('c3', nn.Conv2d(6, 16, kernel_size=(5, 5))),
            ('relu3', nn.ReLU()),
            ('s4', nn.MaxPool2d(kernel_size=(2, 2), stride=2)),
            ('c5', nn.Conv2d(16, 120, kernel_size=(5, 5))),
            ('relu5', nn.ReLU())
        ]))

        #  定义后半部分的全连接层
        self.fc = nn.Sequential(OrderedDict([
            ('f6', nn.Linear(120, 84)),
            ('relu6', nn.ReLU()),
            ('f7', nn.Linear(84, 10)),
            ('sig7', nn.LogSoftmax(dim=-1))
        ]))
    # 定义网络的前向运算
    def forward(self, img):
        output = self.convnet(img)
        # 在第一个全连接层与卷积层连接的位置
        # 需要将特征图拉成一个一维向量
        output = output.view(img.size(0), -1)
        output = self.fc(output)
        return output

2.定义数据读取以及网络的优化过程。

3.实现网络模型的训练

 run.py

from lenet import LeNet5
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision.datasets.mnist import MNIST
import torchvision.transforms as transforms
from torch.utils.data import DataLoader

# 读取数据
data_train = MNIST('../data/mnist',
                   download=True,
                   transform=transforms.Compose([
                       transforms.Resize((32, 32)),
                       transforms.ToTensor()]))
data_test = MNIST('../data/mnist',
                  train=False,
                  download=True,
                  transform=transforms.Compose([
                      transforms.Resize((32, 32)),
                      transforms.ToTensor()]))

# num_workers=8 使用多进程加载数据
data_train_loader = DataLoader(data_train, batch_size=256, shuffle=True, num_workers=8)
data_test_loader = DataLoader(data_test, batch_size=1024, num_workers=8)

# 初始化网络
net = LeNet5()

# 定义损失函数
criterion = nn.CrossEntropyLoss()
# 定义网络优化方法
optimizer = optim.Adam(net.parameters(), lr=2e-3)

# 定义训练阶段
def train(epoch):
    net.train()
    loss_list, batch_list = [], []
    for i, (images, labels) in enumerate(data_train_loader):
        # 初始0梯度
        optimizer.zero_grad()
        # 网络前向运行
        output = net(images)
        # 计算网络的损失函数
        loss = criterion(output, labels)

        # 存储每一次的梯度与迭代次数
        loss_list.append(loss.detach().cpu().item())
        batch_list.append(i+1)

        if i % 10 == 0:
            print('Train - Epoch %d, Batch: %d, Loss: %f' % (epoch, i, loss.detach().cpu().item()))
        # 反向传播梯度
        loss.backward()
        # 优化更新权重
        optimizer.step()
		    # 保存网络模型结构
    torch.save(net.state_dict(), 'model//' + str(epoch) + '_model.pkl')



def test():
    # 验证阶段
    net.eval()
    total_correct = 0
    avg_loss = 0.0
    # 取消梯度,避免测试阶段out of memory
    with torch.no_grad():
        for i, (images, labels) in enumerate(data_test_loader):
            output = net(images)
            avg_loss += criterion(output, labels).sum()
            # 计算准确率
            pred = output.detach().max(1)[1]
            total_correct += pred.eq(labels.view_as(pred)).sum()

        avg_loss /= len(data_test)
        print('Test Avg. Loss: %f, Accuracy: %f' % (avg_loss.detach().cpu().item(), float(total_correct) / len(data_test)))


def train_and_test(epoch):
    train(epoch)
    test()


def main():
    for e in range(1, 16):
        train_and_test(e)


if __name__ == '__main__':
    main()

4.利用训练好的网络权重来重现网络模型。

 mnist_deploy.py

from lenet import LeNet5
import torch
from torchvision.datasets.mnist import MNIST
import torchvision.transforms as transforms
from torch.utils.data import DataLoader

# 读取数据
data_test = MNIST('./data/mnist',
                  train=False,
                  download=True,
                  transform=transforms.Compose([
                      transforms.Resize((32, 32)),
                      transforms.ToTensor()]))

# num_workers=8 使用多进程加载数据
data_test_loader = DataLoader(data_test, batch_size=1024, num_workers=8)

# 初始化网络
net = LeNet5()
net.load_state_dict(torch.load(r'model\1_model.pkl'))

def deploy():
    # 验证阶段
    # net.eval()
    total_correct = 0
    for i, (images, labels) in enumerate(data_test_loader):
        output = net(images)
        # 计算准确率
        pred = output.detach().max(1)[1]
        total_correct += pred.eq(labels.view_as(pred)).sum()
    print('Accuracy: %f' % ( float(total_correct) / len(data_test)))


def main():
    deploy()


if __name__ == '__main__':
    main()

以上是,学习过程中的一个记录与总结,如有理解错误,恳请批评指正。

完整代码地址:https://github.com/Sun-DongYang/Pytorch.git

你可能感兴趣的:(Pytorch从入门到放弃(1)——实现经典神经网络模型LeNet5)