乐趣区

关于图像识别:MindSpore网络实战系列使用LeNet实现图像分类任务

摘要

本篇的次要内容是解析一下应用 MindSpore 深度学习框架训练 LeNet 网络对 Mnist 数据集进行分类。首先我给大家展现出本篇内容的一个示意图,帮忙大家更直观的看到训练过程的一个重要步骤,如图所示,其中 1、2、3…示意训练过程中的秩序,上面咱们也将从这些秩序进行解析。

训练导图

数据集(Mnist)
Mnist 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字形成,其中 50% 是高中学生,50% 来自人口普查局(the Census Bureau)的工作人员,测试集(test set)也是同样比例的手写数字数据。

Mnist 数据集构造
它蕴含了以下四个局部,数据集中,训练样本:共 60000 个,其中 55000 个用于训练,另外 5000 个用于验证。测试样本:共 10000 个,验证数据比例雷同。MNIST 数据集下载页面:http://yann.lecun.com/exdb/mn…。

请依照图 1 中地位的关系进行寄存。

图 1:数据集结构图

Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 蕴含 60,000 个样本)
Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 蕴含 60,000 个标签)
Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 蕴含 10,000 个样本)
Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 蕴含 10,000 个标签)
MNIST 数据集曾经成为一个“榜样”,很多教程都会抉择从它下手。本次咱们应用 MindSpore 深度学习框架进行加载。

检查数据集
在咱们将 Mnist 数据集下载寄存好之后,咱们须要检查一下数据集的完整性和品质,应用 MindSpore 深度学习框架撸它,上代码:

  1. import matplotlib.pyplot as plt
  2. import matplotlib
  3. import numpy as np
  4. import mindspore.dataset as ds
  5. train_data_path = “./datasets/MNIST_Data/train”
  6. test_data_path = “./datasets/MNIST_Data/test”
  7. mnist_ds = ds.MnistDataset(train_data_path)
  8. print(‘The type of mnist_ds:’, type(mnist_ds))
  9. print(“Number of pictures contained in the mnist_ds:”, mnist_ds.get_dataset_size())
  10. dic_ds = mnist_ds.create_dict_iterator()
  11. item = next(dic_ds)
  12. img = item[“image”].asnumpy()
  13. label = item[“label”].asnumpy()
  14. print(“The item of mnist_ds:”, item.keys())
  15. print(“Tensor of image in item:”, img.shape)
  16. print(“The label of item:”, label)
  17. plt.imshow(np.squeeze(img))
  18. plt.title(“number:%s”% item[“label”].asnumpy())
  19. plt.show()

数据加强
以上咱们查看了数据集的正确性,然而想要这样应用还是不行的,咱们还须要将数据集解决成能够喂入网络模型的规格,该过程也能够称为数据预处理,对应于训练导图中的步骤 1。

这里还能够再具体的分为:读入数据集;定义操作并作用到数据集;进行 shuffle、batch 操作。咱们应用 MindSpore 深度学习框架撸它,上代码:

  1. 首先导入 MindSpore 中 mindspore.dataset 和其余相应的模块。

  2. import mindspore.dataset as ds
  3. import mindspore.dataset.transforms.c_transforms as C
  4. import mindspore.dataset.vision.c_transforms as CV
  5. from mindspore.dataset.vision import Inter
  6. from mindspore import dtype as mstype
  7. 定义预处理操作

  8. def create_dataset(data_path, batch_size=32, repeat_size=1,
  9. num_parallel_workers=1):
  10. 定义数据集

  11. mnist_ds = ds.MnistDataset(data_path)
  12. resize_height, resize_width = 32, 32
  13. rescale = 1.0 / 255.0
  14. shift = 0.0
  15. rescale_nml = 1 / 0.3081
  16. shift_nml = -1 * 0.1307 / 0.3081
  17. 定义所须要操作的 map 映射

  18. resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR)
  19. rescale_nml_op = CV.Rescale(rescale_nml, shift_nml)
  20. rescale_op = CV.Rescale(rescale, shift)
  21. hwc2chw_op = CV.HWC2CHW()
  22. type_cast_op = C.TypeCast(mstype.int32)
  23. 应用 map 映射函数,将数据操作利用到数据集

  24. mnist_ds = mnist_ds.map(operations=type_cast_op, input_columns=”label”, num_parallel_workers=num_parallel_workers)
  25. mnist_ds = mnist_ds.map(operations=resize_op, input_columns=”image”, num_parallel_workers=num_parallel_workers)
  26. mnist_ds = mnist_ds.map(operations=rescale_op, input_columns=”image”, num_parallel_workers=num_parallel_workers)
  27. mnist_ds = mnist_ds.map(operations=rescale_nml_op, input_columns=”image”, num_parallel_workers=num_parallel_workers)
  28. mnist_ds = mnist_ds.map(operations=hwc2chw_op, input_columns=”image”, num_parallel_workers=num_parallel_workers)
  29. 进行 shuffle、batch 操作

  30. buffer_size = 10000
  31. mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size)
  32. mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True)
  33. return mnist_ds

