Lenet手写数字识别代码解析

一:文章来源

初学神经网络,觉得博主的代码写的很不错,但其中很多代码在第一遍看的时候有很多地方不理解,后查阅了很多资料,终于看明白了,于是写了一篇笔记,记录自己的学习心得,有不准确的地方,还望各位大佬们不吝赐教~
【深度学习】卷积网络LeNet及其MXNet实现

二:导入各种模块

import mxnet as mx
from mxnet.gluon import nn, data as gdata, loss as gloss
from mxnet import init, gluon, autograd, nd

from matplotlib import pyplot as plt
# from IPython import display # 感觉用不上,就给注释掉了
import pylab

import sys
import time

三:数据处理

# 数据读取并形成批量,注意这里数据是保存在内存中,训练时需要将数据复制到显存
batch_size = 256  # 批量大小
# gdata即gluon.data,表示gluon模块的数据加载模块
# ToTensor()将shape为(H, W, C)的nump.ndarray或img转为shape为(C, H, W)的tensor,其将每一个数值归一化到[0,1],其归一化方法比较简单,直接除以255即可
# 不了解过多,知道是把原数据集转换成tensor格式就行
transformer = gdata.vision.transforms.ToTensor()  # 这时的transformer表示转化器,用于将原数据集转化成Tensor格式
num_workers = 0 if sys.platform.startswith('win32') else 4  # 非windows系统多线程加速数据读取,不知道干啥的
mnist_train = gdata.vision.MNIST(train=True)    # 加载MNIST数据集中的训练集,gdata.vision.MNIST表示取MNIST数据集,train=True表示取得训练集
# 已经取得完整的训练数据集(60000),接下来需要打乱数据集,并按批量大小取数据集来供模型训练,注意:train_iter中包含全部的训练数据
# 括号内的参数依次表示:对mnist_train按transformer进行格式转化,所取批量大小,打乱训练数据集,加速数据读取
train_iter = gdata.DataLoader(mnist_train.transform_first(transformer), batch_size, shuffle=True,
                              num_workers=num_workers)
# 原理同上,不同之处是测试集无需打乱
mnist_test = gdata.vision.MNIST(train=False)    # 加载MNIST数据集中的测试集
test_iter = gdata.DataLoader(mnist_test.transform_first(transformer), batch_size, shuffle=False,
                             num_workers=num_workers)
# 这时的train_iter,test_iter已经可以按批量提供数据集了                          

三:网络模型

# 模型的定义及初始化
lenet = nn.Sequential()	 # 定义了Sequential实例lenet
lenet.add(nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'),  # 卷积层模块,输出(批量大小,通道数,高,宽)
          nn.MaxPool2D(pool_size=2, strides=2),
          nn.Conv2D(channels=16, kernel_size=5, activation='sigmoid'),
          nn.MaxPool2D(pool_size=2, strides=2),
          nn.Dense(units=120, activation='sigmoid'),  # 全连接层,Dense会自动执行flatten操作,变为(批量大小,通道数*高*宽)
          nn.Dense(units=84, activation='sigmoid'),
          nn.Dense(units=10))
gpu_id = 0  # 通过nvidia-smi查看空闲GPU,如果哪个gpu空闲,就给gpu_id赋对应值就行
lenet.initialize(ctx=mx.gpu(gpu_id), init=init.Xavier())  # 通过ctx=gpu(gpu_id)将模型保存在显存中,若用gpu计算,这一步不可或缺

关于batch_size、iteration、epochs的详解,可以看这篇博客:
神经网络中Epoch、Iteration、Batchsize相关理解和说明

四:训练模型

