筹备工作应用MindSpore进行模型训练须要做以下的筹备工作训练的网络模型训练数据以及数据处理训练回调函数启动训练的python脚本网络模型MindSpore提供了一个MindVision的开源网络工具箱,其中蕴含了很多的网络结构以及预训练数据,但在云端的NPU环境中 (过后应用的是mindspore_1.5.1-cann_5.0.3-py_3.7-euler_2.8.3-aarch64) ,会间接报错,因而须要咱们本人编写网络结构。编写网络结构能够参考MindSpore的官网,以下是一个简略的ResNet18的网络结构。编写网络结构的要点在于,在 __init__ 办法中定义网络中不同层的构造,在 construct 办法中设置不同层之间的连贯关系。import mindspore.nn as nn
class ResBlock2RT(nn.Cell):
def __init__(self, in_channel): super(ResBlock2RT, self).__init__() self.seq = nn.SequentialCell( [ nn.Conv2d(in_channel, in_channel, kernel_size=3, stride=1), nn.BatchNorm2d(in_channel), nn.ReLU(), nn.Conv2d(in_channel, in_channel, kernel_size=3, stride=1), nn.BatchNorm2d(in_channel), ]) self.relu = nn.ReLU()def construct(self, x): x = self.seq(x) + x return self.relu(x)
class ResBlock2DS(nn.Cell):
def __init__(self, in_channel, out_channel, stride): super(ResBlock2DS, self).__init__() self.seq = nn.SequentialCell( [ nn.Conv2d(in_channel, out_channel, 3, stride=stride), nn.BatchNorm2d(out_channel), nn.ReLU(), nn.Conv2d(out_channel, out_channel, 3, stride=1), nn.BatchNorm2d(out_channel), ]) self.shortup = nn.SequentialCell( [ nn.Conv2d(in_channel, out_channel, 1, stride=stride), nn.BatchNorm2d(out_channel), ]) self.relu = nn.ReLU()def construct(self, x): y = self.seq(x) + self.shortup(x) return self.relu(y)
class ResNet18(nn.Cell):
def __init__(self, class_num=10, input_size=224): super(ResNet18, self).__init__() self.PreProcess = nn.SequentialCell([ nn.Conv2d(3, 64, 7, stride=2), nn.BatchNorm2d(64), nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same'), ]) self.ResBlock = nn.SequentialCell([ ResBlock2RT(64), ResBlock2RT(64), ResBlock2DS(64, 128, 2), ResBlock2RT(128), ResBlock2DS(128, 256, 2), ResBlock2RT(256), ResBlock2DS(256, 512, 2), ResBlock2RT(512), ]) self.LastProcess = nn.SequentialCell([ nn.AvgPool2d(input_size // 32), nn.Flatten(), nn.Dense(512, 1024), nn.ReLU(), nn.Dense(1024, 512), nn.ReLU(), nn.Dropout(keep_prob=0.5), nn.Dense(512, class_num), nn.Softmax(), ])def construct(self, x): x = self.PreProcess(x) x = self.ResBlock(x) y = self.LastProcess(x) return y
数据与数据处理数据挂载以及解压既然应用云端NPU进行训练,诚然要将数据上传到云端。MindSpore能够应用华为云的对象存储服务存储须要训练的数据;在启动训练后,MindSpore能够挂载Obs桶内的一个文件夹到本地的一个文件夹中,这样用户就能够通过Obs桶进行加载训练数据。如果咱们的训练数据是由大量的小文件组织起来,上传会比拟麻烦。所以咱们能够将训练数据进行压缩,而后能够应用python自带的 zipfile 库进行文件解压,应用起来比较简单,解压代码只有一行,其余的代码是用于判断文件存在性以及输入日志。import os
import zipfile
import logging
def unzip_file(file_name, target):
logging.info('unzip file {}'.format(file_name))with zipfile.ZipFile(file_name) as zf: zf.extractall(path=target)logging.info('file {} unzipped.'.format(file_name))
数据处理对分类模型,其数据格式是一张图片对应一个标签。咱们能够应用一个类将硬盘数据转换为MindSpore模型,也能够应用列表、迭代器,其比拟重要的是 __len__ 以及 __getitem__ 这两个办法,作用是计算数据个数以及应用索引的形式获取数据。其大略的格局如下,留神 __getitem__ 办法中须要同时返回图片的数据以及标签。class DatasetGenerator:
def __init__(self, data_path: str, label_path: str, resize=None): self.data = [] self.label = [] ...def __getitem__(self, item): ... return img, labeldef __len__(self): return len(self.label)
数据宰割如果不是所有数据都会用于训练,就要进行数据宰割。数据宰割能够手工宰割,也能够通过代码实现。以下是实现数据宰割的简略的代码。import time
import numpy as np
class SubSet:
def __init__(self, dataset, ls): self.dataset = dataset self.ls = lsdef __getitem__(self, item): return self.dataset[self.ls[item]]def __str__(self): return str(self.ls)def __len__(self): return len(self.ls)
def random_split(dataset, target_size, seed=int(time.time())):
data_length = len(dataset)combine_ls = list(range(data_length)) # get the dataset indexout_ls = []np.random.seed(seed)for item in target_size: x = np.random.choice(combine_ls, size=int(data_length * item), replace=False) tuple(map(combine_ls.remove, x)) out_ls.append(SubSet(dataset, x))return out_ls
具体的应用形式是这样的, random_split 会应用列表保留几个宰割后的子集。x = [i for i in range(100)]
y = random_split(x, (0.1, 0.2, 0.3, 0.4))
数据转换应用 mindspore.dataset.GeneratorDataset 将数据转换为MindSpore的数据格式,以下代码蕴含了数据加载以及数据宰割,后果是将数据导入为训练以及测试数据集。import mindspore.dataset as ds
ds_gen = DatasetGenerator(data_path=data_path,
label_path=label_path, # resize=resize )
split dataset to train dataset and valid dataset
train_gen, valid_gen = random_split(ds_gen, (0.75, 0.25))
train_ds = ds.GeneratorDataset(train_gen, ["image", "label"], shuffle=True)
valid_ds = ds.GeneratorDataset(valid_gen, ["image", "label"], shuffle=True)
仅仅如此还是不够的,咱们还须要进行下一步的解决,即须要图片大小放缩随机程度翻转随机角度旋转数据类型转换将(h,w,c)通道的格局转换为(c,h,w)格局其中(1),(5)是数据格式变换,为必选项;(2)-(4)为数据加强,是可选项。具体的代码如下:import mindspore.dataset.vision.c_transforms as cv_trans
import mindspore.dataset.transforms.c_transforms as c_trans
resize_op = cv_trans.Resize(size=(resize, resize), interpolation=Inter.BICUBIC)
random_hor_flip_op = cv_trans.RandomHorizontalFlip()
random_rotation_op = cv_trans.RandomRotation(10, resample=Inter.BICUBIC)
type_cast_op_image = c_trans.TypeCast(dtype.float32)
type_cast_op_label = c_trans.TypeCast(dtype.int32)
HWC2CHW = cv_trans.HWC2CHW()
preprocess_operation = [resize_op,
random_hor_flip_op, random_rotation_op, type_cast_op_image, HWC2CHW]
train_ds = train_ds.map(operations=preprocess_operation,
input_columns="image")
train_ds = train_ds.map(operations=type_cast_op_label, input_columns="label")
train_ds = train_ds.batch(batch_size)
valid_ds = valid_ds.map(operations=preprocess_operation,
input_columns="image")
valid_ds = valid_ds.map(operations=type_cast_op_label, input_columns="label")
valid_ds = valid_ds.batch(batch_size)
训练回调训练时如果须要查看模型的精度,以及保留模型训练过程中迭代最优的后果,须要重载 mindspore.train.callback.Callback 类,以下是回调类的款式。import logging
import mindspore
import mindspore.nn as nn
from mindspore.train.callback import Callback
def get_accuracy(network, datasets):
metric = nn.Accuracy('classification')for x, y in datasets: y_hat = network(x) metric.update(y_hat, y)return metric.eval()
def get_loss(network, loss_f, dataset):
loss = 0for x, y in dataset: y_hat = network(x) loss += loss_f(y_hat, y)return loss
class AccuracyMonitor(Callback):
def __init__(self, save_url, valid_data, export): self.save_file_pth = save_url + 'auto-save.ckpt' self.best_file_pth = save_url + 'best.ckpt' self.local_best_pt = './best.ckpt' self.valid_data = valid_data self.export = export self.max_valid_acc = 0def epoch_end(self, run_context): """Called after each epoch finished.""" callback_params = run_context.original_args() cur_epoch_num = callback_params.cur_epoch_num epoch_num = callback_params.epoch_num network = callback_params.network train_data = callback_params.train_dataset train_accu = get_accuracy(network, train_data) valid_accu = get_accuracy(network, self.valid_data) loss = get_loss(network, callback_params.loss_fn, train_data) loss /= callback_params.batch_num if valid_accu > self.max_valid_acc: self.max_valid_acc = valid_accu mindspore.save_checkpoint(network, self.local_best_pt) mindspore.save_checkpoint(network, self.best_file_pth) logging.info('Save the best state to {}'.format(self.local_best_pt)) elif not cur_epoch_num % 10 and cur_epoch_num: mindspore.save_checkpoint(network, self.save_file_pth) logging.debug('Auto-save state to {}'.format(self.save_file_pth)) print('epoch:[{}/{}] Loss:{} Train Accuracy:{} Valid Accuracy:{}'.format(cur_epoch_num, epoch_num, loss, train_accu, valid_accu))
模型训练训练代码MindSpore封装了高阶的训练接口,训练时只须要定义好训练网络(net)、损失函数(loss)、优化器(optim)、训练轮次(args.epochs)、训练数据(t_dt)、回调列表(callback_list)即可,如下所示。loss = ...
optim = ...
callback_list = [...]
model = Model(net, loss_fn=loss, optimizer=optim)
model.train(args.epochs, t_dt, callback_list, False)
启动脚本应用云端NPU进行训练,须要应用命令行进行交互,能够应用python的 argparse 的库进行操作,具体能够参考https://docs.python.org/3/lib...。因为Modelarts进行云端训练是通过Shell的形式进行启动的,点击新建训练能看到如下所示的启动代码
因而,咱们要在训练脚本中设置解析参数。应用云端NPU进行训练Modelarts是华为提供的一个PaaS,应用Modelarts进行训练,须要进行以下工作。建设文件夹在对象存储服务中新建一个桶,在桶的对象中建设如下目录resnet/
|-code/|-data/ |-images/|-log/|-output/
其中,resnet是咱们的我的项目工程的总目录,属于可选项;code目录用于寄存代码; data目录用于存放数据信息,其中的images目录寄存图片数据,dataset.txt寄存图片的数据的标签; log目录用于寄存输入的日志信息;output目录用于寄存输入的数据信息。上传数据具体是将数据上传到obs桶中的data/目录中,能够应用obsutil进行上传;也能够压缩后打包上传,再进行解压缩。上传算法将所有的算法文件上传到obs桶的/resnet/code/目录下。创立算法进入Modelarts界面,点击算法治理,创立,抉择AI引擎为Ascend-Powered-Engine,mindspore,设置代码目录为/resnet/code/,抉择启动文件。而后在输出/输入数据配置,设置启动脚本中的门路名称,并设置超参,如下所示。
创立训练进入Modelarts界面,点击训练治理-训练作业,创立训练作业,输出名称,配置输出文件门路、输入文件门路、超参数、日志,抉择资源为Ascend,点击提交,即可开始训练。
总结具体代码能够查看 https://gitee.com/ruqy/res-net