通过运行上例代码就能够实现咱们的第 1 步了,准备好咱们的数据集,当初筹备咱们的配角 LeNet 网络。

网络(LeNet5)
LeNet 诞生于 1994 年,是最早的卷积神经网络之一,并且推动了深度学习畛域的倒退。自从 1988 年开始,在许多次胜利的迭代后,这项由 Yann LeCun 实现的开拓性成绩被命名为 LeNet5。LeNet5 是一种用于手写体字符识别的十分高效的卷积神经网络,这与 Mnist 数据集正好匹配。

LeNet5 构造
首先是最最具备代表性的 LeNet5 网络结构图,从图中咱们能够看到每层之间的变换,包含卷积、池化和全连贯三种变换形式。这里咱们简略介绍下三种变换形式对图像的作用。

卷积:首先卷积须要一个卷积核,卷积核的卷积计算过程就相当于一个滤波器,能够让图像的边缘更加显著。

池化:通常图像中相邻的像素具备很大相似性,因而通过卷积后输入的像素值也仍然有此个性,这意味着卷积输入的信息中存在冗余,池化能够做到缩小图像中的信息冗余。

全连贯:全连贯在整个卷积神经网络中起到分类器的作用,卷积和池化是将原始数据映射到隐层特色空间中,全连贯层就是把学到的特色映射到样本空间中,以此实现分类。

图 2:LeNet5 结构图

介绍了不同变换形式,上面咱们通过数值计算,看一下图像的变动。

LeNet5 的数值计算
如图 2 中所示,是 LeNet 卷积网络的整体流程图,整体蕴含 8 个网络层,上面咱们将理解每一层的计算。

输出层:咱们应用的数据集是 MNIST 数据集,该数据集中的样本数据都是规格为 32×32 的灰度图,咱们以 1 个样本图片为例。那么咱们输出的图片规格就是 1×1×32×32,示意一个通道输出 1 个 32×32 的数组。

C1 层:C1 层中数组规格为 6×1×28×28,从 1×1×32×32 卷积失去。首先须要 6 个批次的卷积数组,每一个批次中都有 1 个规格为 5×5 的卷积数组,卷积步幅默认为 1。即卷积数组规格为 6×1×5×5。

该卷积层共有 6 +1×5×5×6=156 个参数,其中 6 个偏置参数。这一层网络 ** 有 6×1×28×28=4704 个节点,每个节点和以后层 5×5=25 个节点相连,所以本层卷积层共有 6×(1×28×28)×(1×5×5+1)=122304 个全连贯。

S2 层:S2 层的数组规格为 6×1×14×14,从 1×1×28×28 卷积失去。应用的是 2×2,步幅为 1 的最大池化操作,所以并不扭转批次数,只是将每一个输出数组从 28×28 降到 14×14 的输入数组。

该池化层共有 6×2=12 个可训练参数,以及 6×(1×14×14)×(2×2+1)=5880 个全连贯。

C3 层:C3 层的数组规格为 16×1×10×10,从 6×1×14×14 卷积失去。输入通道数数扭转,所以卷积数组须要 16 批卷积数组,每一批中有 6 个卷积核与输出通道对应,每一个卷积数组规格都是 5×5,步幅为 1。即卷积数组规格为 16×6×5×5。

该卷积层共有 16+1×5×5×16=2416 个参数,其中 16 个偏置参数。这一层网络 ** 有 16×1×10×10=1600 个节点,每个节点和以后层 5×5=25 个节点相连,所以本层卷积层共有 16×(1×10×10)×(1×5×5+1)=41600 个全连贯。

S4 层:S4 层的数组规格为 16×1×5×5,这一层池化与 S2 层池化设置雷同。所以输入数组只扭转每一个数组的规格,不扭转数量。

该池化层共有 16×2=32 个可训练参数,以及 16×(1×5×5)×(2×2+1)=2000 个全连贯。

C5 层:C5 层是规格为 120×1 的一维向量,那么须要将 S4 层数组转换成一维向量,输出的数组规格是 1×(16×1×5×)=1×400。应用全连贯层将 1×400 转为 1×120 的向量。在全连贯层中,每一个节点计算处后果后,都须要再通过激活函数计算,得出的值为输入的值。

该连贯层共有 5×5×16=400 个输出节点,参数个数为 5×5×16×120+120=48120 个,输入节点 120 个。

