Теперь вы можете задаться вопросом о разнице с SoftmaxOutput, которая не требует этой группировки. Это немного сбивает с толку, но SoftmaxOutput вычисляет выходной сигнал в прямом проходе и потерю в обратном проходе: он избавляет вас от необходимости делать этот трюк группировки, но делает его более хитрым, чтобы получить потерю.

у заменить mx.symbol.SoftmaxOutput на взвешенную версию (назначить другой вес по отношению к частоте метки во всем наборе данных)

Оригинальная функция работает хорошо, как показано ниже:

cls_prob = mx.symbol.SoftmaxOutput(data=data,
                                   label=label,
                                   multi_output=True,
                                   normalization='valid',
                                   use_ignore=True, 
                                   ignore_label=-1,
                                   name='cls_prob')

Текущий код я написал как ниже. Код может работать без ошибок, но потери быстро взрываются до нан. Я имею дело с проблемой обнаружения, потеря RCNNL1 быстро превращается в нан, когда я использую свой код в качестве CustomOp. Другое дело, что я должен игнорировать метку -1, и я не уверен, как это сделать правильно. Любая помощь будет оценена.

import mxnet as mx
import numpy as np

class WeightedSoftmaxCrossEntropyLoss(mx.operator.CustomOp):
    def __init__(self, num_class):
        self.num_class = int(num_class)

    def forward(self, is_train, req, in_data, out_data, aux):

        data = in_data[0]
        label = in_data[1]
        pred = mx.nd.SoftmaxOutput(data, label, multi_output=True,
                               normalization='valid', use_ignore=True, ignore_label=-1,
                               name='rcnn_cls_prob')

        self.assign(out_data[0], req[0], pred)

    def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
        cls_weight = np.array([
            0.002852781814876101, 
            0.30715984513157385, 
            1.0932468996115976, 
            1.1598757152765971, 
            0.20739109264009636, 
            1.1984256112776808, 
            0.18746186040248036, 
            2.9009928470737023, 
            0.92140970338602113, 
            1.200317380251021
        ])
        label = in_data[1]
        pred = out_data[0]
        label = label.asnumpy().astype('int32').reshape((-1))
        pred = pred.asnumpy().reshape((pred.shape[0], pred.shape[1], -1)).transpose((0, 2, 1))
        pred = pred.reshape((label.shape[0], -1))

        # Need to ignore label (how)
        out_inds = np.where(label == -1)[0]
        #label = label[keep_inds]
        one_hot = np.zeros((label.shape[0], self.num_class))
        one_hot[np.arange(label.shape[0]), label] = 1
        # gradient
        dx = pred - one_hot
        #dx[out_inds] = 0.0
        weighted_dx = cls_weight * dx / 4
        self.assign(in_grad[0], req[0], weighted_dx)

@mx.operator.register("weighted_softmax_ce_loss")
class WeightedSoftmaxCrossEntropyLossProp(mx.operator.CustomOpProp):
    def __init__(self, num_class):
        super(WeightedSoftmaxCrossEntropyLossProp, self).__init__(need_top_grad=False)
        self.num_class = num_class

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

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

    def infer_shape(self, in_shapes):
        data_shape = in_shapes[0]
        label_shape = (in_shapes[0][0],)
        output_shape = in_shapes[0]
        return [data_shape, label_shape], [output_shape], []

    def create_operator(self, ctx, in_shapes, in_dtypes):
        #  create and return the CustomOp class.
        `enter code here`return WeightedSoftmaxCrossEntropyLoss(self.num_class)

Ответы на вопрос(1)

Решение Вопроса

что использование customop здесь будет лучшим, так как оно может быть медленным. Поскольку SoftmaxOuput вычисляет градиент в обратном проходе, неуместно умножать потери, как вы хотите. Однако это не так уж сложно сделать с символическим API. Я приложил пример игрушки, надеюсь, это поможет.

import mxnet as mx
import numpy as np
import logging

# learn floor function from random numbers in [-1, -1 + num_classes]
n = 10000
batch_size = 128
num_classes = 10
x = (np.random.random((n,)) * num_classes) - 1
y = np.floor(x)
print(x[:2])
print(y[:2])

