在讨论这一个问题之前,先看一个很有意思的事实:
现在我们有这么一张图:
那么二分类问题就可以是,这张图里有没有房子。答案只能是有(1)或者无(0),共两类。
而多标签分类问题就相当于有多个二分类问题,比如这张图里是否有房子、有树、有山、有云朵。对于一张特定的图,这些标签可能都具备,也可能都不具备,那么这就叫多标签分类(注意与多分类区分)。
先看数学表达:
loss ( o , t ) = − 1 / n ∑ i ( t [ i ] ∗ log ( o [ i ] ) + ( 1 − t [ i ] ) ∗ log ( 1 − o [ i ] ) ) \operatorname{loss}(o, t)=-1 / n \sum_{i}(t[i] * \log (o[i])+(1-t[i]) * \log (1-o[i])) loss(o,t)=−1/ni∑(t[i]∗log(o[i])+(1−t[i])∗log(1−o[i]))
t就是target,目标值,需要在[0, 1]之间(不过作为标签其一般也只有0、1这两个值),而o就是模型的预测值,在计算loss之前需要经过sigmoid函数进行处理。
这个公式其实就是交叉熵变形而来的,由于二分类任务的特别性,因此可以直接用 1 − t [ i ] 1-t[i] 1−t[i]和 1 − o [ i ] 1-o[i] 1−o[i]来代替其他的所有可能,二元交叉熵就是这么个意思。计算每一个“预测-目标”对的交叉熵,最后取平均。
这里我们先假设t和o均为:
[
[0, 1, 1],
[1, 1, 1],
[0, 0, 0]
]
o由于需要经过sigmoid处理,处理后的结果为:
[
[0.5000, 0.7311, 0.7311],
[0.7311, 0.7311, 0.7311],
[0.5000, 0.5000, 0.5000]
]
相应代码如下:
import torch
from torch.autograd import Variable
import torch.nn as nn
sig = nn.Sigmoid()
pred = torch.Tensor([[0, 1, 1],
[1, 1, 1],
[0, 0, 0]])
pred = Variable(pred, requires_grad=True)
pred = sig(pred)
print(pred)
label = torch.Tensor([[0, 1, 1],
[1, 1, 1],
[0, 0, 0]])
loss = nn.BCELoss()
print(loss(pred, label))
计算结果为0.4821。可以发现,哪怕预测值和标签值完全相同,相应的BCELoss值也不会为0,这是由公式的数学特性决定的。
我们可以对照公式自己验证一遍:
import math
r11 = 0 * math.log(0.5) + (1-0) * math.log((1 - 0.5))
r12 = 1 * math.log(0.7311) + (1-1) * math.log((1 - 0.7311))
r13 = 1 * math.log(0.7311) + (1-1) * math.log((1 - 0.7311))
r21 = 1 * math.log(0.7311) + (1-1) * math.log((1 - 0.7311))
r22 = 1 * math.log(0.7311) + (1-1) * math.log((1 - 0.7311))
r23 = 1 * math.log(0.7311) + (1-1) * math.log((1 - 0.7311))
r31 = 0 * math.log(0.5) + (1-0) * math.log((1 - 0.5))
r32 = 0 * math.log(0.5) + (1-0) * math.log((1 - 0.5))
r33 = 0 * math.log(0.5) + (1-0) * math.log((1 - 0.5))
r1 = -(r11 + r12 + r13) / 3
r2 = -(r21 + r22 + r23) / 3
r3 = -(r31 + r32 + r33) / 3
bceloss = (r1 + r2 + r3) / 3
print(bceloss)
输出为0.48206,四舍五入为0.4821,与直接调用loss函数的计算结果相同。
https://www.jianshu.com/p/ac3bec3dde3e