在分类问题中,Pytorch提供了多种函数,那么哪种函数是使用softmax;哪些函数不用使用softmax就可以直接套loss;又有哪些函数使用logsoftmax?这里面有一些细节需要我来研究,我找到了pytorch在github上面的这部分代码,以此文记录一下我的学习历程。
分类问题有两种思路:
第一种需要对标签进行处理
NLLLoss(_WeightedLoss)
输入
这个函数的作用适合于样本数量不均衡的情况下,其中_WeightedLoss的维度是(样本个数,类别个数),数值代表了给每个类别分配的权重。这个函数接收到输入后,会给出每个类别的对数概率(因而比较适合样本不均匀的条件)。(也就是说如果你使用NLLoss的话,你可以在forward部分直接在最后一个隐层设定为(类别维度),然后套上一个LogSoftmax层就好了。)
预测
对于predict阶段代码中也给出了方法,可以在网络的最后一层加入一个LogSoftmax,来给出输出
(当然作者也提到了,如果不想手工在最后一层加入LogSoftmax层,也可以使用CrossEntropyLoss来代替。)
标签
标签应该是一个index数组,其中index的范围是[0, C-1], 其中C为类别的数目
例子
m = nn.LogSoftmax(dim=1)
loss = nn.NLLLoss()
# input is of size N x C = 3 x 5
input = torch.randn(3, 5, requires_grad=True)
# each element in target has to have 0 <= value < C
target = torch.tensor([1, 0, 4])
output = loss(m(input), target)
output.backward()
# 2D loss example (used, for example, with image inputs)
N, C = 5, 4
loss = nn.NLLLoss()
# input is of size N x C x height x width
data = torch.randn(N, 16, 10, 10)
conv = nn.Conv2d(16, C, (3, 3))
m = nn.LogSoftmax(dim=1)
# each element in target has to have 0 <= value < C
target = torch.empty(N, 8, 8, dtype=torch.long).random_(0, C)
output = loss(m(conv(data)), target)
output.backward()
torch.nn.CrossEntropyLoss()
CrossEntropyLoss结合和nn.LogSoftmax和nn.NLLLoss两个函数的功能。
输入
CrossEntropyLoss和NLLLoss函数在输入维度部分是一致的,但是与NLLLoss不同的是,CrossEntropyLoss不需要在网络的最后一层加入LogSoftmax层。在源代码的注释里,官方也强调了“The input is expected to contain raw, unnormalized scores for each class.” 不要使用Softmax(这一点也好理解,因为softmax和对数似然loss求梯度之后正好是y-\hat{y})输入维度为(数据个数,数据维度)
预测
在预测的时候需要加入一个Softmax层
标签
CrossEntropyLoss和NLLLoss在标签上是一致的,给出一个index,index里面表明了分类。
loss = nn.CrossEntropyLoss()
input = torch.randn(3, 5, requires_grad=True)
target = torch.empty(3, dtype=torch.long).random_(5)
output = loss(input, target)
output.backward()
torch.nn.BCELoss
BCELoss的作用是计算二分类target和output之间的loss
标签
BCELoss的标签是 0和1之间的数值,维度为1
这里需要强调一下,BCELoss和NLLLoss比较像,需要在网络最后一层套上一个nn.Sigmoid层(和CrossEntropy不一样)
import torch
import torch.nn as nn
sig = nn.Sigmoid()
loss = nn.BCELoss()
input = torch.randn(3, requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(sig(input), target)
output.backward()
torch.nn.BCEWithLogitsLoss
这个层类似于CrossEntropyLoss,结合和Sigmoid层和BCELoss层,适用于二分类任务(但是相比于Sigmoid+BCELoss数值计算稳定性更好一些)
小结
最后想说一件事情,torch.nn.functional 下面的函数和torch.nn层一致困扰着我,因为他们好像是某种角度看是类似的。在研究代码之后,的确是这样的,torch.nn下的包实现的时候都是在调用torch.nn.functional下面的函数。也算有一个小收获吧!