# 模型训练
lr = 0.1  # 学习率
epochs = 20  # 训练次数
# 注册优化器/训练器,括号内第一个表示收集参数(实例.collect_params(),为约定俗成的用法),第二个表示优化算法,这里使用'sgd',指的是小批量随机梯度下降算法,第三个指优化算法的学习率
trainer = gluon.Trainer(lenet.collect_params(), optimizer='sgd', optimizer_params={'learning_rate': lr})
loss = gloss.SoftmaxCrossEntropyLoss()	# 定义损失函数,这里是softmax交叉熵函数,专门用于分类
train_acc_array, test_acc_array = [], []  # 记录训练过程中的数据,同时用于作图,含义分别是记录训练集和测试集每轮的准确率
for epoch in range(epochs): 	# 进行epochs轮循环
    train_los_sum, train_acc_sum = 0.0, 0.0  # 每轮epoch的损失和准确率
    epoch_start = time.time()  # epoch开始的时间
    for X, y in train_iter:	# 每次在train_iter中取batch_size个输入矩阵X,标签值y来优化参数,一共60000个数据,共取了235轮,除最后一轮是96个外,其余每轮都是256个
        X, y = X.as_in_context(mx.gpu(gpu_id)), y.as_in_context(mx.gpu(gpu_id))  # 将数据复制到GPU中
        with autograd.record():	 # 取梯度:始
            y_hat = lenet(X)
            los = loss(y_hat, y)
        los.backward()		# 取梯度:止   至此,损失函数关于w,b的梯度已经可用
        trainer.step(batch_size)	# 有了梯度后,优化器优化参数
        train_los_sum += los.mean().asscalar()  # 计算训练的损失(一个batch_size中所有损失的平均值)
        # axis=1是跨列进行操作,等式从右向左运算,在输出结果的y(一个列向量)中找出概率最大值的行下标(即预测结果),判断与标签值是否相等(结果值取0或1),一个batch_size中所有的结果值取平均,为这一个batch_size训练的准确率
        train_acc_sum += (y_hat.argmax(axis=1) == y.astype('float32')).mean().asscalar()  # 计算训练的准确率
    train_acc = train_acc_sum / len(train_iter)
    test_acc_sum = nd.array([0], ctx=mx.gpu(gpu_id))  # 计算模型此时的测试准确率
    for features, labels in test_iter:	 # 每次在test_iter中取batch_size个输入矩阵X,标签值y来优化参数,一共100个数据,共取了40轮,除最后一轮是16个外,其余每轮都是256个
        features, labels = features.as_in_context(mx.gpu(gpu_id)), labels.as_in_context(mx.gpu(gpu_id))
        # 计算测试的损失(一个batch_size所有损失的平均值)
        test_acc_sum += (lenet(features).argmax(axis=1) == labels.astype('float32')).mean()
    test_acc = test_acc_sum.asscalar() / len(test_iter)	# 测试集的准确率是所有batch_size测试完的正确次数除以测试集数目
    print('epoch %d, time %.1f sec, loss %.4f, train acc %.4f, test acc %.4f' %
          (epoch + 1, time.time() - epoch_start, train_los_sum / len(train_iter), train_acc,
           test_acc))
    # 训练过程中,每个epoch的训练准确率和测试准确率存入列表,方便后续作图
    train_acc_array.append(train_acc)
    test_acc_array.append(test_acc)

五:作图

plt.rcParams的属性总结:属性总结(三):plt.rcParams
这里是设置图像显示大小

# 作图
# display.set_matplotlib_formats('svg')  # 矢量图,我的电脑运行不起来,于是注释掉
plt.rcParams['figure.figsize'] = (3.5, 2.5)  # 图片尺寸
plt.xlabel('epochs')  # x轴的含义
plt.ylabel('accuracy')	 # y轴的含义
plt.plot(range(1, epochs + 1), train_acc_array)		# 根据第一、二个参数分别表示横、纵坐标进行绘图,图线形状默认
plt.plot(range(1, epochs + 1), test_acc_array, linestyle=":")  # 根据第一、二个参数分别表示横、纵坐标进行绘图,图线形状为点
plt.legend(['train accuracy', 'test accuracy'])	# 分别显示第一、二条线含义
pylab.show()  # 展示图片

版权声明:本文为qq_43138237原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。