【8】pytorch中的损失函数

文章目录

      • 1.L1Loss
      • 2.MSELoss
      • 3.NLLoss
      • 4.CrossEntropyLoss
      • 5.KLDivLoss
      • 6.BCELoss

import torch
import torch.nn as nn
import numpy as np
import math
import torch.nn.functional as F

1.L1Loss

class torch.nn.L1Loss(size_average=None, reduce=None)

官方文档中仍有reduction='elementwise_mean’参数,但代码实现中已经删除该参数

功能: 计算output和target之差的绝对值,可选返回同维度的tensor或者是一个标量。

计算公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G4DKJIry-1619105260819)(attachment:image.png)]

参数: reduce(bool)- 返回值是否为标量,默认为True

size_average(bool)- 当reduce=True时有效。为True时,返回的loss为平均值;为False时,返回的各样本的loss之和。

# 设置预测值
output = torch.tensor([[2,4],[6,8]]).float()
# tensor([[2., 4.],
#        [6., 8.]])

# 设置标签值
target = torch.ones(2,2)
# tensor([[1., 1.],
#        [1., 1.]])

# 设置三种不同参数的L1Loss
# reduce=False返回一个非标量,
reduceFalse_averageTrue = nn.L1Loss(size_average=True, reduce=False)
loss1 = reduceFalse_averageTrue(output,target)

# reduce=True返回一个标量,并求平均
reduceTrue_averageTrue  = nn.L1Loss(size_average=True, reduce=True)
loss2 = reduceTrue_averageTrue(output,target)

# reduce=True返回一个标量,但不用求平均而是求和
reduceFalse_averageTrue = nn.L1Loss(size_average=False, reduce=True)
loss3 = reduceFalse_averageTrue(output,target)

loss1,loss2,loss3
# 输出
# loss1的值:输出一个数组而不是标量
# (tensor([[1., 3.],
#         [5., 7.]]),
# loss2的值:输出一个标量而且求平均
# tensor(4.),
# loss3的值:输出一个标量求全部loss的和
# tensor(16.))

# 一般来说使用第二种,也是默认的那种

2.MSELoss

class torch.nn.MSELoss(size_average=None, reduce=None)

官方文档中仍有reduction='elementwise_mean’参数,但代码实现中已经删除该参数

功能: 计算output和target之差的平方,可选返回同维度的tensor或者是一个标量。

计算公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-stSh3QOF-1619105260823)(attachment:image.png)]

参数: reduce(bool)- 返回值是否为标量,默认为True

size_average(bool)- 当reduce=True时有效。为True时,返回的loss为平均值;为False时,返回的各样本的loss之和。

# 设置预测值
output = torch.tensor([[2,4],[6,8]]).float()
# tensor([[2., 4.],
#        [6., 8.]])

# 设置标签值
target = torch.ones(2,2)
# tensor([[1., 1.],
#        [1., 1.]])

# 设置三种不同参数的MSELoss
# reduce=False返回一个非标量,
reduceFalse_averageTrue = nn.MSELoss(size_average=True, reduce=False)
loss1 = reduceFalse_averageTrue(output,target)

# reduce=True返回一个标量,并求平均
reduceTrue_averageTrue  = nn.MSELoss(size_average=True, reduce=True)
loss2 = reduceTrue_averageTrue(output,target)

# reduce=True返回一个标量,但不用求平均而是求和
reduceFalse_averageTrue = nn.MSELoss(size_average=False, reduce=True)
loss3 = reduceFalse_averageTrue(output,target)

loss1,loss2,loss3
# 输出
# loss1的值:输出一个数组而不是标量
# (tensor([[ 1.,  9.],
#          [25., 49.]]),
# loss2的值:输出一个标量而且求平均
# tensor(21.),
# loss3的值:输出一个标量求全部loss的和
# tensor(84.)

# 一般来说使用第二种,也是默认的那种

3.NLLoss

class torch.nn.NLLLoss(weight=None, size_average=None, ignore_index=-100, reduce=None,reduction=‘elementwise_mean’)

计算公式:loss(input, class) = -input[class]

功能: 不好用言语描述其功能!举个例,三分类任务,input=[-1.233, 2.657, 0.534], 真实标签为2(class=2),则loss为-0.534。就是对应类别上的输出,取一个负号!感觉被NLLLoss的名字欺骗了。