# define graph
data = mx.symbol.Variable('data')
label = mx.symbol.Variable('label')
class_weights = mx.symbol.Variable('class_weights')
fc = mx.sym.FullyConnected(data=data, num_hidden=num_classes)
fc = mx.sym.Activation(data=fc, act_type='relu')
proba = mx.sym.FullyConnected(data=fc, num_hidden=num_classes)
proba = mx.sym.softmax(proba)

# multipy cross entropy loss by weight
cross_entropy = -mx.sym.pick(proba, label) * mx.sym.pick(class_weights, label)

# mask the loss to zero when label is -1
mask = mx.sym.broadcast_not_equal(label, mx.sym.ones_like(label) * -1)
cross_entropy = cross_entropy * mask

# fit module
class_weights = np.array([np.arange(1, 1 + num_classes)]*n) 
data_iter = mx.io.NDArrayIter(data={'data': x, 'class_weights': class_weights}, label={'label': y}, batch_size=batch_size)
mod = mx.mod.Module(
    mx.sym.Group([mx.sym.MakeLoss(cross_entropy, name='ce_loss'), mx.sym.BlockGrad(proba)]),
    data_names=[v.name for v in data_iter.provide_data],
    label_names=[v.name for v in data_iter.provide_label]
)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
mod.bind(data_shapes=data_iter.provide_data, label_shapes=data_iter.provide_label)
mod.init_params()
mod.fit(
    data_iter, 
    num_epoch=200, 
    optimizer=mx.optimizer.Adam(learning_rate=0.01, rescale_grad=1.0/batch_size),
    batch_end_callback=mx.callback.Speedometer(batch_size, 200), 
    eval_metric=mx.metric.Loss(name="loss", output_names=["ce_loss_output"]))

# show result, -1 are not predicted correctly as we did not compute their loss
probas = mod.predict(data_iter)[1].asnumpy()
print(zip(x, np.argmax(probas, axis=1)))
 DanielTseng08 дек. 2017 г., 10:20
Спасибо, это мне очень помогает. Я попробую это, чтобы видеть, работает ли это. Еще один вопрос относительно того, что вы сказали, что пользовательская операция может быть медленной? Это потому, что использование asnumpy () для обмена памяти в процессор? Я изменил взвешенную потерю энтропии softmax в caffe, возможно ли перенести ее в mxnet? Спасибо !
 geoalgo08 дек. 2017 г., 22:04
Да. Что касается кафе, я не пытался портировать модели, может быть, вы можете попробовать для этого onnxaws.amazon.com/blogs/ai/announcing-the-availability-of-onnx-1-0
 geoalgo31 янв. 2018 г., 13:04
Теперь вы можете задаться вопросом о разнице с SoftmaxOutput, которая не требует этой группировки. Это немного сбивает с толку, но SoftmaxOutput вычисляет выходной сигнал в прямом проходе и потерю в обратном проходе: он избавляет вас от необходимости делать этот трюк группировки, но делает его более хитрым, чтобы получить потерю.
 geoalgo31 янв. 2018 г., 13:04
Группировка позволяет сообщить модулю, что несколько выходов необходимо вычислять при каждом прямом проходе. Это позволяет вам извлечь вероятности позже, когда вы вызываете 'mod.predict (data_iter) [1]', который делает прямой проход и извлекает второй элемент группы. BlockGrad требуется, так как не следует вычислять градиент из вероятностей. Вы должны сказать, что ничего не было сделано в обратном направлении для этого вывода, иначе, если я не ошибаюсь, mxnet просто минимизирует ваши вероятности :-)
 Miriam Farber31 янв. 2018 г., 00:52
@geoalgo Спасибо за отличный ответ! Не могли бы вы объяснить, как передача mx.sym.Group ([mx.sym.MakeLoss (cross_entropy, name = 'ce_loss'), mx.sym.BlockGrad (proba)]) в модуль позволяет понять, что первый элемент - это потеря а второй элемент это выход сети? Кроме того, почему вы блокируете градиенты по второму параметру?

Ваш ответ на вопрос