F6 层:F6 层是规格为 84×1 的一维向量,与 C5 层计算雷同,也是通过全连贯层计算失去。为什么要转成 84 个神经元向量呢,如下图中所示,是所有字符规范格局,规格为 12×7. 所以有 84 个像素点,而后应用 F6 层的向量与这些标准图计算类似度。

该连贯层共有 120 个输出节点,参数个数为 120×84+84=10164 个,输入节点 84 个。

图 3:字符标准图

输入层:该连贯层共有 84 个输出节点,参数个数为 84×10+10=850 个,输入节点 10 个。

输入层应用 Softmax 函数做多分类,在 Softmax 用于多分类过程中,它将多个神经元的输入,映射到(0,1)区间中,能够看作是每一个类别的概率值,从而实现多分类。Softmax 从字面上来看,能够分成 Soft 和 max 两局部。Softmax 的外围是 Soft,对于图片分类来说,一张图片或多或少都会蕴含其它类别的信息,咱们更期待失去图片对于每个类别的概率值,能够简略了解为每一个类别的可信度;max 就是最大值的意思,抉择概率值最大的当作分类的类别。

LeNet5 的创立
下面咱们通过图片和每层的计算解析了 LeNet 的构造和计算,上面咱们开始用 MindSpore 撸它,上代码:

  1. import mindspore.nn as nn
  2. from mindspore.common.initializer import Normal
  3. class LeNet5(nn.Cell):
  4. “””
  5. Lenet 网络结构
  6. “””
  7. def __init__(self, num_class=10, num_channel=1):
  8. super(LeNet5, self).__init__()
  9. 定义所须要的运算

  10. self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode=’valid’)
  11. self.conv2 = nn.Conv2d(6, 16, 5, pad_mode=’valid’)
  12. self.fc1 = nn.Dense(16 5 5, 120, weight_init=Normal(0.02))
  13. self.fc2 = nn.Dense(120, 84, weight_init=Normal(0.02))
  14. self.fc3 = nn.Dense(84, num_class, weight_init=Normal(0.02))
  15. self.relu = nn.ReLU()
  16. self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2)
  17. self.flatten = nn.Flatten()
  18. def construct(self, x):
  19. 应用定义好的运算构建前向网络

  20. x = self.conv1(x)
  21. x = self.relu(x)
  22. x = self.max_pool2d(x)
  23. x = self.conv2(x)
  24. x = self.relu(x)
  25. x = self.max_pool2d(x)
  26. x = self.flatten(x)
  27. x = self.fc1(x)
  28. x = self.relu(x)
  29. x = self.fc2(x)
  30. x = self.relu(x)
  31. x = self.fc3(x)
  32. return x
  33. 实例化网络

  34. net = LeNet5()

通过运行上例代码就能够实现咱们的第 2 步了,构建出了咱们的模型,当初最次要的两大部件准备就绪,持续向下走。

损失函数
如果就此开始训练,也就是执行第 3 步,通过初始的模型分类之后会存在分类谬误的状况,为了晋升模型的分类能力,咱们须要可能调整模型参数,那么损失函数就呈现了,损失函数能够通过数值很直观的展现模型此刻的性能,损失值越大示意模型性能越差。

损失函数原理
损失函数也有很多,为什么是 SoftmaxCrossEntropyWithLogits 损失函数呢?咱们再来理解一下本次我的项目的目标:图像分类。那么分类中的损失函数是怎么计算的,它是计算 logits 和标签之间的 softmax 穿插熵。应用穿插熵损失测量输出概率(应用 softmax 函数计算)与类别互斥(只有一个类别为正)的指标之间的散布误差,具体公式能够示意成图 4。

图 4:SoftmaxCrossEntropyWithLogits 表达式

参数阐明:

logits (Tensor) – Tensor of shape (N, C). Data type must be float16 or float32.
labels (Tensor) – Tensor of shape (N,). If sparse is True, The type of labels is int32 or int64. Otherwise, the type of labels is the same as the type of logits.
第一个参数 logits:就是神经网络最初一层的输入,如果有 batch 的话,它的大小就是[batchsize,num_classes],单样本的话,大小就是 num_classes;第二个参数 labels:理论的标签,大小同上。

损失函数的调用
下面是解释了该类型损失函数的根本计算过程,但在咱们须要应用进行调用的时候还是相当简便的,应用 MindSpore 撸它,上代码:

  1. from mindspore import nn
  2. loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction=’mean’)

这里参考官网 API 咱们对做了如上的设置:抉择应用稠密格局为True,损失函数的缩小类型抉择为mean

优化器
下面损失函数的作用是能够计算出模型的性能,咱们也须要明确一点,Loss 值是咱们要最小化的值。那么如何才可能让 Loss 值最小化呢?就须要咱们调整网络模型中的参数,让模型更适应所训练的数据集,这也是模型的训练过程,在其中调整参数的就是优化器。

