训练神经网络涉及不少的步骤。我们需要知道如何提供输入训练数据,初始化模型参数,执行前向和后向传播,根据计算的梯度更新权重,模型检查等。在预测过程中,大部分步骤是重复的。 所有这一切对于新手以及经验丰富的开发人员而言都是相当艰巨的。
幸运的是,MXNet的module 包(简写为mod )模块化了用于训练和推断的常用代码。module提供用于执行预定义网络的高级和中级接口。我们可以互换地使用两个接口。 我们将在本教程中展示两个接口的用法。
前提条件
为了完成以下教程,我们需要:
- MXNet:安装教程
- Jupyter Notebook and Python Requests packages.
pip install jupyter requests
准备
在本部分教程中,我们将使用一个十类的多层感知机(MLP)和一个字母识别数据集。
下面的代码下载数据集并创建一个80:20的训练:测试拆分。它还初始化训练数据迭代器,每次返回一批32个训练样本,还为测试数据创建了一个单独的迭代器。
import logging
logging.getLogger().setLevel(logging.INFO)
import mxnet as mx
import numpy as np
fname = mx.test_utils.download('http://archive.ics.uci.edu/ml/machine-learning-databases/letter-recognition/letter-recognition.data')
data = np.genfromtxt(fname, delimiter=',')[:,1:]
label = np.array([ord(l.split(',')[0])-ord('A') for l in open(fname, 'r')])
batch_size = 32
ntrain = int(data.shape[0]*0.8)
train_iter = mx.io.NDArrayIter(data[:ntrain, :], label[:ntrain], batch_size, shuffle=True)
val_iter = mx.io.NDArrayIter(data[ntrain:, :], label[ntrain:], batch_size)
接下来,定义网络
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(net, name='fc1', num_hidden=64)
net = mx.sym.Activation(net, name='relu1', act_type="relu")
net = mx.sym.FullyConnected(net, name='fc2', num_hidden=26)
net = mx.sym.SoftmaxOutput(net, name='softmax')
mx.viz.plot_network(net)
创建模型
现在我们开始介绍模型,最常用的模型类是Module,我们可以用以下参数来构建一个模型:
- symbol:网络符号
- context:执行设备(设备列表)
- data_names:数据变量名称列表
- label_names:标签变量名称列表
对于net,我们只有名称为data的数据,和一个名称为softmax_label的标签。标签自动以我们为SoftmaxOutput运算符指定的名称softmax命名。
mod = mx.mod.Module(symbol=net,
context=mx.cpu(),
data_names=['data'],
label_names=['softmax_label'])
中间层接口
创建模型之后,让我们来看看如何使用模型的中间层的接口来训练和推断。这些接口让开发人员更方便地通过前向和后向遍历进行逐步计算,对调试也更有用。
为了训练一个模型,我们需要完成以下步骤:
- bind:通过内存分配为计算搭建环境
- init_params :初始化参数
- init_optimizer:初始化优化器,默认是sgd
- metric.create:从输入指标名称创建评估指标
- forward :前向计算
- update_metric:对最后一次前向计算的输出进行评估和累加评估度量
- backward :后向计算
- update :根据安装的优化程序和先前的前向后向批次中计算的梯度更新参数
使用方式如下:
# allocate memory given the input data and label shapes
mod.bind(data_shapes=train_iter.provide_data, label_shapes=train_iter.provide_label)
# initialize parameters by uniform random numbers
mod.init_params(initializer=mx.init.Uniform(scale=.1))
# use SGD with learning rate 0.1 to train
mod.init_optimizer(optimizer='sgd', optimizer_params=(('learning_rate', 0.1), ))
# use accuracy as the metric
metric = mx.metric.create('acc')
# train 5 epochs, i.e. going over the data iter one pass
for epoch in range(5):
train_iter.reset()
metric.reset()
for batch in train_iter:
mod.forward(batch, is_train=True) # compute predictions
mod.update_metric(metric, batch.label) # accumulate prediction accuracy
mod.backward() # compute gradients
mod.update() # update parameters
print('Epoch %d, Training %s' % (epoch, metric.get()))
高级接口
训练
Modules提供训练/预测/评估的高层API。为了适配一个模型,只需要使用fit 函数而不需要按照上面提到的步骤。
# reset train_iter to the beginning
train_iter.reset()
# create a module
mod = mx.mod.Module(symbol=net,
context=mx.cpu(),
data_names=['data'],
label_names=['softmax_label'])
# fit the module
mod.fit(train_iter,
eval_data=val_iter,
optimizer='sgd',
optimizer_params={'learning_rate':0.1},
eval_metric='acc',
num_epoch=8)
默认情况下,fit 函数将eval_metric 设置为accuracy ,将optimizer 设置为sgd,将优化参数设置为((‘learning_rate’, 0.01),).
预测和评估
为了使用一个模型进行预测,只需要使用predict 函数。它会收集并返回所有的预测结果。
y = mod.predict(val_iter)
assert y.shape == (4000, 26)
如果我们不需要预测输出结果,只需要在一个测试集上评估性能,我们可以使用score 函数。它在输入验证数据集中运行预测,并根据给定的输入度量来评估性能。使用示例如下:
score = mod.score(val_iter, ['acc'])
print("Accuracy score is %f" % (score[0][1]))
一些其他的度量方法,比如top_k_acc(前k准确率), F1, RMSE, MSE, MAE, ce(交叉熵)。我们可以通过调节批次,学习率,优化参数来改变分数,从而获得最佳效果。
保存和加载
我们可以通过使用一个检查点回调函数,来保存每一轮训练的模型参数。
# construct a callback function to save checkpoints
model_prefix = 'mx_mlp'
checkpoint = mx.callback.do_checkpoint(model_prefix)
mod = mx.mod.Module(symbol=net)
mod.fit(train_iter, num_epoch=5, epoch_end_callback=checkpoint)
使用load_checkpoint 函数来载入保存的模型参数。它载入符号和与其相关的参数。之后,我们可以将载入的参数设置到模型中。
sym, arg_params, aux_params = mx.model.load_checkpoint(model_prefix, 3)
assert sym.tojson() == net.tojson()
# assign the loaded parameters to the module
mod.set_params(arg_params, aux_params)
或者我们只是想在保存的检查点继续训练,我们可以直接使用fit() 函数代替set_params 函数传递载入的参数。这样fit() 函数就知道使用这些参数开始训练,而不是随机初始化参数。我们也可以设置begin_epoch ,这样fit() 函数知道我们是在一个之前保存的训练轮次上继续的。
mod = mx.mod.Module(symbol=sym)
mod.fit(train_iter,
num_epoch=8,
arg_params=arg_params,
aux_params=aux_params,
begin_epoch=3)