zoukankan      html  css  js  c++  java
  • mxnet 变尺寸输入

    这是个很现实的问题,因为实际中的图片(呃,这里是说多源途径得到的)大部分都是变尺寸的。
    于是每次都要进行bind。然而(hehe),单纯的bind会带来一些一想不到的效果 huixiao。

    重复 bind 使update无效

    import mxnet as mx
    
    M,N=3,3
    num_filter=1
    kernel=mx.nd.array([ [1,2,3],[1,2,3],[1,2,3] ])
    kernel1=mx.nd.array([ [1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4] ])
    
    d=mx.sym.Variable('data')
    
    sym=mx.sym.Convolution(data=d,kernel=(3,3),num_filter=num_filter,no_bias=True,name='conv1')
    sym=mx.sym.sum(sym)
    sym=mx.sym.MakeLoss(data=sym)
    bch_kernel=kernel.reshape((1,1,M,N))
    bch_kernel1=kernel1.reshape((1,1,M+1,N+1))
    
    arg_params={'conv1_weight': bch_kernel}
    mod=mx.mod.Module(symbol=sym,data_names=('data',),label_names=None)
    
    mod.bind(data_shapes=[ ('data',[1,1,M,N]),])
    mod.init_params()  
    mod.set_params(arg_params=arg_params, aux_params=None,allow_missing=True)  
    mod.init_optimizer()  
    ######################   test whether re_bind  will effect weight   ########### 
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    #array([[[[ 42.]]]], dtype=float32)
    mod.backward()
    mod.update()
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    #array([ 41.57999802], dtype=float32)
    
    mod.bind(data_shapes=[ ('data',[1,1,M,N]),],force_rebind=True)  # 重新 bind 一下
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    # array([ 42.], dtype=float32)                       # update 无效了
    

    多次 backward 与一次 等价

    由于一次只能输入一个样本,所以backward最好能不间断进行几轮后,然后再进行update。但原始的backward也不支持这种操作:

    import mxnet as mx
    
    M,N=3,3
    num_filter=1
    kernel=mx.nd.array([ [1,2,3],[1,2,3],[1,2,3] ])
    kernel1=mx.nd.array([ [1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4] ])
    d=mx.sym.Variable('data')
    sym=mx.sym.Convolution(data=d,kernel=(3,3),num_filter=num_filter,no_bias=True,name='conv1')
    sym=mx.sym.sum(sym)
    sym=mx.sym.MakeLoss(data=sym)
    bch_kernel=kernel.reshape((1,1,M,N))
    bch_kernel1=kernel1.reshape((1,1,M+1,N+1))
    arg_params={'conv1_weight': bch_kernel}
    
    mod=mx.mod.Module(symbol=sym,data_names=('data',),label_names=None)
    
    
    mod.bind(data_shapes=[ ('data',[1,1,M,N]),])
    
    mod.init_params()  
    mod.set_params(arg_params=arg_params, aux_params=None,allow_missing=True)  
    mod.init_optimizer() 
    
    
    ############################################   test whether continue using backward is ok (acc the grad)  ### NOT ok!
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    #array([[[[ 42.]]]], dtype=float32)
    mod.backward()
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.backward()                   # 两次 backward
    
    mod.update()
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    # array([ 41.57999802], dtype=float32)    这实际是一次 backward 就能达到的效果
    

    Solution

    想起之前看 example/rcnn时留意到一个关于变尺寸输入的module,翻出来试了下,可以解决第一个问题,稍微改进下,搞定第二个问题。


    Jul 31, 2017 记
    文件较长,移到github上,module.py见后文链接。


    增加了acc_backwardacc_update用以进行累计操作。

    测试

    
    import mxnet as mx
    from module import MutableModule  # 上文的 module 文件
    
    M,N=3,3
    num_filter=1
    kernel=mx.nd.array([ [1,2,3],[1,2,3],[1,2,3] ])
    kernel1=mx.nd.array([ [1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4] ])
    
    d=mx.sym.Variable('data')
    
    sym=mx.sym.Convolution(data=d,kernel=(3,3),num_filter=num_filter,no_bias=True,name='conv1')
    sym=mx.sym.sum(sym)
    sym=mx.sym.MakeLoss(data=sym)
    bch_kernel=kernel.reshape((1,1,M,N))
    bch_kernel1=kernel1.reshape((1,1,M+1,N+1))
    
    arg_params={'conv1_weight': bch_kernel}
    
    
    #mod=mx.mod.Module(symbol=sym,data_names=('data',),label_names=None)
    mod=MutableModule(symbol=sym,data_names=('data',),label_names=None)
    
    mod.bind(data_shapes=[ ('data',[1,1,M,N]),])
    
    mod.init_params()  
    mod.set_params(arg_params=arg_params, aux_params=None,allow_missing=True)  
    mod.init_optimizer()  
    
    
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    #array([[[[ 42.]]]], dtype=float32)
    mod.acc_backward()
    
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.acc_backward()
    
    
    mod.acc_update()
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    # array([ 41.57999802], dtype=float32)
    
    #############   more backward
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    #array([[[[ 42.]]]], dtype=float32)
    mod.acc_backward()
    
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.acc_backward()
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.acc_backward()
    
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.acc_backward()
    
    
    mod.acc_update()
    mod.forward(mx.io.DataBatch([bch_kernel],[],provide_data=[('data',bch_kernel.shape),] ) )
    mod.get_outputs()[0].asnumpy() 
    # array([ 40.73999786], dtype=float32)         YES !
    

    搞定!


    Jul 31, 2017 记

    save_checkpoint 的问题

    之前的程序存在一个潜在的问题,如果后续的训练过程中尺寸发生改变,最终需要保存训练的参数时,就会产生问题。从一些测试结果来看,如果只是调用self._curr_module.save_checkpoint, 保存的参数仅仅只是第一次发生输入数据尺寸扩张(也可能是缩小,但此处不是重点)之前的参数。从原始支持变形版的module.py来看,问题应该产生与forward接近结束前的bind中,这个调用使用了shared_module功能,但doc上对这一功能描述难以确定其具体机制。
    在查看mxnet/python/mxnet/module时意外发现bucketing_module.py是变形版module.py的原型,主要是发现其实现原理都是在forward中进行使用shared_module功能的bind操作,但在这个相当于官方的程序中却没有发现save_checkpoint的定义(这令人费解,根据测试的结果,通过这种方式的实现,简单的svae_checkpoint会导致以后的噩梦--比如我就是在发现load之前的参数,得到0.01acc时意识到这个问题的)
    但是能够work的参数肯定存在,否则,训练中acc就不会持续上升。

    解决这个问题的思路,来自于另一个意外发现,mxnet/python/mxnet/module/module.pyupdate操作,其中有一个对self._params_dirty的赋值操作,这个名字看上去很有意思。追踪发现_sync_params_from_devices是个有意思的调用:

    #mxnet/python/mxnet/module/module.py
        def _sync_params_from_devices(self):
            """Synchronizes parameters from devices to CPU. This function should be called after
            calling `update` that updates the parameters on the devices, before one can read the
            latest parameters from ``self._arg_params`` and ``self._aux_params``.
            """
            self._exec_group.get_params(self._arg_params, self._aux_params)
            self._params_dirty = False
    

    从注释中已经可以明白些了。试了一下,可以解决问题。

    增加的功能如下:

    1. acc_backward 每次forward后调用,可以对grad进行累加
    2. acc_update 多次 acc_backward后使用,接受归一化参数 , 以上两个用以对 bach_size进行扩充
    3. save_checkpoint 保存训练所得参数,其他调用途径肯能导致保存错误的参数

    TODO

    1. 需要一个类似与mx.mod.Module.load的接口。 现在程序进行这一步较为混乱。

    Appendix

    1. 修改后的module.py文件在这里

  • 相关阅读:
    python上selenium的弹框操作
    python中selenuim模块定位方法详解
    python中Selenium模块的安装与简单使用(详细)
    postman测试用例做断言
    postman写测试用例
    python之单元测试及unittest框架的使用
    spring boot mybatis redis缓存
    web插件
    jQuery事件
    C++对象动态内存
  • 原文地址:https://www.cnblogs.com/chenyliang/p/7218364.html
Copyright © 2011-2022 走看看