实际应用: 常用于多分类任务,但是input在输入NLLLoss()之前,需要对input进行log_softmax函数激活,即将input转换成概率分布的形式,并且取对数。其实这些步骤在CrossEntropyLoss中就有,如果不想让网络的最后一层是log_softmax层的话,就可以采用CrossEntropyLoss完全代替此函数。

即 CrossEntropyLoss = log_softmax + NLLLoss

参数:
weight(Tensor)- 为每个类别的loss设置权值,常用于类别不均衡问题。weight必须是float类型的tensor,其长度要于类别C一致,即每一个类别都要设置有weight。

size_average(bool)- 当reduce=True时有效。为True时,返回的loss为除以权重之和的平均值;为False时,返回的各样本的loss之和。

reduce(bool)- 返回值是否为标量,默认为True。

ignore_index(int)- 忽略某一类别,不计算其loss,其loss会为0,并且,在采用size_average时,不会计算那一类的loss,除的时候的分母也不会统计那一类的样本。

例子可以参考:https://blog.csdn.net/weixin_44751294/article/details/115281926 中的对应部分

# 设置预测值
output = torch.tensor([[0.6,0.2],[0.4,1.2],[0.5,0.8]])
# tensor([[0.6000, 0.2000],
#        [0.4000, 1.2000],
#        [0.5000, 0.8000]])

# 设置标签值
target = torch.tensor([0,1,0])
# tensor([0, 1, 0])

# 可以理解为现在将三张图片分成两类,而真实的类比已经在target中给出,分别是第一类,第二类与第三类
# 所以如果分别对每一张图片求最大释然估计,也就是nlloss时,也就是标签对应的那个类别添加一个符号
# 对于第一张照片来说:相当于是 -0.6
# 对于第二张照片来说:相当于是 -1.2
# 对于第三张照片来说:相当于是 -0.5

# 下面进行计算验证
# 设置三种不同参数的NLLoss
# reduce=False返回一个非标量,
reduceFalse_averageTrue = nn.NLLLoss(size_average=True, reduce=False)
loss1 = reduceFalse_averageTrue(output,target)

# reduce=True返回一个标量,并求平均
reduceTrue_averageTrue  = nn.NLLLoss(size_average=True, reduce=True)
loss2 = reduceTrue_averageTrue(output,target)

# reduce=True返回一个标量,但不用求平均而是求和
reduceFalse_averageTrue = nn.NLLLoss(size_average=False, reduce=True)
loss3 = reduceFalse_averageTrue(output,target)

loss1,loss2,loss3
# 输出
# loss1的值:输出一个数组而不是标量
# tensor([-0.6000, -1.2000, -0.5000])
# loss2的值:输出一个标量而且求平均
# tensor(-0.7667)
# loss3的值:输出一个标量求全部loss的和
# tensor(-2.3000)

# 一般来说使用第二种,也是默认的那种

4.CrossEntropyLoss

class torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None,reduction=‘elementwise_mean’)

功能: 将输入经过softmax激活函数之后,再计算其与target的交叉熵损失。即该方法将nn.LogSoftmax()和 nn.NLLLoss()进行了结合。严格意义上的交叉熵损失函数应该是nn.NLLLoss()。

补充:小谈交叉熵损失函数 交叉熵损失(cross-entropy Loss) 又称为对数似然损失(Log-likelihood Loss)、对数损失;二分类时还可称之为逻辑斯谛回归损失(Logistic Loss)。交叉熵损失函数表达式为 L = - sigama(y_i * log(x_i))。pytroch这里不是严格意义上的交叉熵损失函数,而是先将input经过softmax激活函数,将向量“归一化”成概率形式,然后再与target计算严格意义上交叉熵损失。 在多分类任务中,经常采用softmax激活函数+交叉熵损失函数,因为交叉熵描述了两个概率分布的差异,然而神经网络输出的是向量,并不是概率分布的形式。所以需要softmax激活函数将一个向量进行“归一化”成概率分布的形式,再采用交叉熵损失函数计算loss。 再回顾PyTorch的CrossEntropyLoss(),官方文档中提到时将nn.LogSoftmax()和 nn.NLLLoss()进行了结合,nn.LogSoftmax() 相当于激活函数 , nn.NLLLoss()是损失函数,将其结合,完整的是否可以叫做softmax+交叉熵损失函数呢?

