1、交叉熵损失函数
交叉熵的公式有多种形式,一般写作
loss=−1n∑j=1nyjlnaj(∗∗)
loss=-\frac{1}{n}\sum_{j=1}^{n}y_jlna_j (**)
loss=−n1j=1∑nyjlnaj(∗∗)
lossj=−yjlnaj(1)
loss_j=-y_jlna_j (1)
lossj=−yjlnaj(1)
lossjloss_jlossj表示第j个样本的损失。aja_jaj表示softmax函数输出。yjy_jyj表示第j个样本的真实标签,为one-hot形式,所以上式可以变为
lossj=−yjlnexp(zj)∑exp(zm)=−yjzj+yjln∑exp(zm)=−zj+ln∑exp(zm)
loss_j=-y_jln\frac{exp(z_j)}{\sum{exp(z_m)}} \\=-y_jz_j+y_jln\sum{exp(z_m)}\\
=-z_j+ln\sum{exp(z_m)}
lossj=−yjln∑exp(zm)exp(zj)=−yjzj+yjln∑exp(zm)=−zj+ln∑exp(zm)
其中yjy_jyj选取1所在的位置,所以有另一种表示:
lossi=−x[class]+ln∑j=1nexp(x[j])(2)
loss_i=-x[class]+ln\sum_{j=1}^{n}exp(x[j]) (2)
lossi=−x[class]+lnj=1∑nexp(x[j])(2)
实际计算如下:
真实标签,label:[1, 0, 0]
预测标签,pred:[ [-0.7715, -0.6205, -0.2562]]
利用公式(2),
loss=−x[0]+log∑jexp(x[j])=0.7715+log[exp(−0.7715)+exp(−0.6205)+exp(−0.2562)]=1.344726
loss=-x[0]+log\sum_{j}exp(x[j])\\=0.7715+log[exp(-0.7715)+exp(-0.6205)+exp(-0.2562)]\\=1.344726
loss=−x[0]+logj∑exp(x[j])=0.7715+log[exp(−0.7715)+exp(−0.6205)+exp(−0.2562)]=1.344726
利用公式(1),
pred_softmax=softmax(pred)=[[0.2606,0.3031,0.4363]]pred_log=[[−1.3447,−1.1937,−0.8294]]res=label∗pred_log=[1,0,0]∗[[−1.3447,−1.1937,−0.8294]]=[[−1.3447,0,0]]=−1.3447
pred\_softmax=softmax(pred)=[[0.2606,0.3031,0.4363]] \\
pred\_log = [[-1.3447,-1.1937,-0.8294]]\\
res=label*pred\_log=[1,0,0]*[[-1.3447,-1.1937,-0.8294]]\\=[[-1.3447,0,0]]=-1.3447
pred_softmax=softmax(pred)=[[0.2606,0.3031,0.4363]]pred_log=[[−1.3447,−1.1937,−0.8294]]res=label∗pred_log=[1,0,0]∗[[−1.3447,−1.1937,−0.8294]]=[[−1.3447,0,0]]=−1.3447
所以有loss=−res=1.3447loss=-res=1.3447loss=−res=1.3447。可以看到,两种方法的计算结果相同。
2、Pytorch中的使用
Pytorch中使用交叉熵损失有三种方法,分别为nn.CrossEntropy、nn.NLLLoss、手动写。
y_pred = torch.tensor([[-0.7715, -0.6205, -0.2562], [-0.7627, -0.6284, -0.2547]]) # [B, num_labels]
y_label = torch.tensor([[1, 0, 0], [1, 0, 0]]) # [B, num_labels]
y_label_1 = torch.tensor([0, 0]) #
y_pred_softmax = nn.Softmax(dim=-1)(y_pred)
y_pred_log=torch.log(y_pred_softmax)
loss_1 = nn.NLLLoss()(y_pred_log, y_label_1)
print('NLLLoss计算出的loss:{}'.format(loss_1)) # 1.3406
loss_2 = nn.CrossEntropyLoss()(y_pred, y_label_1)
print('CrossEntropy计算出的loss:{}'.format(loss_2)) # 1.3406
loss_3 = -torch.mean(torch.sum(y_pred_log*y_label.float(), dim=-1)) # [-1.3447, -1.3365]
print('手动写计算出的loss:{}'.format(loss_3)) # 1.3406
由上面代码可以得到,loss_1=loss_2=loss_3。
上面计算了两个样本,loss_3在求-mean之前的结果为[-1.3447, -1.3365],1.3447即为在第一部分求得的loss结果,同样可以看出,最终的loss=1.3446,是由多个样本求-mean得到的,对应公式(**)。