定义新层的文档读不懂【求助】


很开心找到一个题目和我的问题对应的文档,但是真是读不懂。

求助,救救孩子!

import os
import mxnet as mx
import numpy as np

class Softmax(mx.operator.CustomOp):
def forward(self, is_train, req, in_data, out_data, aux):
x = in_data[0].asnumpy()
y = np.exp(x - x.max(axis=1).reshape((x.shape[0], 1)))
y /= y.sum(axis=1).reshape((x.shape[0], 1))
self.assign(out_data[0], req[0], mx.nd.array(y))

这里面的0和1都是什么啊?对应关系有地方介绍么?
看起来indata[0]就是输入了?那这个输入是什么维度啊?包不包括batchsize啊?有没有indata[100]啊?
req又是啥?req【0】?为啥是0啊,1又是啥?到几结束啊?
out_data[0],又是0?为什么啊

更可怕的是bp:
def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
l = in_data[1].asnumpy().ravel().astype(np.int)
y = out_data[0].asnumpy()
y[np.arange(l.shape[0]), l] -= 1.0
self.assign(in_grad[0], req[0], mx.nd.array(y))
l是loss吗?还是label啊?为啥又是indata[1]了?1又是啥啊
为啥又要把y计算一下放到in_grad[0]啊?不放到[out_grad]么

在infer shape里面,
def infer_shape(self, in_shape):
data_shape = in_shape[0]
label_shape = (in_shape[0][0],)
output_shape = in_shape[0]
return [data_shape, label_shape], [output_shape], []

这一堆0,1index都是啥啊

小白刚要入坑,发现坑深,要摔死了啊,求助

刚入坑可以先考虑gluon Block定义Layer吧, CustomOp还要写backward感觉好麻烦:thinking:

bp都算好了,就剩代码实现了啊

其实用sym也不一定要定义层,其实更确切的说这是定义算子。一般的网络用现有的算子基本都可以实现。

看了下文档, 对照着另一个工程, 感觉还是可以理解的

定义一个operator参数相关的定义要在CustomOpProp实现:

@mx.operator.register("centerloss")
class CenterLossProp(mx.operator.CustomOpProp):
    def __init__(self, num_class, alpha, scale=1.0, batchsize=64):
        super(CenterLossProp, self).__init__(need_top_grad=False)
        ...
        ...

    def list_arguments(self):
        return ['data', 'label']

    def list_outputs(self):
        return ['output']

    def list_auxiliary_states(self):
        # call them 'bias' for zero initialization
        return ['diff_bias', 'center_bias', 'sum_bias']

这里三个list函数, 定义了CustomOp需要的参数. 在CustomOp forward里的索引是和他们是对应的. 比如:

class CenterLoss(mx.operator.CustomOp):
    def __init__(self, ctx, shapes, dtypes, num_class, alpha, scale=1.0):
         ...

    def forward(self, is_train, req, in_data, out_data, aux):
        labels = in_data[1].asnumpy()
        diff = aux[0]
        center = aux[1]
        ...
        ...
        self.assign(out_data[0], req[0], loss)

    def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
        diff = aux[0]
        center = aux[1]
        sum_ = aux[2]

        # back grad is just scale * ( x_i - c_yi)
        grad_scale = float(self.scale/self.batch_size)
        self.assign(in_grad[0], req[0], diff * grad_scale)

        # update the center
        ...
        ...

这两个类实现之后, 在写网络的时候根据注册的名字来使用这个op, op_type='centerloss':

center_loss_ = mx.symbol.Custom(data=embedding, label=center_label, name='center_loss_', op_type='centerloss',
                                    num_class=10, alpha=0.5, scale=1.0, batchsize=batchsize)
center_loss = mx.symbol.MakeLoss(name='center_loss', data=center_loss_)

上边这些代码来自这:

如果你的层不是特别复杂, 还是推荐用mx.sym中已经实现的op进行组合, 这样效率会高很多.

1赞

感谢!这样一说要明白多了。

还有一点没看明白,我研究一下这个centerloss的代码,有问题再请教~大神偶尔关注下谢啦!