自定义层 讨论区

http://zh.diveintodeeplearning.org/chapter_deep-learning-computation/custom-layer.html

Gluon中Loss类和Block类有什么具体的区别吗?
Loss中如果有params的话,可以被更新吗?

目前Loss类是直接继承HybridBlock类的,所以理论上说可以有param,并且因为在图里所以参数是可以被更新的
什么loss需要parameter?

我最近在用gluon实现center loss (https://github.com/ydwen/caffe-face ),在loss中会需要更新所有center的,所以想make sure一下

如果这么做的话有一点需要记得的是loss的parameter必须传给trainer,或者要手动更新,目前的例子里一般只包含network的parameter

如果需要的是记录center的话更推荐的是把loss做成没有state的,然后把center存在一个array里在training loop里自己更新

好的 谢谢建议 我试试。

另外,在写自己的Loss的时候,对hybrid_forward的中这些参数,如F, output, label, sample_weight这样的格式是固定的吗?sample_weight是怎样的输入呢?

自定义层的参数初始化的两种方式的疑惑(是否应该给net.initialize()增加force_reinit的“选项”?):

方式1:直接在__init__时初始化

class MyDense(nn.Block):
    def __init__(self, units, in_units, **kwargs):
        super(MyDense, self).__init__(**kwargs)
        with self.name_scope():
            self.weight = self.params.get('weight', shape=(in_units, units))
            self.bias = self.params.get('bias', shape=(units,)) 
        self.params.initialize(init.One(), force_reinit=True)

    def forward(self, x):
        linear = nd.dot(x, self.weight.data()) + self.bias.data()
        return nd.relu(linear)

dense= MyDense(5, in_units=10, prefix='o_my_dense_')
但这样如果我们需再强制恢复为默认初始化时,如果采用dense.initialize()会有警告—>可以改为dense.params.initialize(force_reinit=True)时警告消失(但我们无法在dense.initialize()里面加force_reinit=True

方式2:在外面进行初始化(去掉self.params.initialize(init.One(), force_reinit=True)),每次有需要时,再通过dense.params.initialize(init.One(), force_reinit=True)进行初始化。

还是说:覆盖block的initialize函数?

我用了你的方式试了一下:方式一:如果你在里面都已经初始化好了,那么就不需要进行net.initialize了,因为net.initialize就是对参数进行初始化的。如果你想在外面另行初始化,net.initialize方法里面暂时没有force_reinit=True这个参数,使用dense.params.initialize(force_reinit=True)这个方式是可以的。

方式2,如果去掉block里面的self.params.initialize方法,就没必要使用dense.params.initialize(force_reinit=True)这个了,可以直接使用net.initialize来进行初始化(因为只有这个地方会进行初始化,也就不需要force_reinit=True了)。当然使用dense.params.initialize(force_reinit=True)这个也是没有任何问题的。

不过我觉得教程里面在block内部使用初始化的目的应该就是构建自定义的层(专用),这样就不需要在外部重新初始化了,而且每个block都有自己专门的用处。这是我的理解,有什么问题可以交流一下

2赞

恩,就是演示一种偷懒的写法。

以下的讨论是基于:
MXNet 版本: mxnet-cu80, 0.12.1b20171104
操作系统: ubuntu 16.04

“自定义层初始化” 与 “原有层初始化” 对比, 原有的Dense初始化后, 为什么最后还需要 net.initialize() 初始化?

自定义的Dense (已经初始化)

class MyDense(nn.Block):
    def __init__(self, units, in_units, **kwargs):
        super(MyDense, self).__init__(**kwargs)
        with self.name_scope():
            self.weight = self.params.get('weight', shape=(in_units, units))
            self.bias = self.params.get('bias', shape=(units,)) 
        self.params.initialize(init.One(), force_reinit=True)

    def forward(self, x):
        linear = nd.dot(x, self.weight.data()) + self.bias.data()
        return nd.relu(linear)

搭建的网络无需重新初始化

net = nn.Sequential()
with net.name_scope():
    net.add(MyDense(32, in_units=64))
    net.add(MyDense(2, in_units=32))
    
#net.initialize()
data = nd.arange(128).reshape((2,64))
net(data)

原有的Dense初始化后, 为什么最后还需要 net.initialize() 初始化? 不然会报错

net = nn.Sequential()
with net.name_scope():
    net.add(nn.Dense(32, in_units=64, activation='relu', weight_initializer = init.One()))
    net.add(nn.Dense(2, in_units=32, weight_initializer = init.One()))

net.initialize()
data = nd.arange(128).reshape((2,64))
net(data)

你说的原有的Dense初始化应该是指这一句吧nn.Dense(32, in_units=64, activation='relu', weight_initializer = init.One()),这一句只是指定参数的初始话方式,并没有完成初始化,你需要net.initialize()才是真正完成了初始化操作

2赞

如果你写了一个python的自定义层然后觉得需要优化性能如何翻译到cpp的指令?

自定义层参数初始化函数设置

class MyDense(gluon.nn.Block):
def __init__(self,units,in_units,**kwargs):
    super(MyDense,self).__init__(**kwargs)
    with self.name_scope():
        self.weight = self.params.get('weight_1', shape = (in_units,units), init = init.One())
        self.bias = self.params.get('bias_1',shape = (units), init = init.Zero())

def forward(self, x):
    print(x.shape)
    print(self.weight.data().shape)
    print(self.bias.data().shape)

    return nd.dot(x,self.weight.data())+self.bias.data()

看了Dense的实现,不是很懂…它首先定义了in_unit=0,然后也没看到其他的地方来改变它?看HybridBlock里面有个infer_shape()似乎起到了修改的作用,但是没看到Dense里面对它进行调用啊?求解答:joy:

如何将带可训练参数的自定义层 进行Hybridiez?还是无法在python层实现 只能从C++层面实现?

是里面的intiailizer在真正的起作用

python可以的。需要继承 HybridBlock

不是很懂…自定义Dense的时候为什么不可以使用延迟初始化呢?initialize的操作不是都有吗?:sweat_smile:

1赞

自己定义延迟初始化层稍微麻烦点,建议看下gluon源代码。需要hybridize,以及intializer延后调用