CrossEntropyLoss = log_softmax + NLLLoss

例子可以参考:https://blog.csdn.net/weixin_44751294/article/details/115281926 中的对应部分

# 设置预测值
output = torch.tensor([[0.6,0.1,0.3],[1.2,0.4,0.6]],requires_grad=True)
# tensor([[0.6000, 0.1000, 0.3000],
#        [1.2000, 0.4000, 0.6000]], requires_grad=True)

# 设置标签值
target = torch.tensor([0,1]).type(torch.LongTensor)
# tensor([0, 1])

# 现在对其求交叉熵,得到一个标量,并且求平均
loss_f = nn.CrossEntropyLoss(weight=None, size_average=True, reduce=True)
loss = loss_f(output, target)
# 输出为:tensor(1.1728, grad_fn=)

# 现在进行求解论证
# 其中我们先对其求softmax,然后再取对数
log_output = F.log_softmax(output)
# tensor([[-0.8533, -1.3533, -1.1533],
#        [-0.6922, -1.4922, -1.2922]], grad_fn=)

# 此时,由于我们设置的标签值是0,1,也就是对于第一张图像选择的是-0.8533,对第二张图像选择是-1.4922
# 然后将这两个值进行最大释然运算,也就是
# -(-0.8533 + -1.4922)/2 = 1.1728

# CrossEntropyLoss = log_softmax + NLLLoss

其实可以从公式上对这个操作进行理解,我们现在是希望预测值是离真实值越接近越好的,也就是希望softmax生成出来的数据的某一个数据接近于1,这样的话取log对数之后这个数值会接近于0,而此时如果标签值也恰好地选定为该数值,则损失函数输出值便为0,达到了最优的效果。

而如果softmax之后是一个比较小的数值,也就是神经网络判断该类别为一个比较低的概率之后,也就是接近于0,而此时取log对数之后会变成一个很小的数值,也就是一个很大的负数负数。而去过此时标签值正好为这个数,那么loss函数输出值便会很高,也就是预测严重的错误。

基于这个思想,深度学习网络就是希望不断的优化,调整这个loss输出,使得预测值比较的符合标签值。

5.KLDivLoss

class torch.nn.KLDivLoss(size_average=None, reduce=None, reduction=‘elementwise_mean’)

功能: 计算input和target之间的KL散度( Kullback–Leibler divergence)。

计算公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49gNCZ1p-1619105260826)(attachment:image.png)]

真实的计算式子为:loss = p(x) * [logp(x) - q(x)]

补充:KL散度 KL散度( Kullback–Leibler divergence) 又称为相对熵(Relative Entropy),用于描述两个概率分布之间的差异。计算公式(离散时):

其中p表示真实分布,q表示p的拟合分布, D(P||Q)表示当用概率分布q来拟合真实分布p时,产生的信息损耗。这里的信息损耗,可以理解为损失,损失越低,拟合分布q越接近真实分布p。同时也可以从另外一个角度上观察这个公式,即计算的是 p 与 q 之间的对数差在 p 上的期望值。 特别注意,D(p||q) ≠ D(q||p), 其不具有对称性,因此不能称为K-L距离。

信息熵 = 交叉熵 - 相对熵 从信息论角度观察三者,其关系为信息熵 = 交叉熵 - 相对熵。在机器学习中,当训练数据固定,最小化相对熵 D(p||q) 等价于最小化交叉熵 H(p,q) 。

参数:(与上类似)
size_average(bool)- 当reduce=True时有效。为True时,返回的loss为平均值,平均值为element-wise的,而不是针对样本的平均;为False时,返回是各样本各维度的loss之和。 reduce(bool)- 返回值是否为标量,默认为True。

使用注意事项: 要想获得真正的KL散度,需要如下操作:

  1. reduce = True ;size_average=False

  2. 计算得到的loss 要对batch进行求平均

# 设置预测值与标签值
output = torch.from_numpy(np.array([[0.1132, 0.5477, 0.3390]])).float()
output.requires_grad = True
target = torch.from_numpy(np.array([[0.8541, 0.0511, 0.0947]])).float()
# output,target
# 输出为:
# (tensor([[0.1132, 0.5477, 0.3390]], requires_grad=True),
# tensor([[0.8541, 0.0511, 0.0947]]))

