神经网络中多类、多标签分类任务的损失函数是什么?

机器算法验证 神经网络 Python 损失函数 喀拉斯 交叉熵
2022-01-24 18:49:48

我正在训练一个神经网络来将一组对象分类为 n 类。每个对象可以同时属于多个类(多类,多标签)。

我读到对于多类问题,通常建议使用 softmax 和分类交叉熵作为损失函数而不是 mse,我或多或少地理解为什么。

对于我的多标签问题,当然使用 softmax 是没有意义的,因为每个类概率应该相互独立。所以我的最后一层只是 sigmoid 单元,它们将它们的输入压缩到每个类的概率范围 0..1。

现在我不确定我应该为此使用什么损失函数。查看分类交叉熵的定义,我认为它不适用于这个问题,因为它只会考虑应该为 1 的神经元的输出而忽略其他神经元。

二元交叉熵听起来更合适,但我只看到它用于单个输出神经元的二元分类问题。

我正在使用 python 和 keras 进行培训,以防万一。

4个回答

如果您使用的是 keras,只需将 sigmoids 放在您的输出层上,并将 binary_crossentropy 放在您的成本函数上。

如果您使用的是 tensorflow,则可以使用sigmoid_cross_entropy_with_logits但就我而言,这种直接损失函数并没有收敛。所以我最终使用了显式 sigmoid 交叉熵损失您可以在此示例中制作自己的(yln(sigmoid(logits))+(1y)ln(1sigmoid(logits)))

Sigmoid 与 softmax 不同,它不给出周围的概率分布作为输出,而是给出独立的概率。nclasses

如果平均而言,为任何行分配了较少的标签,那么您可以使用softmax_cross_entropy_with_logits,因为当类互斥时,由于这种损失,它们的概率不必如此。所需要的只是每一行标签都是一个有效的概率分布。如果不是,则梯度的计算将不正确。

更新(18/04/18):旧答案在我的模型上仍然有用。诀窍是分别对分区函数和分布进行建模,从而利用 softmax 的威力。

考虑你的观察向量y包含装有m标签。yim=δim (如果样本 i 包含标签 m,则为 1,否则为 0)。因此,目标是以每个样本的方式对矩阵进行建模。因此模型评估F(yi,xi)=logP(yi|xi). 考虑扩大yim=ZP(ym)实现两个属性:

  1. 分配功能:mP(ym)=1
  2. 分区功能:Z估计标签的数量

然后是分别对两者建模的问题。分布函数最好用softmax层建模,分区函数可以用线性单元建模(实际上我把它剪成max(0.01,output). 像泊松单位这样更复杂的建模可能会更好)。然后你可以选择应用分布式损失(KL 分布和 MSE 分区),或者你可以在他们的产品上尝试以下损失。

实际上,优化器的选择也有很大的不同。我对分解方法的经验是它在Adadelta下效果最好(Adagrad 不适合我,还没有尝试 RMSprop,SGD 的性能取决于参数)。

关于 sigmoid的旁注:我当然尝试过 sigmoid + crossentropy,但没有成功。该模型倾向于预测Z只是,未能捕捉到分布函数的变化。(又名,它在某种程度上对分区建模非常有用,背后可能有数学原因)

更新:(随机想法)似乎使用 Dirichlet 过程将允许在标签数量上加入一些先验?

更新:通过实验,修改后的 KL-divergence 仍然倾向于提供多类输出而不是多标签输出。


(旧答案)

我对 sigmoid 交叉熵的体验不是很愉快。目前我正在使用修改后的 KL-divergence。它采用以下形式

Loss(P,Q)=x|P(x)Q(x)||logP(x)Q(x)|=x|(P(x)Q(x))logP(x)Q(x)|
在哪里P(x)是目标伪分布和Q(x)是预测的伪分布(但函数实际上是对称的,所以实际上并不重要)

它们被称为伪分布,因为它们没有被归一化。所以你可以拥有xP(x)=2如果您有 2 个特定样品的标签。

Keras 执行

def abs_KL_div(y_true, y_pred):
    y_true = K.clip(y_true, K.epsilon(), None)
    y_pred = K.clip(y_pred, K.epsilon(), None)
    return K.sum( K.abs( (y_true- y_pred) * (K.log(y_true / y_pred))), axis=-1)

我遇到了同样的问题,经过一些研究,这是我的解决方案:

如果您使用的是张量流:

多标签损失:

    cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=logits, labels=tf.cast(targets,tf.float32))

    loss       = tf.reduce_mean(tf.reduce_sum(cross_entropy, axis=1))

    prediction = tf.sigmoid(logits)
    output     = tf.cast(self.prediction > threshold, tf.int32)
    train_op   = tf.train.AdamOptimizer(0.001).minimize(loss)

解释 :

例如,如果来自模型和标签的 Logits 是:

logits = array([[ 1.4397182 , -0.7993438 ,  4.113389  ,  3.2199187 ,  4.5777845 ],
       [ 0.30619335,  0.10168511,  4.253479  ,  2.3782277 ,  4.7390924 ],
       [ 1.124632  ,  1.6056736 ,  2.9778094 ,  2.0808482 ,  2.0735667 ],
       [ 0.7051575 , -0.10341895,  4.990803  ,  3.7019827 ,  3.8265839 ],
       [ 0.6333333 , -0.76601076,  3.2255085 ,  2.7842572 ,  5.3817415 ]],
      dtype=float32)

labels = array([[1, 1, 0, 0, 0],
       [0, 1, 0, 0, 1],
       [1, 1, 1, 1, 0],
       [0, 0, 1, 0, 1],
       [1, 1, 1, 1, 1]])

then

cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=logits, labels=tf.cast(targets,tf.float32))

will give you :

[[0.21268466 1.170648   4.129609   3.2590992  4.58801   ]
 [0.85791767 0.64359653 4.2675934  2.466893   0.00870855]
 [0.28124034 0.18294993 0.04965096 0.11762683 2.1920042 ]
 [1.1066352  0.64277405 0.00677719 3.7263577  0.02155003]
 [0.42580318 1.147773   0.03896642 0.059942   0.00458926]]

and

prediction = tf.cast(tf.sigmoid(one_placeholder) > 0.5, tf.int32) 

will give you :  

[[1 0 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 0 1 1 1]
 [1 0 1 1 1]]

现在您已经有了预测标签和真实标签,您可以轻松计算准确度。

对于多类:

标签必须是 one-hot 编码

cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels = one_hot_y)
loss = tf.reduce_sum(cross_entropy)

optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(loss)

predictions = tf.argmax(logits, axis=1, output_type=tf.int32, name='predictions')
accuracy = tf.reduce_sum(tf.cast(tf.equal(predictions, true_labels), tf.float32))

另一个例子

# LOSS AND OPTIMIZER
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=output, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate,
                                   beta1=0.9,
                                   beta2=0.999,
                                   epsilon=1e-08).minimize(loss, global_step=global_step)


# PREDICTION AND ACCURACY CALCULATION
correct_prediction = tf.equal(y_pred_cls, tf.argmax(y, axis=1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

我还没用过keras。以 caffe 为例,可以SigmoidCrossEntropyLossLayer用于多标签问题。