1 MNIST手写体识别实验 1.1实验介绍 本例子会实现一个简单的图片分类的功能,整体流程如下: 1、处理需要的数据集,这里使用了MNIST数据集。 1、定义一个网络,这里我们使用LeNet网络。 2、定义损失函数和优化器。 3、加载数据集并进行训练,训练完成后,查看结果及保存模型文件。 4、加载保存的模型,进行推理。 验证模型,加载测试数据集和训练后的模型,验证结果精度。 1.2实验准备 在动手进行实践之前,确保,你已经正确安装了MindSpore。如果没有,可以通过 MindSpore官网安装页面:https:/www.mindspore.cn/install/,将MindSpore安装在你的 电脑当中。 同时希望你拥有Python编码基础和概率、矩阵等基础数学知识。 1.3实验详细设计与实现 1.3.1数据准备 我们示例中用到的MNIST数据集是由10类28*28的灰度图片组成,训川练数据集包含60000 张图片,测试数据集包含10000张图片。 MNIST数据集下载页面:http:/yann..lecun..com/exdb/mnist/。页面提供4个数据集下载链 接,其中前2个文件是训练数据需要,后2个文件是测试结果需要。 将数据集下载并解压到本地路径下,这里将数据集解压分别存放到工作区 的./MNIST_Data/,train、/MNIST_Data/test路径下。 目录结构如下:
1 MNIST 手写体识别实验 1.1 实验介绍 本例子会实现一个简单的图片分类的功能,整体流程如下: 1、处理需要的数据集,这里使用了 MNIST 数据集。 1、 定义一个网络,这里我们使用 LeNet 网络。 2、 定义损失函数和优化器。 3、 加载数据集并进行训练,训练完成后,查看结果及保存模型文件。 4、 加载保存的模型,进行推理。 验证模型,加载测试数据集和训练后的模型,验证结果精度。 1.2 实验准备 在动手进行实践之前,确保,你已经正确安装了 MindSpore。如果没有,可以通过 MindSpore 官网安装页面:https://www.mindspore.cn/install/ ,将 MindSpore 安装在你的 电脑当中。 同时希望你拥有 Python 编码基础和概率、矩阵等基础数学知识。 1.3 实验详细设计与实现 1.3.1 数据准备 我们示例中用到的 MNIST 数据集是由 10 类 28*28 的灰度图片组成,训练数据集包含 60000 张图片,测试数据集包含 10000 张图片。 MNIST 数据集下载页面:http://yann.lecun.com/exdb/mnist/。页面提供 4 个数据集下载链 接,其中前 2 个文件是训练数据需要,后 2 个文件是测试结果需要。 将数据集下载并解压到本地路径下,这里将数据集解压分别存放到工作区 的./MNIST_Data/train、./MNIST_Data/test 路径下。 目录结构如下:
L-MNIST_Data test t10k-images.idx3-ubyte t10k-labels.idx1-ubyte train train-images.idx3-ubyte train-labels.idx1-ubyte 为了方便样例使用,我们在样例脚本中添加了自动下载数据集的功能。 1.3.2实验步骤 步骤一 导入Python库&模块并配置运行信息 在使用前,导入需要的Python库。 目前使用到s库,为方便理解,其他需要的库,我们在具体使用到时再说明。 详细的MindSpore的模块说明,可以在MindSpore API页面中搜索查询。 可以通过context.set_context来配置运行需要的信息,譬如运行模式、后端信息、硬件等 信息。 导入context:模块,配置运行需要的信息。 import os import mindspore as ms import mindspore.context as context #transforms.c transforms用于通用型数据增强,vision.c transforms用于图像类数据增强 import mindspore.dataset.transforms.c_transforms as C import mindspore.dataset.vision.c_transforms as CV #nn模块用于定义网络,model摸块用于编译模型,callback模块用于设定监督指标 from mindspore import nn from mindspore.train import Model from mindspore.train.callback import LossMonitor #设定运行模式为图模式,运行硬件为异腾芯片 context.set_context(mode=context.GRAPH_MODE,device_target='Ascend')#Ascend,CPU,GPU 在样例中我们配置样例运行使用图模式。根据实际情况配置硬件信息,警如代码运行在 Ascend Al处理器上,则device_.target选择Ascend,代码运行在CPU、GPU同理。详细参数 说明,请参见context.set_context接口说明。 步骤二.数据处理 数据集对于训练非常重要,好的数据集可以有效提高训练精度和效率。在加载数据集前, 我们通常会对数据集进行一些处理
└─MNIST_Data ├─ test │ t10k-images.idx3-ubyte │ t10k-labels.idx1-ubyte │ └─ train train-images.idx3-ubyte train-labels.idx1-ubyte 为了方便样例使用,我们在样例脚本中添加了自动下载数据集的功能。 1.3.2 实验步骤 步骤一. 导入 Python 库&模块并配置运行信息 在使用前,导入需要的 Python 库。 目前使用到 os 库,为方便理解,其他需要的库,我们在具体使用到时再说明。 详细的 MindSpore 的模块说明,可以在 MindSpore API 页面中搜索查询。 可以通过 context.set_context 来配置运行需要的信息,譬如运行模式、后端信息、硬件等 信息。 导入 context 模块,配置运行需要的信息。 import os import mindspore as ms import mindspore.context as context #transforms.c_transforms 用于通用型数据增强,vision.c_transforms 用于图像类数据增强 import mindspore.dataset.transforms.c_transforms as C import mindspore.dataset.vision.c_transforms as CV #nn 模块用于定义网络,model 模块用于编译模型,callback 模块用于设定监督指标 from mindspore import nn from mindspore.train import Model from mindspore.train.callback import LossMonitor #设定运行模式为图模式,运行硬件为昇腾芯片 context.set_context(mode=context.GRAPH_MODE, device_target='Ascend') # Ascend, CPU, GPU 在样例中我们配置样例运行使用图模式。根据实际情况配置硬件信息,譬如代码运行在 Ascend AI 处理器上,则 device_target 选择 Ascend,代码运行在 CPU、GPU 同理。详细参数 说明,请参见 context.set_context 接口说明。 步骤二. 数据处理 数据集对于训练非常重要,好的数据集可以有效提高训练精度和效率。在加载数据集前, 我们通常会对数据集进行一些处理
定义数据集及数据操作 我们定义一个函数create_.dataset来创建数据集。在这个函数中,我们定义好需要进行的 数据增强和处理操作: 1.定义数据集。 2.定义进行数据增强和处理所需要的一些参数。 3.根据参数,生成对应的数据增强操作。 4.使用map映射函数,将数据操作应用到数据集。 5.对性成的数据集进行处理。 #根据数据集存储地址,生成数据集 def create_dataset(data_dir,training=True,batch_size=32,resize=(32,32) rescale:=1/(255*0.3081),shit=-0.1307/0.3081,buffer size:=64: 生成训练集和测试集的路径 data_train os.path.join(data_dir,'train')#train set data test=os.path.join(data_dir,'test')#test set 利用MnistDataset方法读取mnist数据集,如果training是True则读取训练集 ds ms.dataset.MnistDataset(data_train if training else data_test) map方法是非常有效的方法,可以整体对数据集进行处理,resize改变数据形状, rescale进行归一化,HWC2CHW改变图像通道 ds=ds.map(input_columns=["image"],operations=[CV.Resize(resize),CV.Rescale(rescale, shift),CV.HWC2CHW()]) #利用map方法改变数据集标签的数据类型 dsds.map(input_columns=["label"],operations=C.TypeCast(ms.int32)) #shuffle是打乱操作,同时设定了batchsize的大小,并将最后不足一个batch的数据抛 dsds.shuffle(buffer size=buffer size).batch(batch size,drop_remainder=True) return ds 其中, batch size:每组包含的数据个数,现设置每组包含32个数据。 先进行修改图片尺寸,归一化,修改图像频道数等工作,再修改标签的数据类型。最后进 行shuffle操作,同时设定batch_.size,设置drop_remainder为True,则数据集中不足最后 一个batch的数据会被抛弃。 MindSpore支持进行多种数据处理和增强的操作,各种操作往往组合使用,具体可以参考 数据处理与数据增强章节。 步骤三. 定义网络 我们选择相对简单的LeNet网络。LeNet网络不包括输入层的情况下,共有7层:2个卷积 层、2个下采样层(池化层)、3个全连接层。每层都包含不同数量的训练参数,如下图所 示: LeNet-5
定义数据集及数据操作 我们定义一个函数 create_dataset 来创建数据集。在这个函数中,我们定义好需要进行的 数据增强和处理操作: 1. 定义数据集。 2. 定义进行数据增强和处理所需要的一些参数。 3. 根据参数,生成对应的数据增强操作。 4. 使用 map 映射函数,将数据操作应用到数据集。 5. 对生成的数据集进行处理。 #根据数据集存储地址,生成数据集 def create_dataset(data_dir, training=True, batch_size=32, resize=(32, 32), rescale=1/(255*0.3081), shift=-0.1307/0.3081, buffer_size=64): #生成训练集和测试集的路径 data_train = os.path.join(data_dir, 'train') # train set data_test = os.path.join(data_dir, 'test') # test set #利用 MnistDataset 方法读取 mnist 数据集,如果 training 是 True 则读取训练集 ds = ms.dataset.MnistDataset(data_train if training else data_test) #map 方法是非常有效的方法,可以整体对数据集进行处理,resize 改变数据形状, rescale 进行归一化,HWC2CHW 改变图像通道 ds = ds.map(input_columns=["image"], operations=[CV.Resize(resize), CV.Rescale(rescale, shift), CV.HWC2CHW()]) #利用 map 方法改变数据集标签的数据类型 ds = ds.map(input_columns=["label"], operations=C.TypeCast(ms.int32)) # shuffle 是打乱操作,同时设定了 batchsize 的大小,并将最后不足一个 batch 的数据抛 弃 ds = ds.shuffle(buffer_size=buffer_size).batch(batch_size, drop_remainder=True) return ds 其中, batch_size:每组包含的数据个数,现设置每组包含 32 个数据。 先进行修改图片尺寸,归一化,修改图像频道数等工作,再修改标签的数据类型。最后进 行 shuffle 操作,同时设定 batch_size,设置 drop_remainder 为 True,则数据集中不足最后 一个 batch 的数据会被抛弃。 MindSpore 支持进行多种数据处理和增强的操作,各种操作往往组合使用,具体可以参考 数据处理与数据增强章节。 步骤三. 定义网络 我们选择相对简单的 LeNet 网络。LeNet 网络不包括输入层的情况下,共有 7 层:2 个卷积 层、2 个下采样层(池化层)、3 个全连接层。每层都包含不同数量的训练参数,如下图所 示: LeNet-5
C3:f.maps 16@10x10 S4:f.maps 16@5x5 Full connection Gaussian connections Convolutions Subsampling Full connection 更多的LeNet网络的介绍不在此赘述,希望详细了解LeNet网络,可以查询 http://yann.lecun.com/exdb/lenet/. 使用MindSpore定义神经网络需要继承mindspore.nn.cel.Cell。Cell是所有神经网络 (conv2d等)的基类 神经网络的各层需要预先在_init_方法中定义,然后通过定义construct方法来完成神经 网络的前向构造。按照LeNet的网络结构,定义网络各层如下: 定义模型结构,MindSpore中的模型时通过construct定义模型结构,在_init中初始化各 层的对橡 class LeNet5(nn.Cell): def init (self): super(LeNet5,self).__init__() #定义卷积层,RLU激活函数,平坦层和全连接层 #cov2d的输入通道为1维,输出为6维,卷积核尺寸为5*5,步长为1,不适用 padding self.conv1=nn.Conv2d(1,6,5,stride=1,pad_mode='valid') self.conv2 nn.Conv2d(6,16,5,stride=1,pad_mode='valid') self.relu nn.ReLU() self.pool nn.MaxPool2d(kernel_size=2,stride=2) self.flatten =nn.Flatten() self.fc1 nn.Dense(400,120) self.fc2 nn.Dense(120,84) self.fc3 =nn.Dense(84,10) 构建Lenet5架构,x代表网络的输入 def construct(self,x): x=self.relu(self.conv1(x)) x self.pool(x) x self.relu(self.conv2(x)) x self.pool(x) x self.flatten(x) x=self.fc1(x) x=self.fc2(x) x self.fc3(x) return x 步骤四. 定义损失函数及优化器 在进行定义之前,先简单介绍损失函数及优化器的概念。 损失函数:又叫目标函数,用于衡量预测值与实际值差异的程度。深度学习通过不停地迭 代来缩小损失函数的值。定义一个好的损失函数,可以有效提高模型的性能
更多的 LeNet 网络的介绍不在此赘述,希望详细了解 LeNet 网络,可以查询 http://yann.lecun.com/exdb/lenet/。 使用 MindSpore 定义神经网络需要继承 mindspore.nn.cell.Cell。Cell 是所有神经网络 (Conv2d 等)的基类。 神经网络的各层需要预先在__init__方法中定义,然后通过定义 construct 方法来完成神经 网络的前向构造。按照 LeNet 的网络结构,定义网络各层如下: #定义模型结构,MindSpore 中的模型时通过 construct 定义模型结构,在__init__中初始化各 层的对象 class LeNet5(nn.Cell): def __init__(self): super(LeNet5, self).__init__() #定义卷积层,ReLU 激活函数,平坦层和全连接层 #conv2d 的输入通道为 1 维,输出为 6 维,卷积核尺寸为 5*5,步长为 1,不适用 padding self.conv1 = nn.Conv2d(1, 6, 5, stride=1, pad_mode='valid') self.conv2 = nn.Conv2d(6, 16, 5, stride=1, pad_mode='valid') self.relu = nn.ReLU() self.pool = nn.MaxPool2d(kernel_size=2, stride=2) self.flatten = nn.Flatten() self.fc1 = nn.Dense(400, 120) self.fc2 = nn.Dense(120, 84) self.fc3 = nn.Dense(84, 10) #构建 Lenet5 架构,x 代表网络的输入 def construct(self, x): x = self.relu(self.conv1(x)) x = self.pool(x) x = self.relu(self.conv2(x)) x = self.pool(x) x = self.flatten(x) x = self.fc1(x) x = self.fc2(x) x = self.fc3(x) return x 步骤四. 定义损失函数及优化器 在进行定义之前,先简单介绍损失函数及优化器的概念。 损失函数:又叫目标函数,用于衡量预测值与实际值差异的程度。深度学习通过不停地迭 代来缩小损失函数的值。定义一个好的损失函数,可以有效提高模型的性能
优化器:用于最小化损失函数,从而在训练过程中改进模型 定义了损失函数后,可以得到损失函数关于权重的梯度。梯度用于指示优化器优化权重的 方向,以提高模型性能。 定义损失函数 MindSpore支持的损失函数有SoftmaxCrossEntropyWithLogits、L1Loss、MSELoss等。这里使 用SoftmaxCrossEntropyWithLogits损失函数。 MindSpore提供了callback机制,可以在训练过程中执行自定义逻辑,这里使用框架提供的 ModelCheckpoint为例。ModelCheckpoint可以保存网络模型和参数,以便进行后续的 fine-tuning(微调)操作。 #构建训陈、验证函数进行模型训练和验证,提供数据路径,设定学习率,epoch数量 def train(data_dir,Ir=0.01,momentum=0.9,num_epochs=3) 调用函数,读取训练集 ds_train=create_dataset(data_dir) 调用函数,读取验证集 ds eval=create_dataset(data_dir,training=False) 构建网络 net=LeNet5() #设定1oss函数 loss=nn.loss.SoftmaxCrossEntropyWithLogits(sparse=True,reduction='mean') 设定优化器 opt nn.Momentum(net.trainable_params(),Ir,momentum) 设定损失监控 loss_cb LossMonitor(per_print times=ds_train.get_dataset size()) 编译形成模型 model Model(net,loss,opt,metrics=['acc','loss']) #训练网络,dataset sink mode为on device模式 model.train(num epochs,ds train,callbacks=[loss_cb],dataset sink mode=False) #用验证机评估网络表现 metrics model.eval(ds eval,dataset_sink_mode=False) 输出相关指标 print('Metrics:',metrics) 步骤五: 开始训练及验证过程 #main函数负责调用之前定义的函数,完成整个训练验证过程 ifname=="_main": #argsparse是python的命令行解析的标准模块,可以通过命令行传入参数 import argparse parser argparse.ArgumentParser() 设定训练数据路径 parser.add_argument('--data_url',required=False,default='./FCN/MNIST/,help='Location of data.')
优化器:用于最小化损失函数,从而在训练过程中改进模型。 定义了损失函数后,可以得到损失函数关于权重的梯度。梯度用于指示优化器优化权重的 方向,以提高模型性能。 定义损失函数 MindSpore 支持的损失函数有 SoftmaxCrossEntropyWithLogits、L1Loss、MSELoss 等。这里使 用 SoftmaxCrossEntropyWithLogits 损失函数。 MindSpore 提供了 callback 机制,可以在训练过程中执行自定义逻辑,这里使用框架提供的 ModelCheckpoint 为例。 ModelCheckpoint 可以保存网络模型和参数,以便进行后续的 fine-tuning(微调)操作。 # 构建训练、验证函数进行模型训练和验证,提供数据路径,设定学习率,epoch 数量 def train(data_dir, lr=0.01, momentum=0.9, num_epochs=3): #调用函数,读取训练集 ds_train = create_dataset(data_dir) #调用函数,读取验证集 ds_eval = create_dataset(data_dir, training=False) #构建网络 net = LeNet5() #设定 loss 函数 loss = nn.loss.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean') #设定优化器 opt = nn.Momentum(net.trainable_params(), lr, momentum) #设定损失监控 loss_cb = LossMonitor(per_print_times=ds_train.get_dataset_size()) #编译形成模型 model = Model(net, loss, opt, metrics={'acc', 'loss'}) # 训练网络,dataset_sink_mode 为 on_device 模式 model.train(num_epochs, ds_train, callbacks=[loss_cb], dataset_sink_mode=False) #用验证机评估网络表现 metrics = model.eval(ds_eval, dataset_sink_mode=False) #输出相关指标 print('Metrics:', metrics) 步骤五. 开始训练及验证过程 #main 函数负责调用之前定义的函数,完成整个训练验证过程 if __name__ == "__main__": #argsparse 是 python 的命令行解析的标准模块,可以通过命令行传入参数 import argparse parser = argparse.ArgumentParser() #设定训练数据路径 parser.add_argument('--data_url', required=False, default='./FCN/MNIST/', help='Location of data.')
parser.add_argument('--train_url',required=False,default=None,help='Location of training outputs.") args,unknown parser.parse_known_args() 判断路径是否为obs路径,如果是,从obs路径下载数据 if args.data_url.startswith('s3'): import moxing WAY1:copy dataset from your own OBS bucket to container/cache. moxing.file.copy_parallel(src_url=args.data_url,dst_url='MNIST/") WAY2:copy dataset from other's OBS bucket,which has been set public read or public read&write. moxing.file.copy parallel(src_url="s3://share-course/dataset/MNIST/",dst_url='MNIST/') data path ='MNIST/" else: data_path=os.path.abspath(args.data_url) #调用train函数,训练并验证模型 train(data_path) 训川练过程中会打印Ioss值,类似下图。loss值会波动,但总体来说Ioss值会逐步减小,精 度逐步提高。每个人运行的Ioss值有一定随机性,不一定完全相同。训练过程中Io5s打 印示例如下: epoch:1 step:1875,loss is 2.2767615 epoch:2 step:1875,loss is 0.109801136 epoch:3 step:1875,loss is 0.016787775 Metrics:'acc':0.9755608974358975,1oss':0.07498854234551963} 1.4开放题 请使用MindSpore框架实现MNIST手写体识别,并和本章实验进行对比,描述 MindSpore框架的优点
parser.add_argument('--train_url', required=False, default=None, help='Location of training outputs.') args, unknown = parser.parse_known_args() #判断路径是否为 obs 路径,如果是,从 obs 路径下载数据 if args.data_url.startswith('s3'): import moxing # WAY1: copy dataset from your own OBS bucket to container/cache. # moxing.file.copy_parallel(src_url=args.data_url, dst_url='MNIST/') # WAY2: copy dataset from other's OBS bucket, which has been set public read or public read&write. moxing.file.copy_parallel(src_url="s3://share-course/dataset/MNIST/", dst_url='MNIST/') data_path = 'MNIST/' else: data_path = os.path.abspath(args.data_url) #调用 train 函数,训练并验证模型 train(data_path) 训练过程中会打印 loss 值,类似下图。loss 值会波动,但总体来说 loss 值会逐步减小,精 度逐步提高。每个人运行的 loss 值有一定随机性,不一定完全相同。 训练过程中 loss 打 印示例如下: ... epoch: 1 step: 1875, loss is 2.2767615 epoch: 2 step: 1875, loss is 0.109801136 epoch: 3 step: 1875, loss is 0.016787775 Metrics: {'acc': 0.9755608974358975, 'loss': 0.07498854234551963} ... 1.4 开放题 请使用 MindSpore 框架实现 MNIST 手写体识别,并和本章实验进行对比,描述 MindSpore 框架的优点