torch.nn.LogSoftmax
model = nn.Linear(5, 2)
x = torch.randn(5, 5)
y = model(x)
y
Out[81]:
tensor([[ 0.1358, -0.7312],
[-0.1503, -0.2752],
[-0.3823, -0.2795],
[-1.2358, -0.0971],
[-0.9683, 0.6525]], grad_fn=<AddmmBackward>)
# 用torch得到的结果
nn.LogSoftmax()(y)
Out[82]:
tensor([[-0.3508, -1.2178],
[-0.6326, -0.7575],
[-0.7458, -0.6431],
[-1.4164, -0.2778],
[-1.8012, -0.1804]], grad_fn=<LogSoftmaxBackward>)
# 下面我们自己手动实现LogSoftmax
y_exp = y.exp()
y_exp
Out[85]:
tensor([[1.1455, 0.4814],
[0.8604, 0.7594],
[0.6823, 0.7562],
[0.2906, 0.9074],
[0.3797, 1.9203]], grad_fn=<ExpBackward>)
lsm = torch.log(y_exp / y_exp.sum(-1).unsqueeze(1))
lsm
Out[95]:
tensor([[-0.3508, -1.2178],
[-0.6326, -0.7575],
[-0.7458, -0.6431],
[-1.4164, -0.2778],
[-1.8012, -0.1804]], grad_fn=<LogBackward>)
# 可以发现,得到的结果和上面一样
由上可以验证公式 L o g S o f t m a x ( x i ) = l o g ( e x p ( x i ) ∑ j e x p ( x j ) ) LogSoftmax(x_i)=log(\frac{exp(x_i)}{\sum_jexp(x_j)}) LogSoftmax(xi)=log(∑jexp(xj)exp(xi))
torch.nn.NLLLoss
# 接上面的代码
# 先定义类别标签
target = torch.randint(0, 2, (5,))
target
Out[110]: tensor([0, 1, 1, 0, 1])
# 用torch封装的函数得到的结果
nlll = torch.nn.NLLLoss()(lsm, target)
nlll
Out[112]: tensor(0.6697, grad_fn=<NllLossBackward>)
# 下面我们自己手动实现NLLLoss(Negative Log Likely Loss)
sum = 0
for i in range(5):
...: sum += -lsm[i, target[i]]
nlll = sum / target.size(0)
nlll
Out[124]: tensor(0.6697, grad_fn=<DivBackward0>)
以上可以验证,torch.nn.NLLLoss是把每个样本对应的真实类别的概率取log之后取平均。
torch.nn.CrossEntropyLoss
torch.nn.CrossEntropyLoss()(y, target)
Out[130]: tensor(0.6697, grad_fn=<NllLossBackward>)
# 与之前得到的loss一致
以上可以验证,nn.CrossEntropyLoss = nn.LogSoftmax + nn.NLLLoss
想说点什么呢
就是想说,网络得到的输出最后一层不需要加softmax就可以直接放进CrossEntropyLoss计算loss。
这两天写了个baseline,结果训练之后的分类结果总是奇迹般的得到一个类别的输出(就是一个10分类,所有的样本都预测为第0类)。
花了两天的时间,得到了这篇博客。