MXNet学习3——Symbol

    xiaoxiao2021-03-25  175

    概要

    本节介绍MXNet中的Symbol(模块)。Symbol是MXNet中另一个重要的概念,可以通过 mxnet.symbol 或者 mxnet.sym 使用。一个symbol表示一个具有多输出的符号表达式,表达式由多个运算组成,运算可以是简单的矩阵和(“+”),也可以是一个神经网络层(比如卷积层)。一个运算可以有多个输入,产生一个或多个输出,同时也可以由隐变量。变量可以是一个等待绑定参数值的自由符号,也可以是其他symbol的某一个输出值。

    MXNet中重要概念NDArray与numpy类似,操作上也比较简单就不单独说明了,之后代码中遇到会提及。另外,本节代码跳过了官方tutorial中存储部分以及卷积神经网络部分,有些说明部分没有摘录,感兴趣请参考官方教程

    正文

    基础操作

    import mxnet as mx ###构造一个简单的表达式 a+b a = mx.sym.Variable('a') b = mx.sym.Variable('b') ###c没有人为指定名称,MXNet会自动命名一个不重复的名称,注意不一定是"C" c = a + b ###下方的操作类似numpy # elemental wise times d = a * b # matrix multiplication e = mx.sym.dot(a, b) # reshape f = mx.sym.Reshape(d+e, shape=(1,4)) # broadcast g = mx.sym.broadcast_to(f, shape=(2,4)) mx.viz.plot_network(symbol=g).view()

    基础神经网络

    除了上述基本的操作之外,Symbol还有一系列神经网络层,下面的代码构造了一个两层全连接的神经网络

    ###以下神经网络比较简单,就不单独说明了,其中relu是激活函数 # Output may vary 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=10) net = mx.sym.SoftmaxOutput(data=net, name='out') mx.viz.plot_network(net, shape={'data':(100,200)}).view()

    组合多个Symbol

    如果想构造具有多种输出的神经网络,可以使用mxnet.sym.Group将多个组合在一起,比如以下softmax和线性回归

    net = mx.sym.Variable('data') fc1 = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=128) net = mx.sym.Activation(data=fc1, name='relu1', act_type="relu") out1 = mx.sym.SoftmaxOutput(data=net, name='softmax') out2 = mx.sym.LinearRegressionOutput(data=net, name='regression') group = mx.sym.Group([out1, out2]) group.list_outputs()

    [‘softmax_output’, ‘regression_output’]

    NDArray与Symbol的区别

    事实上MXNet的Symbol和NDArray很类似(NDArray和numpy类似),两者都提供了多维数组的操作,比如c=a+b,若想详细区分请点这里看官网说明,以下给出简单的区分。

    NDArray 中的计算是一行一行向下顺序执行的,每一个参数都会参与当前操作的计算,并传递。而Symbol更类似于描述程序的逻辑,首先是申明计算然后再给参数赋值,机制类似正则和SQL(Examples in this category include regular expression and SQL,官网说的这个没有太懂)。

    NDArray的优势:

    直接能简单的结合编程语言的特性(比如循环,判断)和函数库(比如numpy)能简单的一步步debug(吐槽一下,MXNet真的不容易debug,隔壁Tensorflow的Tensorboard貌似不错)

    Symbol的优势:

    提供了NDArray几乎所有的方法,诸如 +, *, sin, shape提供了神经网络相关的操作,诸如卷积,激活函数,BatchNorm(不然就不用MXNet了)自动求导简单的构造和操纵复杂的计算,比如深度神经网络简单的存储,加载参数和可视化后端能简单的优化计算和内存占用

    下一节将描述如何结合两者开发一个完整的训练程序,这里还是专注Symbol吧。

    Symbol的操纵

    上文讲到Symbol相比较NDArray的一个重要区别是,必须首先申明计算,然后赋值,才能计算。下面将介绍如何直接操纵一个Symbol,需要注意的是,下面部分与module联系紧密,如果不关心可以跳过。

    Shape Inference

    对于每个symbol可以直接查询其输入和输出。也可以通过给定的输入shape推断出输出的shape,这有利于更好的分配内存。

    arg_name = c.list_arguments() # get the names of the inputs out_name = c.list_outputs() # get the names of the outputs arg_shape, out_shape, _ = c.infer_shape(a=(2,3), b=(2,3)) {'input' : dict(zip(arg_name, arg_shape)), 'output' : dict(zip(out_name, out_shape))}

    {‘input’: {‘a’: (2L, 3L), ‘b’: (2L, 3L)}, ‘output’: {‘_plus0_output’: (2L, 3L)}}

    Bind with Data and Evaluate

    Symbol c声明了即将要运行的计算,为了得到结果,我们需要赋值。bind方法通过接收设备(cpu,gpu)信息以及一个字典(参数名称映射NDArrays)作为参数,然后返回一个执行器。这个执行器提供了forward用来计算以及得到所有的结果。

    ex = c.bind(ctx=mx.cpu(), args={'a' : mx.nd.ones([2,3]), 'b' : mx.nd.ones([2,3])}) ex.forward() print 'number of outputs = %d\nthe first output = \n%s' % ( len(ex.outputs), ex.outputs[0].asnumpy()) number of outputs = 1 the first output = [[ 2. 2. 2.] [ 2. 2. 2.]]

    定制Symbol

    MXNet允许用户自己定制Symbol,只需要自行定义前向(forward )和后向(backward 计算方法,同时提供一些属性查询方法,比如 list_arguments and infer_shape. forward and backward默认的参数类型都是NDArray,但为了显示MXNet的灵活性,这里展示使用numpy实现softmax 层。Numpy实现的操作只能在CPU上运行,但是提供的函数十分丰富,比较利于演示。

    首先定义一个mx.operator.CustomOp的子类然后实现 forward and backward.

    class Softmax(mx.operator.CustomOp): def forward(self, is_train, req, in_data, out_data, aux): x = in_data[0].asnumpy() ###将NDArray转换成numpy.ndarray y = np.exp(x - x.max(axis=1).reshape((x.shape[0], 1))) y /= y.sum(axis=1).reshape((x.shape[0], 1)) self.assign(out_data[0], req[0], mx.nd.array(y)) ###将numpy.ndarray转换成NDArray ###CustomOp.assign 将转换后的y赋值给out_data[0],根据req的不同可能是覆盖或者写入 def backward(self, req, out_grad, in_data, out_data, in_grad, aux): l = in_data[1].asnumpy().ravel().astype(np.int) y = out_data[0].asnumpy() y[np.arange(l.shape[0]), l] -= 1.0 self.assign(in_grad[0], req[0], mx.nd.array(y))

    接下来在定义一个mx.operator.CustomOpProp的子类用来查询属性

    # register this operator into MXNet by name "softmax" @mx.operator.register("softmax") class SoftmaxProp(mx.operator.CustomOpProp): def __init__(self): # softmax is a loss layer so we don’t need gradient input # from layers above. super(SoftmaxProp, self).__init__(need_top_grad=False) def list_arguments(self): return ['data', 'label'] def list_outputs(self): return ['output'] def infer_shape(self, in_shape): data_shape = in_shape[0] label_shape = (in_shape[0][0],) output_shape = in_shape[0] return [data_shape, label_shape], [output_shape], [] def create_operator(self, ctx, shapes, dtypes): return Softmax()

    最后我们就可以使用刚刚自己定制的操作了,需要注意的是操作的名称我们已经申明注册过了

    net = mx.symbol.Custom(data=prev_input, op_type='softmax')

    定制这块的内容,官方tutorial给的十分简易,按照目前的学习进度也不需要掌握这块知识,所以略过啦~


    这一次对tutorial中的内容翻译了不少,但有些方法名翻译成中文感觉还挺奇怪的,果然基础还是太查了/(ㄒoㄒ)/~~不管怎么样,欢迎大家拍砖讨论

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

    最新回复(0)