# 进行两组实验,分别取平均或者不取平均
loss_f = nn.KLDivLoss(size_average=True, reduce=False)
loss_f_mean = nn.KLDivLoss(size_average=True, reduce=True)

# 求出数值
loss_1 = loss_f(output, target)
loss_mean = loss_f_mean(output, target)
# loss_1,loss_mean
# 输出为:
# (tensor([[-0.2314, -0.1800, -0.2553]], grad_fn=),
# tensor(-0.2222, grad_fn=))

# 下面进行手动的验算
# 先转换成numpy的格式
target = target.detach().numpy()
output = output.detach().numpy()
# 利用公式进行验算
# loss = p(x) * [logp(x) - q(x)]
loss = target*(np.log(target)-output)
# 输出为:
# array([[-0.23138168, -0.17995737, -0.25531512]], dtype=float32)

# 验证结果是与上类似的

6.BCELoss

class torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction=‘elementwise_mean’)

功能: 二分类任务时的交叉熵计算函数。此函数可以认为是nn.CrossEntropyLoss函数的特例。其分类限定为二分类,y必须是{0,1}。还需要注意的是,input应该为概率分布的形式,这样才符合交叉熵的应用。所以在BCELoss之前,input一般为sigmoid激活层的输出,官方例子也是这样给的。该损失函数在自编码器中常用。

计算公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1qbAzRr-1619105260829)(attachment:image.png)]

参数:

weight(Tensor)- 为每个类别的loss设置权值,常用于类别不均衡问题。

size_average(bool)- 当reduce=True时有效。为True时,返回的loss为平均值;为False时,返回的各样本的loss之和。

reduce(bool)- 返回值是否为标量,默认为True

对于BCELoss来说,输出值与真实值的数据的shape必须是相同的,否则会出错

# 设置输出值
output = torch.tensor([[0.2,0.8],[0.9,0.1]],requires_grad=True).float()
# output.shape:torch.Size([2, 2])

# 设置真实值
target = torch.tensor([[0,1],[1,0]]).float()
# target.shape:torch.Size([2, 2])

# 将输出值变成一个概率分布,因为由于涉及log的计算,所以输出值必须是0-1之间的
sig_output = F.sigmoid(output)
# tensor([[0.5498, 0.6900],
#         [0.7109, 0.5250]], grad_fn=)

# 设置两种损失值的求法
loss_rf = nn.BCELoss(size_average=True,reduce=False)
loss_rt = nn.BCELoss(size_average=True,reduce=True)

# 计算损失值
loss1 = loss_rf(sig_output,target)
loss2 = loss_rt(sig_output,target)
# loss1,loss2:输出为:
# (tensor([[0.7981, 0.3711],
#          [0.3412, 0.7444]], grad_fn=),
#  tensor(0.5637, grad_fn=))

# 下面进行手动的验算
# 转换格式
output = output.detach().numpy()
target = target.detach().numpy()

# 根据公式:ln = -[yn*log(xn)+(1-yn)*log(1-xn)]
(math.log(sig_output[0][0])*target[0][0]+math.log(1-sig_output[0][0])*(1-target[0][0]))*-1
# 输出为:tensor(0.7981),与0.7981相同,验证成功

# 而假设现在输出值进行训练优化之后变成了
output = torch.tensor([[0.1,0.9],[0.95,0.05]],requires_grad=True).float()
sig_output = F.sigmoid(output)
# 输出为:
# tensor([[0.5250, 0.7109],
#         [0.7211, 0.5125]], grad_fn=)

# 相较之前的每个参数都有了一点点的进步,现在来计算更新后的每个样本的损失值
loss1 = loss_rf(sig_output,target)
loss2 = loss_rt(sig_output,target)
# loss1,loss2输出为:
# (tensor([[0.7444, 0.3412],
#         [0.3270, 0.7185]], grad_fn=),
# tensor(0.5327, grad_fn=))
# 可以看见,无论是个体的样本还是总体的损失函数值,都得到了改进,也就是达到了优化的目的

参考链接:https://zhuanlan.zhihu.com/p/61379965

你可能感兴趣的:(Pytorch,损失函数,loss)