从零开始写模型LeNet

个人博客同步更新,界面更美观呀

Introduction

近来安装了MindSpore框架,便想测试一下它和Pytorch等其他深度学习框架有什么区别。首先便从模型的训练耗时和测试耗时来测试,MindSpore官方提供了使用LeNet解决MNIST手写数字识别的Demo,此时还需要一个用Pytorch编写的作为对比,为了使参数、模型等各方面尽可能一致,便有了照猫画虎自己编写的需求。

Coding

为了能总览模型编写训练流程,我决定从__main__函数入手,用到什么写什么。

parser

首先要用的就是一个命令行代码的解析工具,使用方法概括如下:

# 导包
import argparse
# 定义一个解析器
parser = argparse.ArgumentParser(description="")
# 给解析器添加需要从命令行读入的参数
parser.add_argument('--device_target', type=str, default="CPU", choices=['GPU', 'CPU'])
# 调用解析函数生成参数对象
args = parser.parse_args()

dataset

准备数据集,最好单独封装为一个处理函数,提供路径加载临时下载两种方式。

super parameter

超参数的设置,其实也可以放到第一步命令行参数解析里面。

模型的定义

__main__函数中实例化一个模型:

model = LeNet()

在前面预先定义模型类:

class LeNet(nn.Module):
    """lenet network structure."""
    def __init__(self, num_channel=1, num_class=10):
        self.conv1 = nn.Conv2d(in_channels=num_channel, out_channels=6, 
            kernel_size=5, stride=1, padding=2) 
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, num_class)
        self.relu = nn.ReLU()
        self.max_pool2d = nn.MaxPool2d(2, 2)

    def forward(self, x):
        x = self.max_pool2d(self.relu(self.conv1(x)))
        x = self.max_pool2d(self.relu(self.conv2(x)))
        # 展开为一维
        x = x.view(x.size()[0], -1)
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x

模型的训练

训练的过程就是送入数据执行计算过程,得到输出结果,与标准值进行误差计算,将误差进行梯度反向传播更新模型参数。然后重复此过程,训练结束条件可以为设定的epoch次数,或者给定一个收敛条件(误差的缩小程度已经不值得继续训练了)。

**损失函数:**此例采用交叉熵损失函数,多用于分类问题。

loss_function = nn.CrossEntropyLoss()

**优化器:**即计算得到的损失以怎样的方式作用到模型参数上,此例采用随机梯度下降(SGD)。

optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)

训练循环:

第一层循环一般为epoch维度的循环,简单说就是在这个数据集上训练几次,epoch=2即利用这个数据集进行两次训练过程。第二层循环就是在batch上的训练了,对所有数据分批进行前向计算和反向传播。

训练体一般有如下流程:

  1. 对得到的数据x标签label进行device上的转换,比如使用了GPU。
  2. 前向计算:pred=model(x)
  3. 计算损失(看看算的对不对):loss=loss_function(pred, label)
  4. 反向传播(不对的话如何改正):先要对梯度进行一个清零操作optimizer.zero_grad(),然后进行反向传播loss.backward(),执行一个优化步optimizer.step()。

至此一个训练过程就结束了,有需要的话可以在不同逻辑层进行损失准确率等参数的打印输出,方便监测训练状况。

for epoch in range(1, epoch_size+1):
        # data loop
        for batch_idx, (img, label) in enumerate(train_loader):
            img = img.to(device)
            label = label.to(device)
            img, label = Variable(img), Variable(label)
            # 前向传播
            pred = model(img)
            # 计算损失
            loss = loss_function(pred, label)
            # 梯度清零
            optimizer.zero_grad()
            # 误差反向传播
            loss.backward()
            # 优化步
            optimizer.step()
            # 为了和MindSpore版本保持一致 每次都打印一遍
            print("epoch: {} step: {}, loss id {}".format(epoch, batch_idx, loss.item()))

模型的测试

测试过程即对一些样本逐个进行预测并记录是否正确,最终输出准确率、召回率等参数指标。

eval_acc = 0
    for img, label in test_loader:
        img = img.to(device)
        label = label.to(device)
        img, label = Variable(img), Variable(label)
        # 预测
        output = model(img)
        _, pred = torch.max(output, 1)
        num_cortect = (pred == label).sum()
        eval_acc += num_cortect.item()
acc = eval_acc / (len(test_loader.dataset))

Experiment

对于添加了argparse的训练脚本来说,执行命令一般为:

python lenet.py --device_target=GPU --other=otherValue

需要通过命令行传入的参数以如上方式进行匹配赋值。

运行无误的话就能得到预期的结果反馈,有错误就根据提示Debug啦。

执行命令一般为:

python lenet.py --device_target=GPU --other=otherValue

需要通过命令行传入的参数以如上方式进行匹配赋值。

运行无误的话就能得到预期的结果反馈,有错误就根据提示Debug啦。

从零开始写模型LeNet

上一篇:CNN 经典骨架网络汇总


下一篇:LeNet网络实现cifar10数据集分类