- import mxnet as mx import numpy as np import logging logging.basicConfig(level = logging.INFO) sample_count = 1000 train_count = 800 valid_count = sample_count - train_count feature_count = 100 category_count = 10 batch = 10
我们将通过均匀分布的方式生成这 1000 个样本,将其存储在一个名为 "X" 的 NDArray 中:1000 行,100 列。
- X = mx.nd.uniform(low = 0, high = 1, shape = (sample_count, feature_count)) >>> X.shape(1000L, 100L) >>> X.asnumpy() array([[0.70029777, 0.28444085, 0.46263582, ..., 0.73365158, 0.99670047, 0.5961988], [0.34659418, 0.82824177, 0.72929877, ..., 0.56012964, 0.32261589, 0.35627609], [0.10939316, 0.02995235, 0.97597599, ..., 0.20194994, 0.9266268, 0.25102937], ..., [0.69691515, 0.52568913, 0.21130568, ..., 0.42498392, 0.80869114, 0.23635457], [0.3562004, 0.5794751, 0.38135922, ..., 0.6336484, 0.26392782, 0.30010447], [0.40369365, 0.89351988, 0.88817406, ..., 0.13799617, 0.40905532, 0.05180593]], dtype = float32)
这 1000 个样本的类别用介于 0-9 的整数来代表,类别是随机生成的,存储在一个名为 "Y" 的 NDArray 中。
- Y = mx.nd.empty((sample_count, )) for i in range(0, sample_count - 1) : Y[i] = np.random.randint(0, category_count) >>> Y.shape(1000L, ) >>> Y[0 : 10].asnumpy() array([3., 3., 1., 9., 4., 7., 3., 5., 2., 2.], dtype = float32)
随后我们将针对训练和验证两个用途对数据集进行 80/20 拆分。为此需要使用 NDArray.crop 函数。在这里,数据集是完全随机的,因此可以使用前 80% 的数据进行训练,用后 20% 的数据进行验证。实际运用中,我们可能需要首先搅乱数据集,这样才能避免按顺序生成的数据可能造成的偏差。
- X_train = mx.nd.crop(X, begin = (0, 0), end = (train_count, feature_count - 1)) X_valid = mx.nd.crop(X, begin = (train_count, 0), end = (sample_count, feature_count - 1)) Y_train = Y[0 : train_count] Y_valid = Y[train_count: sample_count]
至此数据已经准备完毕!
这个网络其实很简单,一起看看其中的每一层:
- data = mx.sym.Variable('data')
- fc1 = mx.sym.FullyConnected(data, name = 'fc1', num_hidden = 64)
- relu1 = mx.sym.Activation(fc1, name = 'relu1', act_type = "relu")
- fc2 = mx.sym.FullyConnected(relu1, name = 'fc2', num_hidden = category_count)
- out = mx.sym.SoftmaxOutput(fc2, name = 'softmax') mod = mx.mod.Module(out)
在第 1 篇文章中,我们了解到神经网络并不会一次只训练一个样本,因为这样做从性能的角度来看效率太低。因此我们会使用批,即一批固定数量的样本。
为了给神经网络提供这样的 "批",我们需要使用 NDArrayIter 函数构建一个迭代器。其参数包括训练数据、分类(MXNet 将其称之为标签 (Label)),以及批大小。
如你所见,我们可以对整个数据集进行迭代,同时对 10 个样本和 10 个标签执行该操作。随后即可调用 reset() 函数将迭代器恢复为初始状态。
- train_iter = mx.io.NDArrayIter(data = X_train, label = Y_train, batch_size = batch) >>>
- for batch in train_iter: ...print batch.data...print batch.label... [ < NDArray 10x99@cpu(0) > ][ < NDArray 10@cpu(0) > ][ < NDArray 10x99@cpu(0) > ][ < NDArray 10@cpu(0) > ][ < NDArray 10x99@cpu(0) > ][ < NDArray 10@cpu(0) > ] < edited
- for brevity > >>>train_iter.reset()
网络已经准备完成,开始训练吧!
首先将输入 Symbol** 绑定 ** 至实际的数据集(样本和标签),这时候就会用到迭代器。
- mod.bind(data_shapes = train_iter.provide_data, label_shapes = train_iter.provide_label)
随后对网络中的神经元权重进行初始化。这个步骤非常重要:使用 "恰当" 的技术对齐进行初始化可以帮助网络更快速地学习。此时可用的技术很多,Xavier 初始化器(名称源自该技术的发明人 Xavier Glorot?—?PDF)就是其中之一。
- #Allowed,
- but not efficient mod.init_params()#Much better mod.init_params(initializer = mx.init.Xavier(magnitude = 2.))
接着需要定义优化参数:
- mod.init_optimizer(optimizer = 'sgd', optimizer_params = (('learning_rate', 0.1), ))
最后,终于可以开始训练网络了!我们会执行 50 个回合 (Epoch) 的训练,也就是说,整个数据集需要在这个网络中(以 10 个样本为一批)运行 50 次。
- mod.fit(train_iter, num_epoch = 50) INFO: root: Epoch[0] Train - accuracy = 0.097500 INFO: root: Epoch[0] Time cost = 0.085 INFO: root: Epoch[1] Train - accuracy = 0.122500 INFO: root: Epoch[1] Time cost = 0.074 INFO: root: Epoch[2] Train - accuracy = 0.153750 INFO: root: Epoch[2] Time cost = 0.087 INFO: root: Epoch[3] Train - accuracy = 0.162500 INFO: root: Epoch[3] Time cost = 0.082 INFO: root: Epoch[4] Train - accuracy = 0.192500 INFO: root: Epoch[4] Time cost = 0.094 INFO: root: Epoch[5] Train - accuracy = 0.210000 INFO: root: Epoch[5] Time cost = 0.108 INFO: root: Epoch[6] Train - accuracy = 0.222500 INFO: root: Epoch[6] Time cost = 0.104 INFO: root: Epoch[7] Train - accuracy = 0.243750 INFO: root: Epoch[7] Time cost = 0.110 INFO: root: Epoch[8] Train - accuracy = 0.263750 INFO: root: Epoch[8] Time cost = 0.101 INFO: root: Epoch[9] Train - accuracy = 0.286250 INFO: root: Epoch[9] Time cost = 0.097 INFO: root: Epoch[10] Train - accuracy = 0.306250 INFO: root: Epoch[10] Time cost = 0.100...INFO: root: Epoch[20] Train - accuracy = 0.507500...INFO: root: Epoch[30] Train - accuracy = 0.718750...INFO: root: Epoch[40] Train - accuracy = 0.923750...INFO: root: Epoch[50] Train - accuracy = 0.998750 INFO: root: Epoch[50] Time cost = 0.077
如你所见,训练的准确度有了飞速提升,50 个回合后已经接近 99% 以上。似乎我们的网络已经从训练数据集中学成了。非常惊人!
但针对验证数据集执行的效果如何呢?
随后将新的数据样本放入网络,例如剩下的那 20% 尚未在训练中使用过的数据。
首先构建一个迭代器,这一次将使用验证样本和标签。
- pred_iter = mx.io.NDArrayIter(data = X_valid, label = Y_valid, batch_size = batch)
随后要使用 Module.iter_predict() 函数,借此让样本在网络中运行。这样做的同时,还需要对预测的标签和实际标签进行对比。我们需要追踪比分并显示验证准确度,即,网络针对验证数据集的执行效果到底如何。
- pred_count = valid_count correct_preds = total_correct_preds = 0
- for preds,
- i_batch,
- batch in mod.iter_predict(pred_iter) : label = batch.label[0].asnumpy().astype(int) pred_label = preds[0].asnumpy().argmax(axis = 1) correct_preds = np.sum(pred_label == label) total_correct_preds = total_correct_preds + correct_preds print('Validation accuracy: %2.2f' % (1.0 * total_correct_preds / pred_count))
这个过程中发生了不少事 :)
iter_predict() 返回了:
来源: http://www.infoq.com/cn/articles/an-introduction-to-the-mxnet-api-part03