• #mxnet# 权值共享


    https://www.cnblogs.com/chenyliang/p/6847744.html

    Note:后记
    此权值共享非彼卷积共享。说的是layer实体间的参数共享。

    Introduction

    想将两幅图像”同时“经过同一模型,似乎之前有些听闻的shared model没有找到确凿的痕迹,单个构建Variable然后每层设置,对debug阶段(甚至使用阶段)来说是场噩梦。能够可行的只想到了,在set_params阶段进行指定,如果简单的将两个load的symbol进行Group,然后进行bind会提示出现多个名称。于是问题就是:如何生成同一结构内含指定符号名的symbol?

    Exploration

    此类非标准操作,更别指望mxnet的doc了,只有从dir()src查起。

    Change the name

    首先想到的自然是改名:
    本来是

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

    要改成与

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

    相同的效果。

    关于名称的接口:

    import mxnet as mx
    d=mx.sym.Variable('data')
    conv1_w=mx.sym.Variable('kw')
    conv1=mx.sym.Convolution(data=d,weight=conv1_w,kernel=(3,3),num_filter=num_filter,no_bias=True,name='conv1')
    conv1.name
    #'conv1'

    How to change it

    怎么改呢?看起来只有*_set_attr*靠谱些,先看看都有那些属性:

    conv1.list_attr()
    #{'no_bias': 'True', 'kernel': '(3, 3)', 'num_filter': '1'}

    。。。并没有什么好结果出现,看起来还有一个接口:

    conv1.attr_dict()
    #{'conv1': {'no_bias': 'True', 'kernel': '(3, 3)', 'num_filter': '1'}}

    那就试试,'conv1'?

    >>>conv1._set_attr(conv1='yy')
    >>>conv1.name   # 有戏?!赶紧看看
    'conv1'   # 那刚才的是什么?
    >>> conv1.list_attr()
    {'no_bias': 'True', 'kernel': '(3, 3)', 'conv1': 'yy', 'num_filter': '1'}  # 呵呵,被骗了...

    Check the Src

    来看看名字是到哪取的(~当然是家里取的...)

    # python/mxnet/symbol.py
        @property
        def name(self):
         ret = ctypes.c_char_p()
            success = ctypes.c_int()
            check_call(_LIB.MXSymbolGetName(
                self.handle, ctypes.byref(ret), ctypes.byref(success)))
            if success.value != 0:
                return py_str(ret.value)
            else:
                return None

    于是追寻MXSymbolGetName,虽然直觉告诉我很有可能不会有python接口了(很有可能是通过底层实现的名字获取),但还是得看看。

    //src/c_api/c_api_symbolic.cc
    int MXSymbolGetName(SymbolHandle symbol,
                        const char** out,
                        int* success) {
      return NNSymbolGetAttr(symbol, "name", out, success);
    }

    这不禁让人浮想起来。。。赶紧试试:

    >>> conv1._set_attr(name='yy')
    >>> conv1.name
    'yy'

    被我发现了吧 :)

    失败

    失败的原因是,上述的操作只改变了node,但参数的名称并没有改变(可以.list_arguments()进行查看)。我当时想的是将参数名称保持相同,然后在set_params的时候就可以直接调用,然而实际调用时,会报错,提示检测出了多个相同的名称,所以此路基本封死。

    json入手

    这是一个当时认为最惨的办法——每次都要先对文件进行操作(非常粗野)。但今早发现symbol中还有操作json的接口(当然说的不是save,laod之类的):

    sn_epoch_load=0
    model_prefix='nin'
    sym1, arg_params, aux_params = mx.mod.module.load_checkpoint(model_prefix, n_epoch_load)
    
    sym=sym1.get_internals()['conv4_1024_output'].__copy__()
    ss=sym.__getstate__()['handle']
    ss1=ss.replace('"name": "','"name": "sha-')
    sym2 = sym.__copy__()
    h={'handle':ss1}
    sym2.__setstate__(h)
    >>> sym2.list_arguments()
    ['sha-data', 'sha-conv1_weight', 'sha-conv1_bias', 'sha-cccp1_weight', 'sha-cccp1_bias', 'sha-cccp2_weight', 'sha-cccp2_bias', 'sha-conv2_weight', 'sha-conv2_bias', 'sha-cccp3_weight', 'sha-cccp3_bias', 'sha-cccp4_weight', 'sha-cccp4_bias', 'sha-conv3_weight', 'sha-conv3_bias', 'sha-cccp5_weight', 'sha-cccp5_bias', 'sha-cccp6_weight', 'sha-cccp6_bias', 'sha-conv4_1024_weight', 'sha-conv4_1024_bias']
    >>> sym2.attr_dict()
    {'sha-cccp3': {'no_bias': 'False', 'kernel': '(1,1)', 'num_group': '1', 'dilate': '(1,1)', 'num_filter': '256', 'stride': '(1,1)', 'cudnn_off': 'False', 'pad': '(0,0)', 'workspace': '1024', 'cudnn_tune': 'off'}, 'sha-cccp2': {'no_bias': 'False', 'kernel': '(1,1)', 'num_group': '1', 'dilate': '(1,1)', 'num_filter': '96', 'stride': '(1,1)', 'cudnn_off': 'False', 'pad': '(0,0)', 'workspace': '1024', 'cudnn_tune': 'off'}, 'sha-drop': {'p': '0.5'}, 'sha-conv2': {'no_bias': 'False', 'kernel': '(5,5)', 'num_group': '1', 'dilate': '(1,1)', 'num_filter': '256', 'stri
    # 示意一下就可

    这样看上去问题被解决了。

    Solution

    于是我们的答案就是:

    import mxnet as mx
    
    M,N=3,3
    num_filter=1
    kernel=mx.nd.array([ [1,2,3],[1,2,3],[1,2,3] ])
    
    
    d=mx.sym.Variable('data')
    
    conv1=mx.sym.Convolution(data=d,kernel=(3,3),num_filter=num_filter,no_bias=True,name='conv1')
    loss=mx.sym.MakeLoss(data=conv1)
    bch_kernel=kernel.reshape((1,1,M,N))
    arg_params={'conv1_weight': bch_kernel}
    
    def shareParams(sym,params):
        sym1 = sym.__copy__()
        new_params= {}
        ss=sym1.__getstate__()['handle']
        ss1=ss.replace('"name": "','"name": "sha-')
        h={'handle':ss1}
        sym1.__setstate__(h)
        for i in params:
            new_params['sha-'+i] = params[i]
            new_params[i] = params[i]
        return mx.sym.Group([sym,sym1]),new_params
    
    
    sym,params = shareParams(loss,arg_params)
    
    mod=mx.mod.Module(symbol=sym,data_names=('data','sha-data',))
    mod.bind(data_shapes=[ ('data',[1,1,M,N]), ('sha-data',[1,1,M,N]),])
    
    mod.init_params()   
    mod.set_params(arg_params=params, aux_params=[],allow_missing=True)    
    mod.init_optimizer()  
    
    
    mod.forward(mx.io.DataBatch([bch_kernel,bch_kernel],[]))
    mod.get_outputs()[0].asnumpy() 
    #array([[[[ 42.]]]], dtype=float32)
    mod.get_outputs()[1].asnumpy() 
    #array([[[[ 42.]]]], dtype=float32)
    mod.backward()
    mod.update()
    
    
    mod.forward(mx.io.DataBatch([bch_kernel,bch_kernel],[]))
    mod.get_outputs()[0].asnumpy() 
    #array([[[[ 41.57999802]]]], dtype=float32)
    mod.get_outputs()[1].asnumpy() 
    #array([[[[ 41.57999802]]]], dtype=float32)

    搞定 :)


    22 Jul, 2017 记
    关于这个问题,我后面还曾设想找段空闲时期,试着用mxnet内部机制进行封装。最近发现,自己也是傻得可以。。。
    两张图先进行batch维的拼接,通过所需段后再拆分 (⊙﹏⊙)b

  • 相关阅读:
    打开网页总结
    学期总结
    总结
    Sprint3
    Sprint2团队贡献分
    6.14
    典型用户与场景
    5种创建型模式
    JAVA 将接口的引用指向实现类的对象
    Java里的接口
  • 原文地址:https://www.cnblogs.com/jukan/p/10209807.html
Copyright © 2020-2023  润新知