【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


目录
相关文章
|
4月前
|
机器学习/深度学习 文字识别 PyTorch
PyTorch内置损失函数汇总 !!
PyTorch内置损失函数汇总 !!
74 0
|
4月前
|
数据挖掘 PyTorch 算法框架/工具
人脸识别中的损失函数ArcFace及其实现过程代码(pytorch)--理解softmax损失函数及Arcface
人脸识别中的损失函数ArcFace及其实现过程代码(pytorch)--理解softmax损失函数及Arcface
411 0
|
存储 数据可视化 PyTorch
【PyTorch基础教程17】损失函数详解
功能:计算二分类任务时的交叉熵(Cross Entropy)函数。在二分类中,label是{0,1}。对于进入交叉熵函数的input为概率分布的形式。一般来说,input为sigmoid激活层的输出,或者softmax的输出。
872 0
【PyTorch基础教程17】损失函数详解
|
机器学习/深度学习 PyTorch 算法框架/工具
Pytorch学习笔记(6):模型的权值初始化与损失函数
Pytorch学习笔记(6):模型的权值初始化与损失函数
257 0
Pytorch学习笔记(6):模型的权值初始化与损失函数
|
机器学习/深度学习 数据采集 PyTorch
pytorch教程 (四)- 损失函数
pytorch教程 (四)- 损失函数
107 0
|
机器学习/深度学习 PyTorch 算法框架/工具
pytorch基础使用—自定义损失函数
pytorch基础使用—自定义损失函数
280 0
|
机器学习/深度学习 人工智能 PyTorch
【Pytorch神经网络理论篇】 34 样本均衡+分类模型常见损失函数
Sampler类中有一个派生的权重采样类WeightedRandomSampler,能够在加载数据时,按照指定的概率进行随机顺序采样。
378 0
|
机器学习/深度学习 人工智能 PyTorch
【Pytorch神经网络理论篇】 09 神经网络模块中的损失函数
根据损失值,使用链式反向求导的方法,依次计算出模型中每个参数/权重的梯度
206 0

相关实验场景

更多