mxnet module 解析

2018-01-31  本文已影响0人  迷途的Go

mxnet module.py类

module.py是一个重要的类,一个完整的训练过程都要经过这个类

构建模型

>>> data = mx.sym.Variable('data')
>>> fc1  = mx.sym.FullyConnected(data, name='fc1', num_hidden=128)
>>> act1 = mx.sym.Activation(fc1, name='relu1', act_type="relu")
>>> fc2  = mx.sym.FullyConnected(act1, name='fc2', num_hidden=10)
>>> out  = mx.sym.SoftmaxOutput(fc2, name = 'softmax')
>>> mod = mx.mod.Module(out)
>>> out
<Symbol softmax>
>>> mod
<mxnet.module.module.Module object at 0x7f52df417c50>
>>>

上述使用符号式编程构建了一个计算图,SoftmaxOutput带了loss

bind

假设这里已经加载出imagenet的数据集data_set, 包含两个MXDataIter的数据,一个train,一个val,

>>> mod.bind(data_shapes=train.provide_data, label_shapes=train.provide_label)
>>> data_shapes
[DataDesc[data,(128L, 3L, 224L, 224L),<type 'numpy.float32'>,NCHW]]
>>> mod.bind(data_shapes=train.provide_data, label_shapes=train.provide_label)
>>> label_shapes = train.provide_label
>>> label_shapes
[DataDesc[softmax_label,(128L,),<type 'numpy.float32'>,NCHW]]

把初始化module时候的symbols绑定到construct executors,并初始化内存

bind首先得到DataDesc类型的_data_shape,_label_shape(通过参数传递进来),_symbol,_context(初始化的时候得到),这些参数传给DataParallelExecutorGroup, 得到_exec_group对象,这是个重要的对象,如果参数已经初始化,就将初始化后参数和辅助参数设置给_exec_group对象

下面整理一下executor_group.py这个类及其所对应的对象DataParallelExecutorGroup

接收参数:

全局变量:

 000 = {str} 'bn_data_gamma'
 001 = {str} 'bn_data_beta'
 002 = {str} 'conv0_weight'
 003 = {str} 'bn0_gamma'
 004 = {str} 'bn0_beta'
 005 = {str} 'stage1_unit1_bn1_gamma'
 006 = {str} 'stage1_unit1_bn1_beta'
 007 = {str} 'stage1_unit1_conv1_weight'
 008 = {str} 'stage1_unit1_bn2_gamma'
 009 = {str} 'stage1_unit1_bn2_beta'
 010 = {str} 'stage1_unit1_conv2_weight'
 011 = {str} 'stage1_unit1_bn3_gamma'
 .
 .
 .
 151 = {str} 'stage4_unit3_bn3_beta'
 152 = {str} 'stage4_unit3_conv3_weight'
 153 = {str} 'bn1_gamma'
 154 = {str} 'bn1_beta'
 155 = {str} 'fc1_weight'
 156 = {str} 'fc1_bias'
self.aux_names = {list} <type 'list'>: ['bn_data_moving_mean', 'bn_data_moving_var', 'bn0_moving_mean', 'bn0_moving_var', 'stage1_unit1_bn1_moving_mean', 'stage1_unit1_bn1_moving_var', 'stage1_unit1_bn2_moving_mean', 'stage1_unit1_bn2_moving_var', 'stage1_unit1_bn3_moving_mean', 
 __len__ = {int} 102
 000 = {str} 'bn_data_moving_mean'
 001 = {str} 'bn_data_moving_var'
 002 = {str} 'bn0_moving_mean'
 003 = {str} 'bn0_moving_var'
 004 = {str} 'stage1_unit1_bn1_moving_mean'
 005 = {str} 'stage1_unit1_bn1_moving_var'
 006 = {str} 'stage1_unit1_bn2_moving_mean'
 007 = {str} 'stage1_unit1_bn2_moving_var'
 098 = {str} 'stage4_unit3_bn3_moving_mean'
 099 = {str} 'stage4_unit3_bn3_moving_var'
 100 = {str} 'bn1_moving_mean'
 101 = {str} 'bn1_moving_var'
self.workload = {list} <type 'list'>: [1, 1, 1, 1]
 __len__ = {int} 4
 0 = {int} 1
 1 = {int} 1
 2 = {int} 1
 3 = {int} 1

execs-mxnet.executor.Executor对象,有几个设备就有几个对象,比如四个GPU,就四个这样对象的列表

grad_arrays-也绑定在各个设备上,目测是用于更新参数的梯度,每个GPU计算自己的梯度

param_names-参数列表名称,比如每个卷积的卷积核weight, bias

有计算图symbol,data_shape,label_shape,那么整个网络的细节就已经清楚

grad_requirement, 'write', 'add'或'null',add是把所有的梯度加起来,wirte是什么操作?

symbol.py的check_call(_LIB.MXExecutorSimpleBind())这个函数干了什么?

该方法内部根据设备的个数得到executor,初始化一个executor会初始化参数和辅助参数

执行完bind_exec的_collect_arrays,executor_group的data_arrays得到初始化

init_params

执行完init_params函数,module对象的_exec_group变量的param_arrays对象就不再全部是0,而是用户传递进来的, init_params直接调用_exec_group对象的set_params方法,然后对每个executor执行初始化

init_optimizer

module的init_optimizer方法根据用户传递进来的optimier的字符串和参数,通过optimizer.py创建一个optimizer对象

此外该方法初始化了kvstore,_initialize_kvstore,这个还没有弄明白

forward

module.py->forward()->executor_group.py->forward->为每一个设备的Executor执行forward->executor.py->forward,这里调用C++的MXExecutorForward,执行后,executor.py的self.outputs得到输出,每个输出的大小是32x1000,每一张图片一次前向计算得到1000个概率值,代表该次计算是某一个类的概率

backword

module.py->backward->executor_group.py->backward()->executor.py->MXExecutorBackwardEx(),执行反向计算,反向计算得到梯度self.grad_arrays,存储在每一个executor对象

update

通过optimizer更新梯度到参数,梯度是之前前向-后向计算得到的张量,DataParallelExecutorGroup对象持有设备数量个Executor,每个executor对象持有一个分grad_arrays,这个地方有些疑惑,update方法传递进去的是_exec_group.grad_arrays, 将每一个executor上的梯度传递给_exec_group.grad_arrays的方法_collect_arrays只有bind_exec的时候执行过一次,backward方法完了并没有看到_exec_group去收集各个executor的梯度。--这个问题的可能解释是bind执行的那次把每一个executor的梯度给 exec_group的梯度是指针或者引用,那每次更新了每个executor的梯度,exec_group的梯度值自然就得到了更新

最终梯度聚合在kvstore_dist.h的Push_方法里将梯度相加

和C++怎么交互的还没看明白,看明白的大神请给我留个文章链接,我来研究研究

参考http://www.cnblogs.com/heguanyou/p/7604326.html

上一篇 下一篇

猜你喜欢

热点阅读