mindspore实现手写数字识别

具体流程参考教程:MindSpore快速入门 MindSpore 接口文档
注:本文章记录的是我在开发过程中的学习笔记,仅供参考学习,欢迎讨论,但不作为开发教程使用。

数据的流水线处理

def datapipe(dataset, batch_size):'''数据处理流水线'''image_transform = [vision.Rescale(1.0 / 255.0, 0), # 缩放 output = image * rescale + shift.vision.Normalize(mean=(0.1307,), std=(0.3081,)),    # 根据平均值和标准偏差对输入图像进行归一化vision.HWC2CHW()    # 转换为NCHW格式]label_transform = transforms.TypeCast(mindspore.int32)  # 转为mindspore的int32格式dataset = dataset.map(image_transform, 'image')     # 对各个图像按照流水线处理dataset = dataset.map(label_transform, 'label')     # 对各个标签转换为int32dataset = dataset.batch(batch_size)return dataset

这段代码中对输入图片进行了缩放、归一化和格式转换三个操作,按照流水线运行。

流水线操作

数据流水线处理的介绍:【AI设计模式】03-数据处理-流水线(Pipeline)模式
总结而言,海量数据下,流水线模式可以实现高效的数据处理,当然也会占用更多的CPU和内存资源。

map操作

mindspore下dataset的map操作:第一个参数是处理函数列表,第二个参数是需要处理的列。
map函数会将数据集中第二个参数的指定的列作为输入,调用第一个参数的处理函数执行处理,如果有多个处理函数,上一个函数的输出作为下一个函数的输入。

NCHW和NHWC格式的优缺点

NCHW
缺点:必须等所有通道输入准备好才能得到最终输出结果,需要占用较大的临时空间。
优点:是 Nvidia cuDNN 默认格式,使用 GPU 加速时用 NCHW 格式速度会更快。(这个是什么原因呢?没找到资料_(:з」∠)_)
NHWC
缺点:GPU 加速较NCHW更慢
优点:访存局部性更好(每三个输入像素即可得到一个输出像素)
参考文章:【深度学习框架输入格式】NCHW还是NHWC?
为什么pytorch中transforms.ToTorch要把(H,W,C)的矩阵转为(C,H,W)?

模型

class Network(nn.Cell):'''Network model'''def __init__(self):super().__init__()self.flatten = nn.Flatten()self.dense_relu_sequential = nn.SequentialCell(nn.Dense(28*28, 512),nn.ReLU(),nn.Dense(512, 512),nn.ReLU(),nn.Dense(512, 10))def construct(self, x):x = self.flatten(x)logits = self.dense_relu_sequential(x)return logits

基类

mindspore的模型基类是mindspore.nn.Cell
pytorch的模型基类是torch.nn.Module

全连接层

mindspore的全连接层是mindspore.nn.Dense
pytorch的全连接层是torch.nn.Linear

模型连接

mindspore的顺序容器是mindspore.nn.SequentialCell
pytorch的顺序容器是torch.nn.Sequential

前向传播

mindspore的前向传播函数(要执行的计算逻辑)基类函数为construct(self, xxx)
pytorch的前向传播函数基类函数为forward(self, xxx)

损失函数和优化策略

my_loss_fn = nn.CrossEntropyLoss()
my_optimizer = nn.SGD(model.trainable_params(), 1e-2)

交叉熵:把来自一个分布q的消息使用另一个分布p的最佳代码传达方式计算得到的平均消息长度,即为交叉熵。针对交叉熵,这个文章讲的较好:损失函数:交叉熵详解

mindspore的交叉熵函数和pytorch类似:
前者是mindspore.nn.CrossEntropyLoss(),后者是torch.nn.CrossEntropyLoss()

训练

def train(model_train, dataset, loss_fn, optimizer):'''训练函数'''# Define forward functiondef forward_fn(data, label):logits = model_train(data)loss = loss_fn(logits, label)return loss, logits# Get gradient functiongrad_fn = ops.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=True)# Define function of one-step trainingdef train_step(data, label):(loss, _), grads = grad_fn(data, label)loss = ops.depend(loss, optimizer(grads))return losssize = dataset.get_dataset_size()model_train.set_train()for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):loss = train_step(data, label)if batch % 100 == 0:loss, current = loss.asnumpy(), batchprint(f"loss: {loss:>7f}  [{current:>3d}/{size:>3d}]")

value_and_grad

官网对value_and_grad函数的介绍如下:mindspore.ops.value_and_grad
按照官网的描述,这个函数的作用是:生成求导函数,用于计算给定函数的正向计算结果和梯度。
我们需要给这个函数传入模型的正向传输函数待求导的参数
其中模型的正向传输函数需要封装一下,返回loss的计算, 用于后续优化器的梯度计算;
待求导的参数可以写为model.trainable_params(),也可以由优化器提供(optimizer.parameters),因为优化器初始化时已经传入需要求导的参数。
总之,这个接口返回的是一个函数,函数的作用是把正向传播、反向传播的整个流程走一遍,最后的输出为正向传输函数的返回值+待求导参数的梯度值

depend算子

在训练时使用到了depend算子,官网对Depend函数的介绍如下:mindspore.ops.Depend

    # Define function of one-step trainingdef train_step(data, label):(loss, _), grads = grad_fn(data, label)loss = ops.depend(loss, optimizer(grads))return loss

经询问分析,使用depend算子的原因是,在静态图模式下,函数执行的先后顺序可能会被优化,这就可能存在loss在grad_fn(value_and_grad)之前就被返回使用的情况,导致返回的loss不正确。
因此通过使用depend算子,来保证loss的返回动作在optimizer之后执行,而optimizer的输入依赖grad_fn,因此optimizer一定在grad_fn之后执行,这就保证了depend返回的loss确实是经过grad_fn计算的最新结果
当然,mindspore也是支持动态图模式的,只需加一行代码:

# 设置为动态图模式
mindspore.set_context(mode=mindspore.PYNATIVE_MODE)
# 设置为静态图模式
# mindspore.set_context(mode=mindspore.GRAPH_MODE)model = Network()
print(model)

然后训练函数就可以这么写:

def train_step(data, label):(loss, _), grads = grad_fn(data, label)optimizer(grads)return loss

但是实测,动态图模式下,训练速度相比静态图慢了很多。
关于mindspore动态图和静态图模式的介绍,可看这个官方文档:动静态图

训练尺寸

各个文章在介绍梯度下降法时,通常介绍的是批量梯度下降法,但是训练模型时用的最多的是小批量梯度下降法。这里先讲下批量梯度下降随机梯度下降小批量梯度下降的区别。

批量梯度下降

批量梯度下降法的流程是:假设有1000个数据,经过正向计算,得到1000个计算结果,误差函数的计算公式依赖这1000个计算结果;再对误差函数进行反向传播求导,得到模型里参数的梯度值;同样地,对误差函数求导得梯度,也依赖这1000个计算结果;最后基于学习率更新参数,然后进入下一轮训练。
因此,标准的批量梯度下降,需要每次计算出1000个数据的正向传播结果,才可以得到参数梯度值,然后下一轮训练,重新计算1000个计算结果…这就存在大量的运算量,使得训练容易变得非常耗时。

随机梯度下降

随机梯度下降法的流程是,假设有1000个数据,我们随机取1个数据,经过正向计算,得到1个计算结果,误差函数的计算公式就只依赖这1个计算结果;然后反向传播求导,得到基于1个计算结果的梯度值,最后基于学习率更新参数,然后进入下一轮训练。下一轮训练时,随机取另1个数据,重复上述操作…
这种方法下,极大地降低了计算量,而且理论上,只要数据量够大,数据足够随机,最后也总会下降到所需极值点,毕竟计算数据量小了很多,算得更快了,下降速度也会快很多。但是每次只依赖1个数据,就使得梯度的下降方向在整体方向上不稳定,容易到处飘,最后的结果可能不会是全局最优。

小批量梯度下降

小批量梯度下降法的流程是:假设有1000个数据,我们随机取100个数据,经过正向计算,得到100个计算结果,误差函数的计算公式依赖这100个计算结果;然后反向传播求导,得到基于100个计算结果的梯度值,最后基于学习率更新参数,然后进入下一轮训练。下一轮训练时,随机取另100个数据,重复上述操作…
可以看出,小批量梯度下降 结合了 批量梯度下降 和 随机梯度下降 的优缺点,使得计算即不那么耗时,又保证参数更新路径和结果相对稳定。

实例

mindspore的这个例子用的是小批量梯度下降,train_step每次输入64个数据,然后前向传播、计算梯度、更新参数,再进入下一个epoch,随机取新的64个数据,重复训练…

size = dataset.get_dataset_size()
model_train.set_train()
for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):loss = train_step(data, label)if batch % 100 == 0:loss, current = loss.asnumpy(), batchprint(f"loss: {loss:>7f}  [{current:>3d}/{size:>3d}]")

