from mxnet import cpu, gpu, nd
Context
Context
是模型的数据放置的环境:
a = nd.array([2, 4], ctx=cpu())
a1 = nd.array([3, 7], ctx=gpu(0))
a.context, a1.context
(cpu(0), gpu(0))
当然,也可以在 CPU 与 GPU 之间进行复制。
a2 = a.copyto(a1) # 要求 a 与 a1 有相同的 shape
a1 is a2, a2.context
(True, gpu(0))
a1 # a1 被修改了
[2. 4.]
<NDArray 2 @gpu(0)>
为了进行深度复制,需要使用:
a3 = nd.array([3, 9])
a4 = a3.as_in_context(gpu(0))
a3 is a4, a3.context, a4.context
(False, cpu(0), gpu(0))
Symbol
Symbol
的基本函数 - 定义计算图Symbol.infer_type
: 推导当前 Symbol 所依赖的所有 Symbol 数据类型Symbol.infer_shape
: 推导当前 Symbol 所依赖的所有 Symbol 的形状Symbol.list_argments
: 列出当前 Symbol 所用到的基本参数名称Symbo.list_outputs
: 列出当前 Symbol 的输出名称Symbol.list_auxiliary_states
: 列出当前 Symbol 的辅助参量名称
from mxnet import sym, symbol
X = sym.Variable('X')
out = sym.FullyConnected(data=X, name='fc1', num_hidden=1000)
out = symbol.BatchNorm(out, name='batchnorm')
out = sym.Activation(data=out, act_type='relu')
out = sym.FullyConnected(data=out, name='fc2', num_hidden=10)
arg_types, out_types, aux_types = out.infer_type(X='float32')
arg_types, out_types, aux_types
([numpy.float32,
numpy.float32,
numpy.float32,
numpy.float32,
numpy.float32,
numpy.float32,
numpy.float32],
[numpy.float32],
[numpy.float32, numpy.float32])
arg_shapes, out_shapes, aux_shapes = out.infer_shape(X=(100,784))
arg_shapes, out_shapes, aux_shapes
([(100, 784), (1000, 784), (1000,), (1000,), (1000,), (10, 1000), (10,)],
[(100, 10)],
[(1000,), (1000,)])
out.list_arguments()
['X',
'fc1_weight',
'fc1_bias',
'batchnorm_gamma',
'batchnorm_beta',
'fc2_weight',
'fc2_bias']
out.list_outputs()
['fc2_output']
out.list_auxiliary_states()
['batchnorm_moving_mean', 'batchnorm_moving_var']
Symbol 如何获取中间节点
在定义好一个网络之后,如何去获取任何一个节点的输出值对于深度神经网络的迁移来说非常重要,因为在使用时通常并不是自己从头开始训练一个网络,而是在别人训练好的网络基础上根据自己的问题进行微调。
X = sym.Variable('X')
fc1 = sym.FullyConnected(data=X, name='fc1', num_hidden=1000)
act = sym.Activation(data=fc1, act_type='relu')
fc2 = sym.FullyConnected(data=act, name='fc2', num_hidden=10)
net = sym.SoftmaxOutput(fc2, name="softmax")
net.get_internals()
<Symbol group [X, fc1_weight, fc1_bias, fc1, activation0, fc2_weight, fc2_bias, fc2, softmax_label, softmax]>
subnet = net.get_internals()['fc2_output']
subnet, subnet.list_arguments()
(<Symbol fc2>, ['X', 'fc1_weight', 'fc1_bias', 'fc2_weight', 'fc2_bias'])
首先使用 get_internals()
获取整个 Symbol 的子图,输出是整个内部节点的输出节点列表。然后可以通过索引获取网络的子图。在上面的例子中,我们获取网络层的倒数第二层 fc2,可以看到 fc2 也是一个 Symbol 对象。
fc2.list_arguments()
['X', 'fc1_weight', 'fc1_bias', 'fc2_weight', 'fc2_bias']
同理,有:
subnet = net.get_internals()['fc1_output']
subnet.list_arguments()
['X', 'fc1_weight', 'fc1_bias']
图的拼接
假如,我们拿到了别人训练好的网络参数文件和网络结构文件,我们可以固定网络图的前部分,在尾部添加额外的 Symbol 节点,但是在网络的头部替换输入节点较困难。
X = sym.Variable('X')
fc1 = sym.FullyConnected(data=X, name='fc1', num_hidden=1000)
act = sym.Activation(data=fc1, act_type='relu',name='act')
fc2 = sym.FullyConnected(data=act, name='fc2', num_hidden=10)
net = sym.SoftmaxOutput(fc2,name="softmax")
net.save('model.symbol.json')
如上面所示,我们先定义好了一个 包含 10 个输出节点网络,然后将 Smbol 网络保存到 json 文件。接下来我们重新加载这个 json 文件,并且在网络结尾处重新修改网络层的输出为 20。
net = sym.load('model.symbol.json')
net.get_internals().list_outputs()
['X',
'fc1_weight',
'fc1_bias',
'fc1_output',
'act_output',
'fc2_weight',
'fc2_bias',
'fc2_output',
'softmax_label',
'softmax_output']
newnet = net.get_internals()['act_output']
newnet = sym.FullyConnected(data=fc1,num_hidden=20,name ='fc2_new')
newnet = sym.SoftmaxOutput(data=newnet,name='softmax_new')
newnet.get_internals().list_outputs()
['X',
'fc1_weight',
'fc1_bias',
'fc1_output',
'fc2_new_weight',
'fc2_new_bias',
'fc2_new_output',
'softmax_new_label',
'softmax_new_output']
Metric
Metric 是用来衡量模型效果的接口
当我们定义好一个 Metric,比如说 Accuracy , 然后将 Accuracy 交给 Module 托管的时候,在每个 Epoch 结束时,会自动调用 update 方法,计算 正确预测的样本数量和总共的样本数量,进而调用父类中的 get 方法,计算出最后的 Acc。
from mxnet import metric
predicts = [nd.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
labels = [nd.array([0, 1, 1])]
acc = metric.Accuracy()
acc.update(preds = predicts, labels = labels)
acc.get()
('accuracy', 0.6666666666666666)
Metric Hack 分析
如果我们想要定义 自己的 Metric 类,需要完成下面几步:
-
继承
metric.EvalMetric
接口,重新定义update
方法,update 传入参数分析:- labels :
list
类型,每个元素对应 DataBatch 中的label
predicts
:list
类型, 是 Loss Symbol 中 label 外的输入,因此list
中的元素个数与网络上loss
的个数有关
- labels :
update 函数需要完成:
- 更新属性
sum_metric
和num_inst
的值,mxnet 会调用get
函数中的self.sum_metric / self.num_inst
来计算当前metric
的输出值。 - 与一个特殊的
Callback
类有关:Speedometer
会自动打印出所有metric
的值。