MXNet学习4——Mixed Programing

    xiaoxiao2021-03-25  101

    概要

    前面的文章中讲到了Symbol和NDArray,介绍了如何从零开始训练神经网络。这种混杂风是MXNet相较与其他深度学习框架的不同的地方,值得一提的是MXNet名字中的”MX”同样也蕴含着混合(mixed)的意思。官方提供了很多的方法并都实现了,而这一篇旨在介绍如何自己从零搭建网络并训练。本文与官方tutorial大体相同,详情请点这里


    正文

    训练一个多层感知机

    下面将展示使用一个两层的感知机,首先定义网络

    import mxnet as mx num_classes = 10 net = mx.sym.Variable('data') net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=128) net = mx.sym.Activation(data=net, name='relu1', act_type="relu") net = mx.sym.FullyConnected(data=net, name='fc2', num_hidden=num_classes) net = mx.sym.SoftmaxOutput(data=net, name='out') mx.viz.plot_network(net).view()

    上面定义的自由变量包括两层的权值w和偏差b,训练数据,标签和输出,下面使用list_argument方法查看下参数,以求有一个直观的认识。需要注意的是后缀 _weight, _bias, _label 是自动加上去的

    print(net.list_arguments()) ['data', 'fc1_weight', 'fc1_bias', 'fc2_weight', 'fc2_bias', 'out_label']

    回顾前面的文章,如果要前向和后向(forward and backward)计算,首先需要将所有的自由变量(free variables)绑定值。这里我们创建所有的NDArrays,然后进行绑定,就像我们在Symbol篇里那样。有一个简单的bind方法simple_bind可以完成上述的事情。这个方法实现原理是首先根据提供的数据shape推断出所有自由变量的shape,然后分配内存和绑定数据,最后返回一个执行器。其中绑定的自由变量的属性可以通过arg_arrays来访问。下面是例子,仍旧是数字识别,其中mx.cpu()可以改成mx.gpu():

    num_features = 100 batch_size = 100 ex = net.simple_bind(ctx=mx.cpu(), data=(batch_size, num_features)) args = dict(zip(net.list_arguments(), ex.arg_arrays)) for name in args: print(name, args[name].shape)

    (‘fc2_weight’, (10L, 128L)) (‘fc1_weight’, (128L, 100L)) (‘out_label’, (100L,)) (‘fc2_bias’, (10L,)) (‘data’, (100L, 100L)) (‘fc1_bias’, (128L,))

    这里需要特别注意的是bind方法在Symbol中和Module中都有提供,而simple_bind只有Symbol提供。两者的对比在文章最后简要介绍,详细了解请看官网API

    绑定完之后,接下来就需要初始化了,我们随机初始化

    for name in args: data = args[name] ###可以看到我们真正需要初始化的参数只有weight和bias,两层对应4个参数 ###mx.random.uniform是在区间内等概论随机取值 if 'weight' in name: data[:] = mx.random.uniform(-0.1, 0.1, data.shape) if 'bias' in name: data[:] = 0

    在训练之前,我们需要获取训练数据集,当然是程序自动生成啦

    import numpy as np import matplotlib.pyplot as plt class ToyData: """ num_classes 分类个数 num_features 特征数 mu 正态分布中均值数组,0~1的随机数 sigma 正太分布中标准差数组,全为0.1 mu和sigma是为了规定每个数字对应的特征,定义标准的作用 """ def __init__(self, num_classes, num_features): self.num_classes = num_classes self.num_features = num_features self.mu = np.random.rand(num_classes, num_features) self.sigma = np.ones((num_classes, num_features)) * 0.1 """ get方法是取数据,x是训练数据,y是对应的分类 num_samples是训练数据的个数,这里默认是分类数的整数倍,对应数字识别的例子也就是10的整数倍 np.random.normal是正太分布随机采样 不用关心特征和数字是如何对应的,因为这是我们自己定义的,只要初始化规则就已经确定了 """ def get(self, num_samples): num_cls_samples = int(num_samples / self.num_classes) x = np.zeros((num_samples, self.num_features)) y = np.zeros((num_samples, )) for i in range(self.num_classes): cls_samples = np.random.normal(self.mu[i,:], self.sigma[i,:], (num_cls_samples, self.num_features)) x[i*num_cls_samples:(i+1)*num_cls_samples] = cls_samples y[i*num_cls_samples:(i+1)*num_cls_samples] = i return x, y def plot(self, x, y): colors = ['r', 'b', 'g', 'c', 'y'] for i in range(self.num_classes): cls_x = x[y == i] plt.scatter(cls_x[:,0], cls_x[:,1], color=colors[i%5], s=1) plt.show() ###此处初始化规则就确定了 toy_data = ToyData(num_classes, num_features) x, y = toy_data.get(1000) toy_data.plot(x,y)

    终于,我们可以开始训练了。下面我们使用学习率固定的plain minibatch随机梯度下降方法,迭代10次

    # @@@ AUTOTEST_OUTPUT_IGNORED_CELL learning_rate = 0.1 final_acc = 0 for i in range(100): x, y = toy_data.get(batch_size) ###赋值,args是我们之前跟网络绑定的参数 args['data'][:] = x args['out_label'][:] = y ###is_train=True表示参数需要训练更新,不要此参数也可以,因为默认已经设置为True了 ###由于ex中已经有args,而args中有训练的数据所以此处forward不需要提供训练数据 ex.forward(is_train=True) ex.backward() """ 这里不像学习2中的Module代码,没有update方法,参数都是手动更新 这里如果不懂可以参考知乎上的一篇笔记(https://zhuanlan.zhihu.com/p/25081671?utm_source=tuicool&utm_medium=referral) mx.nd.argmax_channel求的是batch_size*num_classes的数组,每个行向量的最大值的下标,最后得出的结果是batch_size维的列向量 """ for weight, grad in zip(ex.arg_arrays, ex.grad_arrays): weight[:] -= learning_rate * (grad / batch_size) if i % 10 == 0: acc = (mx.nd.argmax_channel(ex.outputs[0]).asnumpy() == y).sum() final_acc = acc print('iteration %d, accuracy %f' % (i, float(acc)/y.shape[0])) assert final_acc > 0.95, "Low training accuracy."

    iteration 0, accuracy 0.210000 iteration 10, accuracy 0.990000 iteration 20, accuracy 1.000000 iteration 30, accuracy 1.000000 iteration 40, accuracy 1.000000 iteration 50, accuracy 1.000000 iteration 60, accuracy 1.000000 iteration 70, accuracy 1.000000 iteration 80, accuracy 1.000000 iteration 90, accuracy 1.000000

    这一节我们展示了如何将NDArray和Symbol结合起来实现一个完整的训练算法。

    NDArray常用在如下场景

    数据容器容器对灵活性要求较高的理程序,比如实现更新参数,监控优化方法中的处理流程实现Symbol运算debug,比如打印每一步的结果

    Symbol常用来定义对象函数,它受益于对Symbol和自动求导的大量优化

    未完待续

    下一篇将接着本篇文章,讲多设备下的数据并行,并用程序实现

    转载请注明原文地址: https://ju.6miu.com/read-24863.html

    最新回复(0)