在 MxNet 中没有开箱即用的加权 softmax 实现,但是为 MxNet 做出了很大贡献的同一个人开发了一个示例,该示例将加权 softmax 用于 2 个类(基本上,它是加权逻辑回归)。你可以在这里查看他们的实现 - https://github.com/apache/incubator-mxnet/blob/df558290930f9f3ed6941c306b5cc650505f7481/example/sparse/linear_classification/weighted_softmax_ce.py
该示例引入了一个新的自定义操作,它采用一个值 - 正类的权重。我已经根据您的用例对他们的代码进行了调整 - 传递一个权重数组而不是单个值。不幸的是,除了字符串之外,您不能将任何内容传递给自定义操作的构造函数。这就是为什么 class_weights_value 被定义为字符串并且在代码中转换为 nd.array 的原因。
这段代码不会像原来的 Softmax 那样快,它是用 C 语言以高度优化的方式编写的:
import mxnet as mx
class WeightedSoftmaxCrossEntropyLoss(mx.operator.CustomOp):
""" softmax cross entropy weighted loss, where the loss is adjusted by \
(class_weight) / sum_of_all_weights)
"""
def __init__(self, class_weights):
# parse initial weights from a string to separate items
self.class_weights = mx.nd.array([float(x) for x in class_weights.split(',')])
# scale weights, so they would add up to 1
self.class_scales = self.class_weights / len(self.class_weights)
def forward(self, is_train, req, in_data, out_data, aux):
"""Implements forward computation.
is_train : bool, whether forwarding for training or testing.
req : list of {'null', 'write', 'inplace', 'add'}, how to assign to out_data. 'null' means skip assignment, etc.
in_data : list of NDArray, input data.
out_data : list of NDArray, pre-allocated output buffers.
aux : list of NDArray, mutable auxiliary states. Usually not used.
"""
data = in_data[0]
label = in_data[1]
pred = mx.nd.SoftmaxOutput(data, label)
self.assign(out_data[0], req[0], pred)
def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
"""Implements backward computation
req : list of {'null', 'write', 'inplace', 'add'}, how to assign to in_grad
out_grad : list of NDArray, gradient w.r.t. output data.
in_grad : list of NDArray, gradient w.r.t. input data. This is the output buffer.
"""
label = in_data[1]
pred = out_data[0]
# move to GPU context if needed
class_scales = self.class_scales.as_in_context(label.context)
dx = pred - mx.nd.one_hot(label, len(class_scales))
# find a weight based on a label of an example
scale_factor = (class_scales[label]).reshape((pred.shape[0],1))
#apply scaling
rescaled_dx = scale_factor * dx
self.assign(in_grad[0], req[0], rescaled_dx)
@mx.operator.register("weighted_softmax_ce_loss")
class WeightedSoftmaxCrossEntropyLossProp(mx.operator.CustomOpProp):
def __init__(self, class_weights):
super(WeightedSoftmaxCrossEntropyLossProp, self).__init__(True)
self.class_weights = class_weights
def list_arguments(self):
return ['data', 'label']
def list_outputs(self):
return ['output']
def infer_shape(self, in_shapes):
"""Calculate output shapes from input shapes. This can be
omited if all your inputs and outputs have the same shape.
in_shapes : list of shape. Shape is described by a tuple of int.
"""
data_shape = in_shapes[0]
output_shape = data_shape
# return 3 lists representing inputs shapes, outputs shapes, and aux data shapes.
return (in_shapes), (output_shape,), ()
def create_operator(self, ctx, in_shapes, in_dtypes):
# create and return the CustomOp class.
return WeightedSoftmaxCrossEntropyLoss(self.class_weights)
要使用它,您需要创建一个使用此自定义操作而不是常规 SoftmaxOutput 的模型。这是模型代码:
import logging
import mxnet as mx
# it is important to import this file, even it is not directly used in the code below
import WeightedSoftmaxCrossEntropyLoss
batch_size = 100
# define a string of weights - one weight for each class, starting from the '0' class
class_weights_values = '5,1,1,1,1,1,1,1,1,1'
mnist = mx.test_utils.get_mnist()
train_iter = mx.io.NDArrayIter(mnist['train_data'], mnist['train_label'], batch_size, shuffle=True)
val_iter = mx.io.NDArrayIter(mnist['test_data'], mnist['test_label'], batch_size)
data = mx.sym.var('data')
label_names = mx.symbol.Variable("softmax_label")
data = mx.sym.flatten(data=data)
fc1 = mx.sym.FullyConnected(data=data, num_hidden=15)
act1 = mx.sym.Activation(data=fc1, act_type="relu")
fc2 = mx.sym.FullyConnected(data=act1, num_hidden=10) # MNIST has 10 classes
# Weighted Softmax
weighted_softmax = mx.sym.Custom(fc2, label_names, op_type='weighted_softmax_ce_loss', class_weights=class_weights_values, name='out')
# Making sure it is used as a loss funciton
mlp = mx.sym.MakeLoss(weighted_softmax)
logging.getLogger().setLevel(logging.DEBUG) # logging to stdout
# create a trainable module on CPU
mlp_model = mx.mod.Module(symbol=mlp, context=mx.cpu(), label_names=['softmax_label'])
mlp_model.fit(train_iter, # train data
eval_data=val_iter, # validation data
optimizer='sgd', # use SGD to train
optimizer_params={'learning_rate':0.1}, # use fixed learning rate
eval_metric='acc', # report accuracy during training
batch_end_callback = mx.callback.Speedometer(batch_size, 100), # output progress for each 100 data batches
num_epoch=1) # train for at most 10 dataset passes
我希望这对你有用。我没有尝试使用培训的结果,也许它没有像你期望的那样工作。因此,请随时根据需要调整此代码。