在将数据集进行datapipe后,返回的train_dataset和test_dataset都是以batch_size=64个为一组进行输出的,此处dataset.get_dataset_size()返回的size是有多少组数据。测试集返回的size为938,表示一共有938组,每组64个图片数据。实际上MNIST只有60000个测试集图片,因此最后一组只有32个图片。

运行结果

Epoch 1
-------------------------------
loss: 2.303684  [  0/938]
loss: 2.291476  [100/938]
loss: 2.273411  [200/938]
loss: 2.212310  [300/938]
loss: 1.969760  [400/938]
loss: 1.600426  [500/938]
loss: 1.004380  [600/938]
loss: 0.735266  [700/938]
loss: 0.672223  [800/938]
loss: 0.578563  [900/938]
Test: Accuracy: 85.3%, Avg loss: 0.528851Epoch 2
-------------------------------
loss: 0.384008  [  0/938]
loss: 0.453575  [100/938]
loss: 0.277697  [200/938]
loss: 0.317674  [300/938]
loss: 0.294471  [400/938]
loss: 0.519272  [500/938]
loss: 0.253794  [600/938]
loss: 0.389252  [700/938]
loss: 0.383196  [800/938]
loss: 0.334877  [900/938]
Test: Accuracy: 90.2%, Avg loss: 0.334850

