【8】pytorch中的损失函数

简介: 【8】pytorch中的损失函数
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或者是一个标量。


计算公式:

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或者是一个标量。


计算公式:

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=<NllLossBackward>)
# 现在进行求解论证
# 其中我们先对其求softmax,然后再取对数
log_output = F.log_softmax(output)
# tensor([[-0.8533, -1.3533, -1.1533],
#        [-0.6922, -1.4922, -1.2922]], grad_fn=<LogSoftmaxBackward>)
# 此时,由于我们设置的标签值是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)。


计算公式:

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


  1. 计算得到的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=<KlDivBackward>),
# tensor(-0.2222, grad_fn=<KlDivBackward>))
# 下面进行手动的验算
# 先转换成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激活层的输出,官方例子也是这样给的。该损失函数在自编码器中常用。


计算公式:

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=<SigmoidBackward>)
# 设置两种损失值的求法
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=<BinaryCrossEntropyBackward>),
#  tensor(0.5637, grad_fn=<BinaryCrossEntropyBackward>))
# 下面进行手动的验算
# 转换格式
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=<SigmoidBackward>)
# 相较之前的每个参数都有了一点点的进步,现在来计算更新后的每个样本的损失值
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=<BinaryCrossEntropyBackward>),
# tensor(0.5327, grad_fn=<BinaryCrossEntropyBackward>))
# 可以看见,无论是个体的样本还是总体的损失函数值,都得到了改进,也就是达到了优化的目的


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


目录
相关文章
|
6月前
|
机器学习/深度学习 文字识别 PyTorch
PyTorch内置损失函数汇总 !!
PyTorch内置损失函数汇总 !!
98 0
|
6月前
|
数据挖掘 PyTorch 算法框架/工具
人脸识别中的损失函数ArcFace及其实现过程代码(pytorch)--理解softmax损失函数及Arcface
人脸识别中的损失函数ArcFace及其实现过程代码(pytorch)--理解softmax损失函数及Arcface
552 0
|
1月前
|
机器学习/深度学习 算法 PyTorch
深度学习笔记(十三):IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU、WIOU损失函数分析及Pytorch实现
这篇文章详细介绍了多种用于目标检测任务中的边界框回归损失函数,包括IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU和WIOU,并提供了它们的Pytorch实现代码。
184 1
深度学习笔记(十三):IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU、WIOU损失函数分析及Pytorch实现
|
2月前
|
机器学习/深度学习
小土堆-pytorch-神经网络-损失函数与反向传播_笔记
在使用损失函数时,关键在于匹配输入和输出形状。例如,在L1Loss中,输入形状中的N代表批量大小。以下是具体示例:对于相同形状的输入和目标张量,L1Loss默认计算差值并求平均;此外,均方误差(MSE)也是常用损失函数。实战中,损失函数用于计算模型输出与真实标签间的差距,并通过反向传播更新模型参数。
|
6月前
|
机器学习/深度学习 监控 PyTorch
PyTorch模型训练:优化器与损失函数的选择
【4月更文挑战第17天】PyTorch中的优化器(如SGD, Adam, RMSprop)和损失函数(如MSE Loss, Cross Entropy Loss)对模型训练效果有显著影响。优化器选择应基于任务复杂度和数据规模,SGD适合简单任务,而Adam和RMSprop适用于复杂情况。损失函数选择依赖于任务类型,MSE Loss用于回归,Cross Entropy Loss用于分类。实践中,应尝试不同组合,调整学习率,监控训练过程,并使用验证集优化模型。
|
机器学习/深度学习 PyTorch 算法框架/工具
Pytorch学习笔记(6):模型的权值初始化与损失函数
Pytorch学习笔记(6):模型的权值初始化与损失函数
318 0
Pytorch学习笔记(6):模型的权值初始化与损失函数
|
机器学习/深度学习 数据采集 PyTorch
pytorch教程 (四)- 损失函数
pytorch教程 (四)- 损失函数
124 0
|
机器学习/深度学习 PyTorch 算法框架/工具
pytorch基础使用—自定义损失函数
pytorch基础使用—自定义损失函数
310 0

相关实验场景

更多
下一篇
无影云桌面