优化器原理
本次训练中咱们应用的是 Momentum,也叫动量优化器。为什么是它?上面咱们理解下它的工作原理,表达式如图 5 所示

图 5:Momentum 表达式

下面表达式中的 grad、lr、p、v 和 u 别离示意梯度、learning_rate、参数、矩和动量。其中的梯度是通过损失函数求导得出的,在训练过程中失去的 Loss 是一个间断值,那么它就有梯度可求,并反向流传给每个参数。Momentum 优化器的次要思维就是利用了相似挪动指数加权均匀的办法来对网络的参数进行平滑解决的,让梯度的摆动幅度变得更小。

参数阐明:

params (Union [list [Parameter], list [dict]]) – 当 params 是一个 dict 列表时,“params”、“lr”、“weight_decay”和“order_params”是能够解析的键。lr:可选。如果键中有“lr”,将应用相应学习率的值。如果没有,将应用 API 中的 learning_rate;weight_decay:可选。如果键中有“weight_decay”,将应用相应的权重衰减值。如果没有,将应用 API 中的 weight_decay;order_params:可选。如果键中有“order_params”,则该值必须是参数的程序,并且在优化器中将遵循该程序。dict 中没有其余键,并且 ‘order_params’ 值中的参数必须在组参数之一中;grad_centralization:可选。“grad_centralization”的数据类型是 Bool。如果“grad_centralization”在键中,将应用设置的值。如果不是,则 grad_centralization 默认为 False。该参数仅实用于卷积层。
learning_rate (Union [float, Tensor, Iterable, LearningRateSchedule] ) – 学习率的值或图表。当 learning_rate 是一个 Iterable 或者一维的 Tensor 时,应用动静学习率,那么第 i 步将取第 i 个值作为学习率。当 learning_rate 为 LearningRateSchedule 时,应用动静学习率,训练过程中会依据 LearningRateSchedule 的公式计算第 i 个学习率。当 learning_rate 是零维的浮点数或张量时,应用固定学习率。不反对其余状况。浮点学习率必须等于或大于 0。如果类型 learning_rate 是 int,它将被转换为 float。
动量 (float) – float 类型的超参数,示意挪动平均线的动量。它必须至多为 0.0。
weight_decay (int, float) – 权重衰减(L2 惩办)。它必须等于或大于 0.0。默认值:0.0。
loss_scale (float) – 损失比例的浮点值。它必须大于 0.0。个别状况下,应用默认值。仅当应用 FixedLossScaleManager 进行训练并且 FixedLossScaleManager 中的 drop_overflow_update 设置为 False 时,该值须要与 FixedLossScaleManager 中的 loss_scale 雷同。默认值:1.0。
use_nesterov (bool) – 图 5 中的 2 和 3 公式和参数 use_nesterov 相干,如果设置use_nesterov = True,则依照公式 2 计算,如果设置use_nesterov = False,则依照公式 3 计算。
优化器的调用
废话不多说,间接应用 MindSpore 撸它,上代码:

  1. from mindspore import nn
  2. lr = 0.01
  3. momentum = 0.9
  4. net_opt = nn.Momentum(network.trainable_params(), lr, momentum)

参考以上的参数解析,咱们对 Momentum 优化器的应用如下面代码。此时咱们实现了首图中的 2->4->6 的训练过程,该过程就是反复网络分类图片、计算损失函数、梯度反向流传、参数调整的过程。

精度
尽管损失函数值能够掂量网络的性能,对于图片分类的工作,应用精度能够更加精确的示意最终的分类后果。

精度表达式
精度表达式比较简单,也好了解。分母是总样本数,分子是分类正确的样本总数,对应于首图中 5。

图 6:Accuracy 表达式

精度调用
精度值也会随着训练过程实时更新,应用 MindSpore 撸他,上代码:

  1. from mindspore import nn
  2. metrics = nn. Accuracy()
  3. model = Model(net, net_loss, net_opt, metrics={“Accuracy”: Accuracy()})

基于下面代码,能够应用 Accuracy 性能。Acc 也是咱们须要最大化的值。

本篇内容中每一段代码为了展现不同接口的调用做了调整,残缺的图像分类工作脚本能够参考:https://gitee.com/mindspore/d…

总结

本次内容是以图像分类工作为例,对于工作首先要明确数据集的内容和构造,以及要实现的指标,而后依据指标抉择根本网络、损失函数、优化器和精度这几局部,这些的抉择和设置是十分重要的,会间接影响到模型的训练和最终的性能。其余工作也都能够基于本示例进行扩大降级,谢谢赏读。

退出移动版