此处跑了两轮训练,可以看出,第一轮的938组数据的训练过程中,参数快速调整至合理范围(loss从2.3降低到0.5),但是第二轮的938组数据的训练过程中,loss出现了上下波动(0.3->0.4->0.2->0.3…),即模型参数向当前数据组的梯度下降的方向走了一小步后,新的数据组算出的loss反而比之前还提高了。
这主要是因为当前数据组的梯度下降方向 无法代表 替他数据组/所有数据的梯度下降方向,当然也可能是学习率(步长)太大导致跨过了最低点,这个就具体问题具体分析了。

总结

mindspore和pytorch在接口命名上存在区别,但是实际使用过程中,开发思路还是一致的。因此最关键的还是要熟悉深度学习的思路和流程,至于思路和代码实现的映射,这就唯手熟尔。

【mindspore】mindspore实现手写数字识别相关推荐

  1. MindSpore手写数字识别初体验,深度学习也没那么神秘嘛

    摘要:想了解深度学习却又无从下手,不如从手写数字识别模型训练开始吧! 深度学习作为机器学习分支之一,应用日益广泛.语音识别.自动机器翻译.即时视觉翻译.刷脸支付.人脸考勤--不知不觉,深度学习已经渗入 ...

  2. MindSpore手写数字识别体验

    文章目录 1. 环境准备 2. 安装minspore及其套件 3. 程序撰写 4. 总结 今天带大家体验一下 MindSpore 这个 AI 框架来完成手写数字识别的任务 1. 环境准备 使用Anac ...

  3. MindSpore实现手写数字识别代码

    MindSpore是华为自研的一套AI框架,最佳匹配昇腾处理器,最大程度地发挥硬件能力.作为AI入门的LeNet手写字体识别网络,网络大小和数据集都不大,可以在CPU上面进行训练和推理.下面是基于Mi ...

  4. MNIST手写数字识别 —— ResNet-经典卷积神经网络

    了解ResNet18的网络结构:掌握模型的保存和加载方法:掌握批量测试图片的方法. 结合图像分类任务,使用典型的图像分类网络ResNet18,实现手写数字识别. ResNet作为经典的图像分类网络有其 ...

  5. 基于LeNet5的手写数字识别,在ModelArts和GPU上复现

    基于LeNet5的手写数字识别 实验介绍 LeNet5 + MNIST被誉为深度学习领域的"Hello world".本实验主要介绍使用MindSpore在MNIST手写数字数据集 ...

  6. MNIST手写数字识别 —— 图像分析法实现二分类

    手写数字任务识别简介 MNIST 数据集来自美国国家标准与技术研究所(National Institute of Standards and Technology,简称 NIST ),总共有7万张图, ...

  7. 深蓝学院第三章:基于卷积神经网络(CNN)的手写数字识别实践

    参看之前篇章的用全连接神经网络去做手写识别:https://blog.csdn.net/m0_37957160/article/details/114105389?spm=1001.2014.3001 ...

  8. 深蓝学院第二章:基于全连接神经网络(FCNN)的手写数字识别

    如何用全连接神经网络去做手写识别??? 使用的是jupyter notebook这个插件进行代码演示.(首先先装一个Anaconda的虚拟环境,然后自己构建一个自己的虚拟环境,然后在虚拟环境中安装ju ...

  9. 深度学习--TensorFlow(项目)Keras手写数字识别

    目录 效果展示 基础理论 1.softmax激活函数 2.神经网络 3.隐藏层及神经元最佳数量 一.数据准备 1.载入数据集 2.数据处理 2-1.归一化 2-2.独热编码 二.神经网络拟合 1.搭建 ...

最新文章

  1. 定义了浮动元素后margin-bottom失效的解决办法
  2. 中文分词算法python代码_python实现中文分词FMM算法实例
  3. python新手遇到的5大坑
  4. ansible roles角色案例:实现httpd角色
  5. 【tensorflow】tf.layers.conv1d函数解析(一维卷积)
  6. ubuntu 18.04使用sysbench测试MySQL性能
  7. 逐渐“狗化”!网易云音乐控诉酷狗像素级抄袭,酷狗高管反击...
  8. BZOJ3514 Codechef MARCH14 GERALD07加强版 LCT维护最大生成树 主席树
  9. 国内首家,携程试点每周两天居家办公反响热烈,76%的员工主动报名
  10. 多语言页面语言标签的使用更适合推广
  11. internet与Internet的区别
  12. bdfg的matlab仿真模型,无刷双馈风力发电机变速恒频控制研究
  13. ignite集群的启动
  14. 怎么把python程序安装到别人电脑上_如何在自己的电脑上安装python的idle版 - 卡饭网...
  15. HTML静态网页作业——仿天猫购物商城(7页) 网页设计作业,网页制作作业, 学生网页作业, 网页作业成品, 网页作业模板
  16. wim linux u盘启动,在U盘启动中安装CDLinux
  17. 运行HQL时,报错:Container killed by YARN for exceeding memory limits
  18. 【完美解决】爬虫伪装代理IP方案
  19. HTML期末大学生网页设计作业 (我的家乡南京介绍网站制作)
  20. 建立您的启动:自定义会议视图

热门文章

  1. 中学-知识与能力【6】
  2. sql Mirroring
  3. 一文读懂 快速掌握示波器使用及原理
  4. C++23种设计模式(22)-中介者模式
  5. 指令流水线 —— 分类和多发技术
  6. 项目总结(二) 网格化管理系统问题总结
  7. 如何将打开res aw目录中的数据库文件?
  8. 2014年音视频即时通讯市场的割据
  9. 公安部:办案中总结出来的60种电信诈骗形式
  10. mac m1上esc键失灵不能退出vi解决方法