训练神经网络涉及不少的步骤。我们需要知道如何提供输入训练数据,初始化模型参数,执行前向和后向传播,根据计算的梯度更新权重,模型检查等。在预测过程中,大部分步骤是重复的。 所有这一切对于新手以及经验丰富的开发人员而言都是相当艰巨的。

幸运的是,MXNet的module 包(简写为mod )模块化了用于训练和推断的常用代码。module提供用于执行预定义网络的高级和中级接口。我们可以互换地使用两个接口。 我们将在本教程中展示两个接口的用法。

前提条件

为了完成以下教程,我们需要:

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)