MxNet学习笔记(3):关于Symbol

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/richard_che/article/details/73719276

Symbol

与caffe类似,MxNet中定义了符号运算。我对符号运算的理解是:区别于之前介绍的NDArray,通过符号,我们可以定义出一系列的表达式,或者网络。这样定义出来的表达式或网络有点类似于数学中的函数式,比如f(x)=ax+b,这时候,a,x,b,都仅仅是一些符号,本身的值是未确定的。当这些符号被赋予具体的数值时,才能计算出函数式本身的值f(x)。而在NDArray中,就没有这个“未确定”的概念了。

换言之,Symbol构造出表达式之后,还需要与具体的数值绑定起来,才能发挥作用。

与NDArray相比,它们各自有这些优点:

  • NDArray:

    • Straightforward.
    • Easy to work with native language features (for loop, if-else condition, ..) and libraries (numpy, ..).
    • Easy step-by-step code debugging.
  • Symbol:
    • Provides almost all functionalities of NDArray, such as +, *, sin, reshape etc.
    • Easy to save, load and visualize.
    • Easy for the backend to optimize the computation and memory usage.

通过符号组建基本表达式

无论是什么网络,都要有输入输出。MxNet中,通过Variable来创建最基本的变量(包括输入/输出),比如:

a = mx.sym.Variable('a')
b = mx.sym.Variable('b')

然后符号之间就可以进行运算了,比如对a,b:

# elemental wise multiplication
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))

显然,这里对a,b分别先逐个相乘,然后再矩阵乘法,然后再将两种乘法结果加起来,变换维度……这里,NDArray所支持的那些运算,这里也支持。

网络的可视化

MxNet提供了很方便的API,对网络进行可视化。比如在上述的代码中,最后生成的网络是g,那么我们可以通过如下的方式对g进行可视化:

mx.viz.plot_network(g).view()

从而生成这样的图片:

生成网络的示意图

通过符号构造神经网络

除了一些基本的运算符以外,MxNet还支持各种各样的层。比如下面的一个包含两个全连接层的网络:

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')

可以看到,每一个layer的建立都需要提供data(该层的输入)还有name,作为它唯一的标识符。上述只是简单地去构造一个网络,对于一些大的网络,这样写太费劲了,通常会将部分模块化,比如通过如下的函数,构造一个卷积层:

def ConvFactory(data, num_filter, kernel, stride=(1,1), pad=(0, 0),name=None, suffix=''):
    conv = mx.sym.Convolution(data=data, num_filter=num_filter, kernel=kernel,
                  stride=stride, pad=pad, name='conv_%s%s' %(name, suffix))
    bn = mx.sym.BatchNorm(data=conv, name='bn_%s%s' %(name, suffix))
    act = mx.sym.Activation(data=bn, act_type='relu', name='relu_%s%s'
                  %(name, suffix))
    return act
prev = mx.sym.Variable(name="Previous Output")
conv_comp = ConvFactory(data=prev, num_filter=64, kernel=(7,7), stride=(2, 2))
shape = {"Previous Output" : (128, 3, 28, 28)}

除此之外,MxNet还支持将多个网络通过group组合起来,从而实现多个输出的网络:

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()

对符号的操作

  • 查看某一个网络里面有哪些符号,可以xxx.list_arguments,或者xxx.list_outputs只查看输出
  • 当创建某一个layer时,它的输出名字都是由电脑分配的。但这个时候可以通过这样的方式来手动指定名字:
net = mx.symbol.Variable('data')
w = mx.symbol.Variable('myweight')
net = mx.symbol.FullyConnected(data=net, weight=w, name='fc1', num_hidden=128)
net.list_arguments()
  • 定义好符号之后,可以通过bind来绑定数据然后进行运算:
gpu_device=mx.gpu() # Change this to mx.cpu() in absence of GPUs.

ex_gpu = c.bind(ctx=gpu_device, args={'a' : mx.nd.ones([3,4], gpu_device)*2,
                                      'b' : mx.nd.ones([3,4], gpu_device)*3})
ex_gpu.forward()
ex_gpu.outputs[0].asnumpy()

或者用eval:

ex = c.eval(ctx = mx.cpu(), a = mx.nd.ones([2,3]), b = mx.nd.ones([2,3]))
print('number of outputs = %d\nthe first output = \n%s' % (
            len(ex), ex[0].asnumpy()))

还有一种常用的方法是使用simple_bind,这样的话不需要对每一层的节点容量进行设置。

阅读更多
换一批

没有更多推荐了,返回首页