中低阶API实现深度学习
Ascend
GPU
CPU
进阶
全流程
为方便用户控制整网的执行流程,MindSpore提供了高阶的训练和推理接口mindspore.Model
,通过指定要训练的神经网络模型和常见的训练设置,调用train
和eval
方法对网络进行训练和推理。同时,用户如果想要对特定模块进行个性化设置,也可以调用对应的中低阶接口自行定义,本文介绍了如何使用中低阶API定义各个模块。
定义数据集
MindSpore的mindspore.dataset
模块集成了常见的数据处理功能:用户既可以调用此模块的相关接口来加载常见的数据集,也可以构造数据集类并配合使用GeneratorDataset
接口,实现自定义数据集和数据加载。使用GeneratorDataset
生成具有多项式关系的样本和对应的结果,代码样例如下:
导入所需的包:
[1]:
import numpy as np
import mindspore as ms
from mindspore import ops, nn
from mindspore import dtype as mstype
import mindspore.dataset as ds
import mindspore.common.initializer as init
定义数据集:
[2]:
def get_data(data_num, data_size):
for _ in range(data_num):
data = np.random.randn(data_size)
p = np.array([1, 0, -3, 5])
label = np.polyval(p, data).sum()
yield data.astype(np.float32), np.array([label]).astype(np.float32)
def create_dataset(data_num, data_size, batch_size=32, repeat_size=1):
"""定义数据集"""
input_data = ds.GeneratorDataset(list(get_data(data_num, data_size)), column_names=['data', 'label'])
input_data = input_data.batch(batch_size)
input_data = input_data.repeat(repeat_size)
return input_data
定义网络
MindSpore的Cell类是构建所有网络的基类,也是网络的基本单元。当用户需要自定义网络时,可以继承Cell类,并重写__init__
方法和construct
方法。MindSpore的ops
模块提供了基础算子的实现,nn
模块实现了对基础算子的进一步封装,用户可以根据需要,灵活使用不同的算子。
使用常用的nn
算子构建一个简单的全连接网络:
[3]:
class MyNet(nn.Cell):
"""定义网络"""
def __init__(self, input_size=32):
super(MyNet, self).__init__()
self.fc1 = nn.Dense(input_size, 120, weight_init=init.Normal(0.02))
self.fc2 = nn.Dense(120, 84, weight_init=init.Normal(0.02))
self.fc3 = nn.Dense(84, 1, weight_init=init.Normal(0.02))
self.relu = nn.ReLU()
def construct(self, x):
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
定义损失函数
损失函数用于衡量预测值与真实值差异的程度。深度学习中,模型训练就是通过不停地迭代来缩小损失函数值的过程。因此在模型训练过程中损失函数的选择非常重要,定义一个好的损失函数可以帮助loss更快收敛,达到更好的精度。
MindSpore提供了许多通用损失函数供用户选择,也支持用户根据需要自定义损失函数。定义损失函数类时,既可以继承网络的基类nn.Cell
,也可以继承损失函数的基类nn.LossBase
。
继承Cell定义损失函数
Cell
是MindSpore的基本网络单元,可以用于构建网络,损失函数也可以通过Cell
来定义。使用Cell
定义损失函数的方法与定义一个普通的网络的差别在于,其执行逻辑用于计算前向网络输出与真实值之间的误差。
以平均绝对误差为例,损失函数的定义方法如下:
[4]:
class MyL1Loss(nn.Cell):
"""定义损失"""
def __init__(self):
super(MyL1Loss, self).__init__()
self.abs = ops.Abs()
self.reduce_mean = ops.ReduceMean()
def construct(self, predict, target):
x = self.abs(predict - target)
return self.reduce_mean(x)
继承基类定义损失函数
在定义损失函数时还可以继承损失函数的基类nn.LossBase
。损失函数的基类在Cell
的基础上,提供了get_loss
方法,用于对损失值求和或求均值,输出一个标量。MyL1Loss使用LossBase
作为基类的定义如下:
[5]:
class MyL1Loss(nn.LossBase):
"""定义损失"""
def __init__(self, reduction="mean"):
super(MyL1Loss, self).__init__(reduction)
self.abs = ops.Abs()
def construct(self, base, target):
x = self.abs(base - target)
return self.get_loss(x)
定义优化器
优化器在模型训练过程中,用于计算和更新网络参数,合适的优化器可以有效减少训练时间,提高最终模型性能。
MindSpore提供了许多通用的优化器供用户选择,同时也支持用户根据需要自定义优化器。自定义优化器时可以继承优化器基类optimizer
,重写construct
函数实现参数的更新。
使用基础的运算算子自定义优化器:
[6]:
class MyMomentum(nn.Optimizer):
"""定义优化器"""
def __init__(self, params, learning_rate, momentum=0.9, use_nesterov=False):
super(MyMomentum, self).__init__(learning_rate, params)
self.momentum = ms.Parameter(ms.Tensor(momentum, mstype.float32), name="momentum")
self.use_nesterov = use_nesterov
self.moments = self.parameters.clone(prefix="moments", init="zeros")
self.assign = ops.Assign()
def construct(self, gradients):
lr = self.get_lr()
params = self.parameters
for i in range(len(params)):
self.assign(self.moments[i], self.moments[i] * self.momentum + gradients[i])
if self.use_nesterov:
update = params[i] - (self.moments[i] * self.momentum + gradients[i]) * lr
else:
update = params[i] - self.moments[i] * lr
self.assign(params[i], update)
return params
MindSpore也封装了ApplyMomentum
算子供用户使用,使用ApplyMomentum
算子自定义优化器:
[7]:
class MyMomentum(nn.Optimizer):
"""定义优化器"""
def __init__(self, params, learning_rate, momentum=0.9, use_nesterov=False):
super(MyMomentum, self).__init__(learning_rate, params)
self.moments = self.parameters.clone(prefix="moments", init="zeros")
self.momentum = momentum
self.opt = ops.ApplyMomentum(use_nesterov=use_nesterov)
def construct(self, gradients):
params = self.parameters
success = None
for param, mom, grad in zip(params, self.moments, gradients):
success = self.opt(param, mom, self.learning_rate, grad, self.momentum)
return success
定义训练流程
MindSpore的nn模块提供了部分封装接口方便用户在训练过程中使用,例如,nn.WithLossCell
接口可以将前向网络与损失函数连接起来;nn.TrainOneStepCell
封装了损失网络和优化器,在执行训练时通过GradOperation
算子来进行梯度的获取,通过优化器来实现权重的更新。
定义损失网络:
[8]:
class MyWithLossCell(nn.Cell):
"""定义损失网络"""
def __init__(self, backbone, loss_fn):
super(MyWithLossCell, self).__init__(auto_prefix=False)
self.backbone = backbone
self.loss_fn = loss_fn
def construct(self, data, label):
out = self.backbone(data)
return self.loss_fn(out, label)
def backbone_network(self):
return self.backbone
MindSpore的nn模块提供了训练网络封装函数nn.TrainOneStepCell
,如无特殊需求,用户可以继承nn.TrainOneStepCell
来定义自己的训练流程。
[9]:
class MyTrainStep(nn.TrainOneStepCell):
"""定义训练流程"""
def __init__(self, network, optimizer):
"""参数初始化"""
super(MyTrainStep, self).__init__(network, optimizer)
self.grad = ops.GradOperation(get_by_list=True)
def construct(self, data, label):
"""构建训练过程"""
weights = self.weights
loss = self.network(data, label)
grads = self.grad(self.network, weights)(data, label)
return loss, self.optimizer(grads)
执行训练:
[10]:
# 生成多项式分布的训练数据
dataset_size = 32
ds_train = create_dataset(2048, dataset_size)
# 网络
net = MyNet()
# 损失函数
loss_func = MyL1Loss()
# 优化器
opt = MyMomentum(net.trainable_params(), 0.01)
# 构建损失网络
net_with_criterion = MyWithLossCell(net, loss_func)
# 构建训练网络
train_net = MyTrainStep(net_with_criterion, opt)
# 执行训练,每个epoch打印一次损失值
epochs = 5
for epoch in range(epochs):
for train_x, train_y in ds_train:
train_net(train_x, train_y)
loss_val = net_with_criterion(train_x, train_y)
print(loss_val)
108.16456
18.994844
10.551674
16.937834
15.274784
定义metric
当训练任务结束,常常需要评估函数来评估模型的好坏,MindSpore的metric
模块提供了常见的评估函数,用户也可以根据需要自行定义评估指标:
[11]:
class MyMAE(nn.Metric):
"""定义metric"""
def __init__(self):
super(MyMAE, self).__init__()
self.clear()
def clear(self):
self.abs_error_sum = 0
self.samples_num = 0
def update(self, *inputs):
y_pred = inputs[0].asnumpy()
y = inputs[1].asnumpy()
error_abs = np.abs(y.reshape(y_pred.shape) - y_pred)
self.abs_error_sum += error_abs.sum()
self.samples_num += y.shape[0]
def eval(self):
return self.abs_error_sum / self.samples_num
定义验证流程
MindSpore的nn模块提供了评估网络包装函数nn.WithEvalCell
,用户也可以自己定义评估网络包装函数:
[12]:
class MyWithEvalCell(nn.Cell):
"""定义验证流程"""
def __init__(self, network):
super(MyWithEvalCell, self).__init__(auto_prefix=False)
self.network = network
def construct(self, data, label):
outputs = self.network(data)
return outputs, label
执行推理并评估:
[13]:
# 获取验证数据
ds_eval = create_dataset(128, dataset_size, 1)
# 定义评估网络
eval_net = MyWithEvalCell(net)
eval_net.set_train(False)
# 定义评估指标
mae = MyMAE()
# 执行推理过程
for eval_x, eval_y in ds_eval:
output, eval_y = eval_net(eval_x, eval_y)
mae.update(output, eval_y)
mae_result = mae.eval()
print("mae: ", mae_result)
mae: 15.619880557060242
输出评估误差,MAE与模型在训练集上效果大致相同。
保存模型
使用MindSpore提供的save_checkpoint
保存模型,传入网络和保存路径:
[14]:
ms.save_checkpoint(net, "./MyNet.ckpt")