在分类任务中,有几个常用的损失函数,包括NLLLoss, CrossEntropy
以及BCELosss
,内容比较基础, 这里以pytorch的函数为例,回顾下细节和使用方法作为记录。
NLLLoss
NLLLoss其实就是负对数似然损失(Negative Log Likelihood Loss),直接将最大化似然取负数,就是最小化损失了。取对数是将为了方便累加,一般用在多分类问题上,定义如下: \[ nll(x, y)= -logx[y] \] 其中, x为输入的向量,维度为类别数目,可以理解为每一个类别的score,y为真实类别的标量。
举例,如果 \(x=[0.1, 0.3, 0.5], y=1\) ,含义就是一共有3个类别,每一类的预测概率或者分数为0.1,0.3,0.5(这里未归一化), 真实标签为1, 那么nll(x,y)=-log0.3.
不过在pytorch的实现中,并没有log,只有一个负号,如下:
1 | import torch |
所以要想真正的使用NLLLoss的用在分类的话, 需要提前对输入取softmax和log,这样才是负对数似然损失。(但这其实就是cross_entropy了)
CrossEntropy Loss
CrossEntropyLoss交叉熵损失函数应该是在分类任务中出现频次最多的损失函数了,其实就是上述NLLLoss的完整版,可以直接用在分类任务中。
即:对于输入x向量,首先进行softmax操作,得到归一化的每一类的概率,之后进行log操作,最后执行NLLLoss,也就是说,
CE就是 log_softmax 与 nll_loss的结合得到的损失函数,如下:
1 | import torch |
所以分类直接用 CrossEntropy Loss, 二分类多分类都可以
这里多说一句,交叉熵的具体计算如下: \[ CE = -\sum_xp(x)\log q(x) \] 其中,p(x)为标签的真实分布,一般为one-hot向量,q(x)为模型预测预测的标签分布,因此这里可以理解为已经经过了softmax操作。
既然p的值只有真实标签的index的地方为1,其余都是0,因此0的地方就不用管了,只需要考虑真实标签的位置的值就行,也就是: \[ CE = - log(q_*) \] 其中,\(q_*\) 就是真实类别处的值,这就是上面的NLLLoss的形式(x[y])。所以CE就是logsoftmax + nllloss即可。
BCELoss
BCELoss可以理解为二分类的CE,就 Binary Cross Entropy Loss, 也就是上面的Cross Entropy中的标签只有两个值0或者1,正负样本。
这样展开求和其实就是: \[ BCE = -[ylogx + (1-y)log(1-x)] \] 所以在使用BCELoss之前,一般将x的值都计算到0-1之间(一般使用sigmoid),即当前数据的标签为正样本的概率,另一类负样本的概率就是1-x了。这个其实就是逻辑回归的损失函数了。
举个例子:
1 | import torch |
以第一条为例,手动计算就是:\(1*log0.3 + 0*log0.7=-1.2040\) 也就是损失函数。
另外torch中另一个相关的 损失函数是BCEWithLogitsLoss
,这个其实就是sigmoid+BCELoss 将sigmoid操作加进去了。
既然已经有了cross entropy, 为什么还要专门多一个binary的呢,我个人在多标签分类中用BCELoss比较多一些(Multi-label) 。
在多标签分类中,每一类其实都是一个二分类任务(即是否将其分为该类别),这种情况下,用BCELoss是最合适的。
举个例子:文本话题分类中,一句话可能有多个topic :
1 | import torch |
另外在计算损失函数的时候,有时候会有mask的需求,比如在文本生成中,句子长度不同,需要做padding,因此padding部分的loss是无效的,不能参加运算,因此,需要把mask部分去掉,一般使用 torch的masked_select
即可,举例如下:
1 | loss = torch.randn(2,3) |
总结一下的话,这三个分类loss之间关系很大,本质都是从似然函数得出的。其中NLLLoss是最基础,当然用的也较少。不如直接使用Cross Entropy,再到多标签分类的BinaryCE等。