基本用法:
criterion = LossCriterion() #构造函数有自己的参数
loss = criterion(x, y) #调用标准时也有参数
计算出来的结果已经对mini-batch
取了平均。
损失函数分类
根据深度函数的模型类型,损失函数可分为三类:
- 回归损失(Regression loss):预测连续的数值,即输出是连续数据:如预测房价、气温等;
- 分类损失(Classification loss):预测离散的数值,即输出是离散数据:如预测硬币正反、图像分类、语义分割等;
- 目标检测损失函数
- 图像分割损失函数
- 排序损失(Ranking loss):预测输入样本间的相对距离,即输出一般是概率值,如预测两张面部图像是否属于同一个人等;
一、回归损失
1. L1范数损失(L1Loss)-平均绝对误差损失函数(Mean Absolute Error, MAE)
torch.nn.L1Loss(size_average=True
L1Loss
,也被称为最小绝对值误差(Least Absolute Error, LAE)或最小绝对偏差(Least Absolute Deviation, LAD),是回归任务中常用的一种损失函数。它的主要目的是最小化预测值与真实值之间差值的绝对值之和。
公式定义
L1Loss
的公式可以表示为:
其中,n 是样本数量,yi 是第 i 个样本的真实值,y^i 是第 i 个样本的预测值,∣yi−y^i∣ 是预测值与真实值之间差值的绝对值。
特点与优势
-
鲁棒性:相比于平方损失(MSE),
L1Loss
对异常值(即那些远离其他观测值的观测值)的惩罚较小。因为异常值在平方损失中会被平方放大,而在L1Loss
中只是线性增加。 -
稀疏解:在一些情况下,使用
L1Loss
作为正则化项(即Lasso回归)可以促使模型学习到稀疏的权重,即许多权重会变为零。这有助于特征选择,因为非零权重的特征可以被认为是重要的。 -
计算简单:
L1Loss
的计算相对简单,只需要计算差值的绝对值并求和。
缺点
- 在预测值与真实值接近零的区域,
L1Loss
的梯度是常数,这可能导致训练过程在接近最优解时变得缓慢,因为梯度不会随着预测值的改进而逐渐减小。
应用场景
L1Loss
在需要处理异常值或追求稀疏解的回归任务中非常有用。它也被广泛应用于机器学习和深度学习的各种应用中,特别是在需要正则化以避免过拟合的情况下。
示例代码(PyTorch)
在PyTorch中,可以使用torch.nn.L1Loss
类来构建L1Loss
损失函数。以下是一个简单的示例代码:
import torch
loss_fun = torch.nn.L1Loss()
input = torch.randn(2, 2, requires_grad=True)
target = torch.randn(2, 2)
output = loss_fun(input, target)
print(input)
print(target)
print(output)
#0.7826 =(|-0.0621-(-0.8778)| + |0.5715-(-0.8386)| + |0.0940-(-0.3857)| + |-1.7804-(-1.3557)|)/4
输出:
tensor([[-0.0621, 0.5715],
[ 0.0940, -1.7804]], requires_grad=True)
tensor([[-0.8778, -0.8386],
[-0.3857, -1.3557]])
tensor(0.7826, grad_fn=<L1LossBackward>)
在这个示例中,predictions
是模型的预测值,targets
是真实值。通过调用l1_loss(predictions, targets)
计算损失,并可以通过.backward()
方法进行反向传播,以更新模型的参数。
2. L2范数损失-均方误差损失函数(Mean Squared Error, MSE)
class torch.nn.MSELoss(size_average=True)
MSELoss,即均方误差损失函数(Mean Squared Error Loss),是深度学习中常用的一种损失函数,特别是在回归任务中。以下是对MSELoss的详细解释:
公式定义
MSELoss的数学表达式为:
其中,n 是样本数量,yi 是第 i 个样本的真实值,y^i 是第 i 个样本的预测值。公式中的求和操作(∑)表示对所有样本的损失进行累加,然后除以样本数量 n 以求得平均损失。
特点与优势
- 易于理解:MSELoss直接计算预测值与真实值之间差值的平方,并求平均,直观反映了预测的准确性。
- 梯度求解方便:MSELoss的梯度是预测值与真实值之间差值的两倍,即 2(yi−y^i),这使得在反向传播过程中梯度的计算相对简单。
- 求解速度快:由于MSELoss的梯度计算简单,因此在训练过程中,使用MSELoss作为损失函数通常能够较快地收敛到最优解。
缺点
- 对异常值敏感:当数据中存在异常值时,MSELoss会给予这些异常值较大的权重,因为它们会显著增大损失函数的值。这可能导致模型在训练过程中过于关注这些异常值,而忽略了其他正常样本的信息。
- 偏导值问题:当预测值接近真实值时,MSELoss的偏导值会逐渐减小,这可能导致模型在接近最优解时训练速度变慢。此外,当预测值远离真实值时,偏导值会迅速增大,这可能导致梯度爆炸的问题。
应用场景
MSELoss广泛应用于各种回归任务中,如房价预测、年龄预测、销量预测等。在这些任务中,目标是预测一个连续的值,而MSELoss能够有效地衡量预测值与真实值之间的差异。
PyTorch中的实现
在PyTorch中,可以使用torch.nn.MSELoss
类来构建MSELoss损失函数。该类接受几个可选参数,如size_average
(已弃用,推荐使用reduction
参数)、reduce
(已弃用,推荐使用reduction
参数)和reduction
(指定返回值的类型,可选值为'none'、'mean'或'sum')。默认情况下,reduction
的值为'mean',即返回所有样本损失的平均值。
示例代码
以下是一个使用PyTorch实现MSELoss的示例代码:
import torch
import torch.nn as nn
# 创建MSELoss实例
mse_loss = nn.MSELoss(reduction='mean')
# 假设预测值和真实值
predictions = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
targets = torch.tensor([1.5, 2.5, 2.0])
# 计算损失
loss = mse_loss(predictions, targets)
# 反向传播
loss.backward()
# 输出损失值和预测值的梯度
print("Loss:", loss.item())
print("Gradients:", predictions.grad)
在这个示例中,predictions
是模型的预测值,targets
是真实值。通过调用mse_loss(predictions, targets)
计算损失,并可以通过.backward()
方法进行反向传播,以更新模型的参数。
3. Huber损失函数-
SmoothL1Loss(平滑L1 Loss
)
torch.nn.SmoothL1Loss(size_average=True)
SmoothL1Loss是一种在深度学习中常用的损失函数,特别适用于回归任务。以下是对SmoothL1Loss的详细解释:
基本概念
SmoothL1Loss是一种结合了平方损失(L2 Loss)和绝对损失(L1 Loss)优点的损失函数。它的主要目的是在回归问题中减少预测值与真实值之间的差异,同时保持对异常值的稳健性。
公式定义
SmoothL1Loss的公式可以表示为:
其中,x 是预测值与真实值之间的差值。当 ∣x∣≤1 时,SmoothL1Loss表现为平方损失,这有助于在差值较小时提供更精确的梯度信息;当 ∣x∣>1 时,它表现为绝对损失,这有助于在差值较大时限制梯度的增长,从而避免梯度爆炸。
SooothL1Loss其实是L2Loss和L1Loss的结合,它同时拥有L2 Loss和L1 Loss的部分优点。
1. 当预测值和ground truth差别较小的时候(绝对值差小于1),梯度不至于太大。(损失函数相较L1 Loss比较圆滑)
2. 当差别大的时候,梯度值足够小(较稳定,不容易梯度爆炸)。
特点与优势
- 鲁棒性:相比于平方损失(MSE),SmoothL1Loss对异常值的惩罚更小,这使得模型在训练过程中更加稳健,不易受到极端值的影响。
- 数值稳定性:在接近零的区域内,SmoothL1Loss通过平方损失的方式提供平滑的梯度,有助于避免数值不稳定性。
- 适应性:它结合了平方损失和绝对损失的优点,能够根据预测值与真实值之间的差值大小自动调整损失的计算方式。
应用场景
SmoothL1Loss在深度学习中有着广泛的应用,特别是在回归问题中,如物体检测、人脸识别、语音识别等领域。在这些应用中,预测值与真实值之间的差值往往需要精确地衡量,并且模型需要能够处理可能出现的异常值。
示例代码(PyTorch)
在PyTorch中,可以使用torch.nn.SmoothL1Loss
类来构建SmoothL1Loss损失函数。以下是一个简单的示例代码:
import torch
import torch.nn as nn
# 创建SmoothL1Loss实例
smooth_l1_loss = nn.SmoothL1Loss(reduction='mean') # 默认reduction为'mean',也可以设置为'sum'或'none'
# 假设预测值和真实值
predictions = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
targets = torch.tensor([1.5, 2.5, 2.0])
# 计算损失
loss = smooth_l1_loss(predictions, targets)
# 反向传播
loss.backward()
# 输出损失值和预测值的梯度
print("Loss:", loss.item())
print("Gradients:", predictions.grad)
在这个示例中,predictions
是模型的预测值,targets
是真实值。通过调用smooth_l1_loss(predictions, targets)
计算损失,并可以通过.backward()
方法进行反向传播,以更新模型的参数。
总结
SmoothL1Loss是一种在深度学习中广泛使用的损失函数,特别适用于回归任务。它结合了平方损失和绝对损失的优点,能够提供更加稳健和精确的梯度信息,从而在训练过程中优化模型的性能。
二、分类损失
1. 交叉熵损失函数(Cross-Entropy, CE)
torch.nn.CrossEntropyLoss(weight=None, size_average=True)
计算实际输出(概率)与期望输出(概率)的距离;(二分类或多分类),可看做nn.LogSoftmax() 和 nn.NLLLoss() 二者的结合;
CrossEntropyLoss(交叉熵损失)是深度学习和机器学习中常用的一种损失函数,特别是在分类问题中。它衡量的是模型预测的概率分布与真实标签的概率分布之间的差异。
它主要刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。
以下是对CrossEntropyLoss的详细解释:
定义与公式
CrossEntropyLoss基于概率分布的距离,通过计算模型预测的概率分布与真实标签的概率分布之间的交叉熵来评估模型的性能。其基本公式(针对二分类问题)可以表示为:
Loss可以表述为以下形式:
当weight
参数被指定的时候,loss
的计算公式变为:
计算出的loss
对mini-batch
的大小取了平均。
其中,L 是损失函数的值,y 是真实标签(0或1),y^ 是模型预测的概率值(介于0和1之间)。对于多分类问题,通常会使用softmax函数将模型的输出转换为概率分布,然后计算交叉熵损失。
特点与优势
- 直观反映差异:交叉熵损失能够直观地反映模型预测与真实标签之间的差异,当预测概率分布越接近真实标签的概率分布时,损失值越小。
- 优化效果显著:在分类问题中,交叉熵损失能够鼓励模型对正确的类别给出更高的预测概率,同时对错误的类别给出更低的预测概率,从而优化模型的分类性能。
- 结合softmax使用:在多分类问题中,交叉熵损失通常与softmax函数结合使用,softmax函数可以将模型的原始输出(logits)转换为概率分布,使得交叉熵损失的计算更加直接和有效。
应用场景
CrossEntropyLoss广泛应用于各种分类问题的模型训练中,如图像分类、文本分类、语音识别等。在神经网络中,特别是卷积神经网络(CNN)和循环神经网络(RNN)等复杂模型中,交叉熵损失是评估模型性能的重要指标之一。
PyTorch中的实现
在PyTorch中,CrossEntropyLoss可以通过torch.nn.CrossEntropyLoss
类实现。该类将nn.LogSoftmax()和nn.NLLLoss()组合在一个类中,使得用户可以直接传入模型的原始输出(logits)和目标标签(类别索引),而无需手动进行softmax转换或负对数似然损失的计算。
示例代码
以下是一个使用PyTorch中的CrossEntropyLoss的示例代码:
import torch
import torch.nn as nn
# 创建模型输出和目标标签
output = torch.randn(10, 5) # 假设有10个样本,每个样本的输出为5个类别的logits
target = torch.tensor([1, 0, 4, 2, 3, 1, 0, 4, 2, 3]) # 目标类别索引
# 创建交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 计算损失
loss = criterion(output, target)
print("Loss:", loss.item())
在这个示例中,output
是模型的原始输出(logits),target
是目标类别索引。通过nn.CrossEntropyLoss()
计算得到的loss
就是模型预测与真实标签之间的交叉熵损失。
实现过程
loss_func = nn.CrossEntropyLoss()
pre = torch.tensor([0.8, 0.5, 0.2, 0.5], dtype=torch.float)
tgt = torch.tensor([1, 0, 0, 0], dtype=torch.float)
print("手动计算:")
print("1.softmax")
print(torch.softmax(pre, dim=-1))
print("2.取对数")
print(torch.log(torch.softmax(pre, dim=-1)))
print("3.与真实值相乘")
print(-torch.sum(torch.mul(torch.log(torch.softmax(pre, dim=-1)), tgt), dim=-1))
print()
print("调用损失函数:")
print(loss_func(pre, tgt))
手动计算:
1.softmax
tensor([0.3300, 0.2445, 0.1811, 0.2445])
2.取对数
tensor([-1.1087, -1.4087, -1.7087, -1.4087])
3.与真实值相乘
tensor(1.1087)
调用损失函数:
tensor(1.1087)
2. 负对数似然损失 NLLLoss
torch.nn.NLLLoss(weight=None, size_average=True)
NLLLoss,全称为Negative Log Likelihood Loss(负对数似然损失),是一种在深度学习中常用的损失函数,尤其在处理分类问题时表现突出。以下是关于NLLLoss的详细解析:
定义与原理
NLLLoss衡量了模型预测概率分布与真实标签之间差异的损失。具体来说,它通过对模型输出的对数概率进行负化处理来评估预测的准确性。在分类任务中,如果模型的预测越接近真实标签,则NLLLoss的值越小,表示模型性能越好。
数学表达式
NLLLoss的数学表达式可以表示为:
其中,N是样本数量,yi是第i个样本的真实标签,p(yi∣xi)是模型对于给定输入xi预测出yi的概率。注意,这里的概率p(yi∣xi)通常是通过对模型输出的logits(即未经softmax归一化的原始分数)应用softmax函数得到的。然而,在实际使用中,由于NLLLoss常与softmax函数结合使用,因此更常见的做法是直接对softmax的输出(即概率分布的对数形式)应用NLLLoss。
PyTorch中的实现
在PyTorch中,NLLLoss通过torch.nn.NLLLoss
类实现。使用时,通常需要将模型的输出先通过torch.nn.LogSoftmax
函数转换为对数概率,然后再将结果传递给torch.nn.NLLLoss
。但是,由于torch.nn.CrossEntropyLoss
内部已经集成了softmax和NLLLoss的功能,因此在实际应用中,直接使用CrossEntropyLoss
可能更为方便和高效。
import torch
m = torch.nn.LogSoftmax(dim=1)
loss_fun = torch.nn.NLLLoss()
# input is of size N x C = 3 x 5
input = torch.randn(3, 5, requires_grad=True)
# each element in target has to have 0 <= value < C
target = torch.tensor([1, 0, 4])
output = loss_fun(m(input), target)
output.backward()
print(m(input))
print(target)
print(output)
# 2.7901 = (3.0978 + 1.1319 + 4.1406)/3
输出:
tensor([[-1.6166, -3.0978, -1.2662, -0.9502, -2.4337],
[-1.1319, -2.5537, -1.3417, -2.4742, -1.3698],
[-1.7925, -3.0011, -0.3990, -2.3350, -4.1406]],
grad_fn=<LogSoftmaxBackward>)
tensor([1, 0, 4])
tensor(2.7901, grad_fn=<NllLossBackward>)
参数与注意事项
torch.nn.NLLLoss
类的主要参数包括:
weight
(权重):一个可选参数,用于为每个类别的损失分配不同的权重。这有助于处理类别不平衡的问题。ignore_index
(忽略索引):一个可选参数,指定一个目标值,该目标值的贡献在计算损失时将被忽略。这可以用于忽略某些不需要训练的类别。reduction
(归约方式):指定损失的计算方式,可选值为'none'、'mean'和'sum'。默认为'mean',表示计算所有样本损失的平均值。
使用NLLLoss时需要注意以下几点:
- 输入到NLLLoss的必须是经过对数处理的概率值(即log-probabilities),这通常是通过LogSoftmax层得到的。
- 目标标签必须是整数类型,并且每个标签的值都应该在有效范围内(即类别索引的范围内)。
- 如果使用权重参数,请确保权重的维度与类别数相匹配。
应用场景
NLLLoss在多分类问题中非常有用,特别是在处理具有大量类别的任务时。然而,由于它要求输入是对数概率,因此在某些情况下可能需要与softmax函数结合使用。此外,对于二分类问题,虽然也可以使用NLLLoss结合sigmoid函数,但通常更倾向于使用BCELoss(Binary Cross Entropy Loss)作为损失函数。
总的来说,NLLLoss是一种强大的损失函数,它在深度学习分类任务中发挥着重要作用。通过合理设置参数和注意使用细节,可以充分利用NLLLoss的优势来提高模型的性能。
3.NLLLoss2d
torch.nn.NLLLoss2d(weight=None, size_average=True)
NLLLoss2d(Negative Log Likelihood Loss for 2d inputs)是PyTorch中用于处理二维数据(如图像数据)的一种损失函数。它在分类问题中,尤其是在图像识别和分割等任务中表现出色。以下是关于NLLLoss2d的详细解析:
定义与原理
NLLLoss2d是NLLLoss在二维数据上的扩展。它计算每个像素的负对数似然损失,并对整个批次的像素进行平均,以获得整体损失。在分类问题中,如果模型的预测越接近真实标签,则NLLLoss2d的值越小,表示模型性能越好。
使用场景
NLLLoss2d特别适用于处理图像数据中的像素级分类任务。由于它对每个像素进行了独立处理,因此非常适合于二维数据的场景。
PyTorch中的实现
在PyTorch中,NLLLoss2d通过torch.nn.NLLLoss2d
类实现。然而,需要注意的是,从PyTorch的较新版本开始,torch.nn.NLLLoss2d
可能已经被弃用或合并到其他更通用的损失函数中。在最新版本的PyTorch中,通常建议使用torch.nn.NLLLoss
结合适当的维度处理来实现类似的功能。
注意事项
- 输入要求:
- 输入到NLLLoss2d(或等效实现)的预测值通常是对数概率(即log-probabilities),这通常是通过LogSoftmax层得到的。然而,在实际使用中,更常见的是使用
torch.nn.CrossEntropyLoss
,它内部集成了softmax和NLLLoss的功能,直接对原始logits(未经softmax归一化的原始分数)进行处理。 - 目标标签必须是整数类型,并且每个标签的值都应该在有效范围内(即类别索引的范围内)。
- 输入到NLLLoss2d(或等效实现)的预测值通常是对数概率(即log-probabilities),这通常是通过LogSoftmax层得到的。然而,在实际使用中,更常见的是使用
- 权重与忽略索引:
- 可以为每个类别的损失分配不同的权重,以处理类别不平衡的问题。
- 可以指定一个忽略索引,该索引对应的标签在计算损失时将被忽略。
- 归约方式:
- 指定损失的计算方式,如'none'、'mean'和'sum'。默认为'mean',表示计算所有样本损失的平均值。
替代方案
由于torch.nn.NLLLoss2d
可能已被弃用,以下是一些替代方案:
- 使用
torch.nn.NLLLoss
结合适当的维度处理。例如,可以先对模型的输出进行softmax操作,然后取对数,最后将结果重塑为二维形式以匹配目标标签的维度,最后应用torch.nn.NLLLoss
。 - 直接使用
torch.nn.CrossEntropyLoss
,它内部集成了softmax和NLLLoss的功能,适用于多分类问题,并且可以直接处理二维数据(通过调整输入数据的维度)。
总结
NLLLoss2d是PyTorch中用于处理二维数据分类问题的一种损失函数。然而,在最新版本的PyTorch中,可能需要使用替代方案来实现类似的功能。在实际应用中,建议根据具体任务和数据集的特点选择合适的损失函数。
4. 二元交叉熵损失函数-BCELoss
torch.nn.BCELoss(weight=None, size_average=True)
BCELoss,全称为Binary Cross-Entropy Loss,即二元交叉熵损失函数,是深度学习中用于二分类问题的一种常用损失函数。它用于衡量模型预测的概率分布与实际标签之间的差异,从而指导模型的优化方向。以下是对BCELoss的详细解析:
定义与原理
BCELoss通过计算模型输出的概率分布与实际标签之间的交叉熵损失来评估模型的性能。对于每个样本,BCELoss计算模型输出的概率值(预测为正类的概率)与实际标签(0或1)之间的交叉熵损失,然后对所有样本的损失取平均值。其数学公式为:
其中,N是样本数量,yi是第i个样本的实际标签,pi是第i个样本模型预测为正类的概率。
应用场景
BCELoss广泛应用于二分类问题中,如图像分类、文本分类等场景。在这些场景中,模型需要预测样本属于两个类别中的哪一个。BCELoss通过衡量模型预测的概率分布与实际标签之间的差异,帮助模型学习正确的分类决策。
PyTorch中的实现
在PyTorch中,可以使用torch.nn.BCELoss
类来计算BCELoss。通常,BCELoss与Sigmoid激活函数结合使用,将模型的原始输出(logits)转换为概率值,然后计算损失。示例代码如下:
import torch
import torch.nn as nn
# 假设model_output是模型的原始输出(logits),target是实际标签
model_output = torch.randn(1, requires_grad=True) # 示例输出
target = torch.empty(1).random_(2) # 示例标签,随机生成0或1
# 使用Sigmoid函数将输出转换为概率值
sigmoid = nn.Sigmoid()
prob = sigmoid(model_output)
# 计算BCELoss
criterion = nn.BCELoss()
loss = criterion(prob, target)
# 反向传播和优化过程(此处省略)
优化与注意事项
- 结合Sigmoid激活函数:在二分类问题中,通常将BCELoss与Sigmoid激活函数结合使用,以将模型的原始输出转换为概率值。
- 权重调整:在某些情况下,可能需要为不同类别的样本分配不同的权重,以处理类别不平衡的问题。PyTorch中的
nn.BCELoss
支持通过weight
参数来指定每个类别的权重。 - 归约方式:BCELoss支持多种归约方式,如'none'(不进行归约,返回每个样本的损失)、'mean'(返回所有样本损失的平均值,默认值)和'sum'(返回所有样本损失的总和)。
- 损失值监控:在训练过程中,应定期监控BCELoss的值,以评估模型的训练效果。损失值越小,表示模型的预测结果越接近实际标签。
综上所述,BCELoss是深度学习中用于二分类问题的一种重要损失函数,它通过衡量模型预测的概率分布与实际标签之间的差异来指导模型的优化方向。在PyTorch等深度学习框架中,可以方便地实现和使用BCELoss。
三、 分割损失函数
交叉熵(Cross Entropy)
- Log loss
- Weighted cross entropy(WCE)
- Balanced cross entropy(BCE)
- Focal loss
Overlap measures
- Dice Loss / F1 score
- IoU(Jaccard Index)
- Tversky loss
- Generalized Dice loss
损失函数组合
- BCE+Dice loss
- Dice+Focal loss
- Exponential Logarithmic loss
1. Log loss(Cross-entropy)
其中, p 为像素 x 的真实类别, 为预测 x 属于类别1的概率。所有样本的对数损失表示为每个样本对数损失的平均值, 对于完美的分类器, 对数损失为 0。
缺陷:同等的关注每一个类别,易受类别不均的影响,在分割领域尤其如此。
在Keras中,这个损失函数是binary_crossentropy(y_true, y_pred)
在TensorFlow中,函数是softmax_cross_entropy_with_logits_v2
2. Weighted cross entropy(WCE)
为正样本加权。设置 � >1,减少假阴性;设置 � <1,减少假阳性。
在TensorFlow中,函数是weighted_cross_entropy_with_logits
在Keras中,需要自己实现这个函数
def weighted_cross_entropy(beta):
def convert_to_logits(y_pred):
# see https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/keras/backend.py#L3525
y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon())
return tf.log(y_pred / (1 - y_pred))
def loss(y_true, y_pred):
y_pred = convert_to_logits(y_pred)
loss = tf.nn.weighted_cross_entropy_with_logits(logits=y_pred, targets=y_true, pos_weight=beta)
# or reduce_sum and/or axis=-1
return tf.reduce_mean(loss)
return loss
其中convert_to_logits()是必需的。因为我们在最后一层应用了sigmoid函数,所以应该对预测进行转换。
3. Balanced cross entropy(BCE)
对负样本也进行加权。
在Keras中可以这样实现
def balanced_cross_entropy(beta):
def convert_to_logits(y_pred):
# see https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/keras/backend.py#L3525
y_pred = tf.clip_by_value(y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon())
return tf.log(y_pred / (1 - y_pred))
def loss(y_true, y_pred):
y_pred = convert_to_logits(y_pred)
pos_weight = beta / (1 - beta)
loss = tf.nn.weighted_cross_entropy_with_logits(logits=y_pred, targets=y_true, pos_weight=pos_weight)
# or reduce_sum and/or axis=-1
return tf.reduce_mean(loss * (1 - beta))
return loss
当 =1时,pos_weight的分母无法定义(当 不是一个固定值时这种情况会发生)。这个时候可以为 加一个小的值
4. Focal loss
focal loss是在目标检测领域提出来的。其目的是关注难例(也就是给难分类的样本较大的权重)。对于正样本,使预测概率大的样本(简单样本)得到的loss变小,而预测概率小的样本(难例)loss变得大,从而加强对难例的关注度。但引入了额外参数,增加了调参难度。
损失随样本概率的变化如图
from keras import backend as K
'''
Compatible with tensorflow backend
'''
def focal_loss(gamma=2., alpha=.25):
def focal_loss_fixed(y_true, y_pred):
pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
return -K.sum(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1))-K.sum((1-alpha) * K.pow( pt_0, gamma) * K.log(1. - pt_0))
return focal_loss_fixed
上述方法都是基于像素分类正确与否计算的损失,不一定能在评测指标上获得较好效果。图像分割领域的评测指标通常使用DICE指数
5. Dice Loss / F1 score
def dice_loss(y_true, y_pred):
numerator = 2 * tf.reduce_sum(y_true * y_pred, axis=-1)
denominator = tf.reduce_sum(y_true + y_pred, axis=-1)
return 1 - (numerator + 1) / (denominator + 1)
有时使用dice loss会使训练曲线有时不可信,而且dice loss好的模型并不一定在其他的评价标准上效果更好
DiceLoss,也称为Dice系数损失或Sørensen-Dice系数损失,是一种在图像分割任务中广泛应用的损失函数。它的设计初衷是为了解决类别不平衡的问题,尤其适用于前景和背景差异显著的场景,如医学图像分割中的肿瘤、器官等区域分割。以下是对DiceLoss的详细解析:
定义与原理
DiceLoss是基于Dice系数(Dice Coefficient)的损失函数。Dice系数是一种用于评估两个集合相似度的指标,其值域为[0, 1],值越大表示两个集合越相似。在图像分割中,Dice系数用于衡量模型预测的分割结果与真实标签之间的重叠程度。DiceLoss则是通过最小化Dice系数的负值(或等价地,最大化Dice系数)来优化模型,从而提高分割精度。
Dice系数的数学表达式为:
其中,A和B分别表示模型预测的分割结果和真实标签的分割结果,∣A∩B∣表示两者交集的元素个数,∣A∣和∣B∣分别表示两者各自的元素个数。
应用场景
DiceLoss在医学图像分割中尤为常用,因为医学图像中往往存在目标区域小、背景复杂且类别不平衡的问题。DiceLoss能够有效地应对这些问题,提高模型对小目标的分割精度。此外,DiceLoss还广泛应用于其他需要高精度分割的场景,如卫星图像处理、自动驾驶中的道路和障碍物分割等。
优点与局限性
优点:
- 对类别不平衡问题鲁棒:DiceLoss通过最大化模型预测结果与真实标签的重叠部分来提高分割精度,即使目标区域很小也能有效衡量模型性能。
- 计算简单直观:DiceLoss的公式简单直观,计算方便,适合在深度学习框架中实现。
- 广泛应用:DiceLoss在医学图像分割等需要高精度分割的场景中表现出色,是图像分割任务中的常用损失函数。
局限性:
- 对小目标敏感:当目标区域非常小时,DiceLoss的计算方式可能导致模型对预测结果的微小变化产生剧烈反应,从而影响训练的稳定性。
- 对预测和标签的相对位置不敏感:DiceLoss只关注预测结果与真实标签的重叠程度,而不考虑它们的相对位置关系。这可能导致在某些情况下模型无法准确捕捉目标的形状和边界信息。
实现方式
在深度学习框架中(如PyTorch、TensorFlow等),DiceLoss通常作为自定义损失函数实现。用户可以根据上述公式编写相应的代码来计算DiceLoss,并将其作为模型的损失函数进行训练。此外,一些深度学习框架还提供了内置的DiceLoss实现,用户可以直接调用这些实现来简化代码编写过程。
class DiceLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(DiceLoss, self).__init__()
self.size_average = size_average
self.register_buffer('weight', weight)
def forward(self, inputs, targets):
self.num_classes = inputs.size(1)
inputs_soft = F.softmax(inputs, dim=1)
if self.weight is not None and targets.dim() == 4:
weights = self.weight.repeat(targets.size(0), 1, targets.size(2), targets.size(3))
inputs_soft = inputs_soft * weights
dice = 0
for i in range(1, self.num_classes):
input_i = inputs_soft[:, i]
target_i = (targets == i).float()
intersection = (input_i * target_i).sum(dim=(2, 3))
union = input_i.sum(dim=(2, 3)) + target_i.sum(dim=(2, 3))
score_i = (2.0 * intersection / (union + 1e-7)).mean()
dice += score_i
loss = 1 - dice / float(self.num_classes - 1)
return loss
综上所述,DiceLoss是一种在图像分割任务中广泛应用的损失函数,具有对类别不平衡问题鲁棒、计算简单直观等优点。然而,它也存在对小目标敏感、对预测和标签的相对位置不敏感等局限性。在实际应用中,用户需要根据具体任务的需求和数据特点来选择合适的损失函数。
6. IoU(Jaccard Index)
类似于Dice loss,Dice>IoU.
7. Tversky loss
Tversky系数是Dice系数和 Jaccard 系数的一种推广。当设置α=β=0.5,此时Tversky系数就是Dice系数。而当设置α=β=1时,此时Tversky系数就是Jaccard系数。α和β分别控制假阴性和假阳性。通过调整α和β我们可以控制假阳性和假阴性之间的平衡。
def tversky(y_true, y_pred):
y_true_pos = K.flatten(y_true)
y_pred_pos = K.flatten(y_pred)
true_pos = K.sum(y_true_pos * y_pred_pos)
false_neg = K.sum(y_true_pos * (1-y_pred_pos))
false_pos = K.sum((1-y_true_pos)*y_pred_pos)
alpha = 0.7
return (true_pos + smooth)/(true_pos + alpha*false_neg + (1-alpha)*false_pos + smooth)
def tversky_loss(y_true, y_pred):
return 1 - tversky(y_true,y_pred)
def focal_tversky(y_true,y_pred):
pt_1 = tversky(y_true, y_pred)
gamma = 0.75
return K.pow((1-pt_1), gamma)
8. Generalized Dice loss
在使用DICE loss时,对小目标是十分不利的,因为在只有前景和背景的情况下,小目标一旦有部分像素预测错误,那么就会导致Dice大幅度的变动,从而导致梯度变化剧烈,训练不稳定。当病灶分割有多个区域时,一般针对每一类都会有一个DICE,而Generalized Dice index将多个类别的dice进行整合,使用一个指标对分割结果进行量化。
对于两个类别的情况
为每个类别的权重, 为类别l在第n个像素的标准值(GT),而为相应的预测概率值
def generalized_dice_coeff(y_true, y_pred):
Ncl = y_pred.shape[-1]
w = K.zeros(shape=(Ncl,))
w = K.sum(y_true, axis=(0,1,2))
w = 1/(w**2+0.000001)
# Compute gen dice coef:
numerator = y_true*y_pred
numerator = w*K.sum(numerator,(0,1,2,3))
numerator = K.sum(numerator)
denominator = y_true+y_pred
denominator = w*K.sum(denominator,(0,1,2,3))
denominator = K.sum(denominator)
gen_dice_coef = 2*numerator/denominator
return gen_dice_coef
def generalized_dice_loss(y_true, y_pred):
return 1 - generalized_dice_coeff(y_true, y_pred)
9. BCE+Dice loss
在数据较为平衡的情况下有改善作用,但是在数据极度不均衡的情况下,交叉熵损失会在几个训练之后远小于Dice 损失,效果会损失。
def loss(y_true, y_pred):
def dice_loss(y_true, y_pred):
numerator = 2 * tf.reduce_sum(y_true * y_pred, axis=(1,2,3))
denominator = tf.reduce_sum(y_true + y_pred, axis=(1,2,3))
return tf.reshape(1 - numerator / denominator, (-1, 1, 1))
return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)
10. Dice+Focal loss
11. Exponential Logarithmic loss
与加权的DICE LOSS进行对比,此方法觉得这种情况跟不同标签之间的相对尺寸无关,但是可以通过标签频率来进行平衡。其损失公式如下
其中
为标签 k 的出现频率, 这个参数可以减小出现频率较高的类别权重。不同的指数损失如图。新增了四个超参数,增加调参难度。
四、目标检测损失函数
1、l2范数损失
在早期,目标检测领域常用的是l2范数损失,个人理解l2损失是一种以距离为衡量指标的损失函数
像在数据预测时,比如以以前的数据去预测某一地区今年的房价时,用l2损失去回归曲线,就是一种比较好的损失函数。以预估的曲线上的点和真实点,用距离的概念去逼近,去回归,是十分符合直观理解的。
但用做目标检测领域时、如果仅仅以四个距离值来衡量预测框的好坏时,会有如下问题——xt:像素点距离框的上边距离,xb:像素点距离框的下边距离,xl:像素点距离框的左边距离,xr:像素点距离框的右边的距离)
一:这四个距离值并不是相互独立的。这导致会出现,当比如预测框的左下角和和真实框的左下角距离固定时,预测框的右上角只要落在以真实框右上角为圆形,半径为r的圆上,l2损失的值是相同的。
二:l2损失不是归一化的,即一个大的预测框对于此损失函数的影响大概率是要比小的预测框大的
2、IOU损失
由此可见l2损失用来作为预测框的损失函数其实是十分不适合的,于是就引入了IOU这个概念来作为预测框的损失函数。
IOU又名交并比,即是预测框与真实框的交集比上并集的值,即下图,黄色面积与蓝色面积的比值。而IOU loss一般有两种定义
一是上图中的 IOU loss = -ln IOU
二是比较常用的 IOU loss = 1 - IOU
IOU损失解决了l2损失中出现的问题,但IOU也有其问题,其中最主要的问题就是IOU没法去定义两个完全不相交的框直接的损失。IOU对此,直接把其IOU定位0,这样就会导致其梯度为0,无法进行反向传播。
IOU(Intersection over Union)损失是深度学习中用于目标检测的常用损失函数,也被称为Jaccard损失函数。它主要用于评估预测框(Bounding Box)与真实框(Ground Truth Box)之间的重叠程度,进而指导模型的优化过程。以下是关于IOU损失的详细解析:
定义与原理
IOU损失通过计算预测框A和真实框B之间的交并比(Intersection over Union)来反映两者之间的重叠程度。交并比的计算公式为:
IoU=frac∣AcapB∣∣AcupB∣
其中,∣A∩B∣表示预测框A和真实框B的交集面积,∣A∪B∣表示它们的并集面积。IoU的值越高,说明预测框与真实框的重叠程度越高,模型的预测效果越好。
IOU损失函数则是对IoU值进行某种形式的变换,以便用于模型的训练。常见的IOU损失函数形式有:
- LIoU=−ln(IoU):通过对IoU取负对数来定义损失,当IoU接近0时,损失值趋于无穷大,有利于模型在预测框与真实框不相交时进行训练。
- LIoU=1−IoU:另一种常见的形式是直接用1减去IoU来定义损失,这种形式更直观,易于理解。
优点与局限性
优点
- 尺度不变性:IOU损失对目标的尺度不敏感,即无论预测框和真实框的大小如何变化,只要它们的相对位置和形状相似,IoU值就会保持不变。
- 直观性:IoU值能够直观地反映预测框与真实框之间的重叠程度,便于理解和评估模型的预测效果。
局限性
- 无法反映距离信息:当预测框与真实框没有交集时,IoU值为0,此时损失函数无法反映两者之间的距离信息,导致无法进行有效的梯度回传和模型训练。
- 只关注重叠区域:IOU损失只关注预测框与真实框的重叠区域,而忽略了非重叠区域的信息,因此在某些情况下可能无法准确反映预测框与真实框之间的实际差异。
改进与发展
为了克服IOU损失的局限性,研究者们提出了多种改进方法,如GIoU、DIoU和CIoU等损失函数。这些改进方法通过引入额外的惩罚项或考虑更多的几何因素来优化损失函数,从而提高目标检测的准确性和鲁棒性。
应用场景
IOU损失函数广泛应用于目标检测领域,包括但不限于行人检测、车辆检测、人脸检测等场景。它作为评价预测框与真实框之间重叠程度的重要指标,对于提高目标检测的精度和性能具有重要意义。
综上所述,IOU损失是目标检测中不可或缺的一种损失函数,它通过计算预测框与真实框之间的交并比来评估模型的预测效果,并指导模型的优化过程。然而,它也存在一定的局限性,需要在实际应用中结合具体场景和需求进行选择和调整。
3、GIOU损失
为了弥补IOU损失中两个方框没有相交,无法进行反向传播的问题,后来提出了GIOU的概念来解决。
GIOU引入了两个方框中最小外接矩形的概念,来解决两个方框不相交的问题。
下图中,黄色框的面积即为公式中的Ac,u为A框和B框相交的面积
通过公式可以知道
当两个框不相交,且距离很远时IOU=0,GIOU = -1
当两个框重合时,IOU=1,GIOU = 1
而GIOU损失函数即为 GIOU loss = 1 - GIOU
但GIOU也有其问题。
我们通过GIOU的损失函数可以知道,当使用GIOU时,网络会尽力去缩小Ac-u,这样会导致,网络会将预测框变大,去缩小Ac-u,而当预测框和真实框十分相近时,Ac-u会变的很小,那么GIOU就会被退化成IOU,最终结果导致网络的收敛变得十分困难。
GIoU(Generalized Intersection over Union)损失是一种在目标检测中广泛使用的损失函数,旨在解决传统IoU(Intersection over Union)损失函数在预测框与真实框没有交集时无法提供有效梯度的问题。GIoU通过引入预测框和真实框的闭包(即包含这两个框的最小矩形框)区域来改进IoU的计算方式,从而允许在没有交集的情况下也能进行模型的优化。
计算公式
GIoU损失的计算通常基于GIoU值,其计算公式如下:
其中:
- IoU 是传统的交并比,计算预测框 A 和真实框 B 的交集面积与并集面积之比。
- C 是预测框 A 和真实框 B 的闭包区域。
- ∣C−(A∪B)∣ 表示闭包区域 C 中不属于 A 和 B 并集的部分的面积。
然而,在损失函数的上下文中,我们通常会对GIoU值进行某种形式的变换,以便在模型训练过程中进行优化。常见的做法是将 1−GIoU 作为损失值,因为当 GIoU 接近1时(即预测框与真实框的重叠程度很高),1−GIoU 接近0,表示损失很小;反之,当 GIoU 很低时,1−GIoU 较大,表示损失较大。
优点
- 解决无交集梯度问题:GIoU损失能够在预测框与真实框没有交集时提供有效的梯度信息,从而允许模型继续优化。
- 更好的收敛性:通过考虑闭包区域,GIoU损失通常能够引导模型更快地收敛到稳定的训练效果。
局限性
尽管GIoU损失在IoU的基础上进行了改进,但它仍然主要关注重叠区域和闭包区域,而没有充分考虑预测框和真实框之间的其他几何关系(如中心点距离、长宽比、方向等)。这可能导致在某些复杂场景下,模型的预测效果仍然不够理想。
后续发展
为了进一步提高目标检测的准确性和鲁棒性,研究者们提出了DIoU(Distance-IoU)和CIoU(Complete IoU)等更先进的损失函数。这些损失函数不仅考虑了重叠区域和闭包区域,还引入了预测框和真实框之间的中心点距离、长宽比等几何因素,从而提供了更全面的优化信息。
综上所述,GIoU损失是一种有效的目标检测损失函数,它通过引入闭包区域的概念来改进传统的IoU损失函数,解决了无交集情况下的梯度回传问题,并提高了模型的收敛性。然而,在实际应用中,还需要根据具体场景和需求选择合适的损失函数。
3、 DIOU,CIOU损失
从上面几种损失,我们可以知道,一个好的损失函数,应该需要考虑以下几个部分。
1.两个框的重叠部分,2.两个框直接的距离,3.两个框的长宽比
而IOU很好的考虑了两个框的重叠部分,DIOU在IOU的基础上,添加了对于两个框的距离的考虑,而CIOU则在DIOU的基础上添加了两个框长宽比的考虑
其中公式中,
c表示两个框最小外接矩形的对角线的长度
d表示两个框的中心点直接的距离。
a在IOU<0.5时为0,即说明,当两个框较远离时,去考虑两个框的长宽比是没有意义的。
而CIOU通过引入距离这个量,可以帮助网络快速的收敛预测框到真实框的距离,在收敛到IOU>0.5时,通过引入长宽比,来限制框的形状,再次加快了网络的收敛
五、排序损失
1. 排序损失-MarginRankingLoss
torch.nn.MarginRankingLoss(margin=0, size_average=True)
MarginRankingLoss是一种在深度学习中常用的损失函数,特别是在排序任务或对比学习任务中表现出色。以下是对MarginRankingLoss的详细解析:
定义与原理
MarginRankingLoss旨在通过训练使得一个向量的值大于另一个向量,这种顺序关系由目标向量指示。具体来说,当第一个向量(x1)相应位置的值大于第二个向量(x2)且目标值为1时,损失为0;否则,损失将基于两者之间的差值和目标设定的边界(margin)来计算。其数学表达式可以简化为:
其中,y 是目标值,当 x1 应该排在 x2 前面时 y=1,否则 y=−1。margin 是一个超参数,用于定义 x1 和 x2 之间所需的最小差距。
应用场景
MarginRankingLoss常用于以下场景:
- 排序任务:如推荐系统、信息检索等,需要确保正例的得分高于负例的得分,且两者之间的得分差距至少为margin。
- 对比学习任务:特别是在Siamese网络中,通过比较样本对来优化模型,使得相似样本的向量表示更加接近,而不相似样本的向量表示则更加远离。
PyTorch中的实现
在PyTorch框架中,MarginRankingLoss可以通过torch.nn.MarginRankingLoss
类来实现。该类接受一个margin参数(默认为0),以及一个reduction参数(默认为'mean',也可以选择'sum'或'none')来控制损失的计算方式。
以下是一个简单的使用示例:
import torch
import torch.nn as nn
# 创建MarginRankingLoss对象
loss_fn = nn.MarginRankingLoss(margin=1.0)
# 创建输入数据
x1 = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
x2 = torch.tensor([2.0, 1.0, 2.5], requires_grad=True)
target = torch.tensor([1, -1, 1]) # 1表示x1>x2,-1表示x2>=x1
# 计算损失
loss = loss_fn(x1, x2, target)
print(loss.item()) # 输出损失值
# 反向传播(此处省略,实际使用中需要)
优点与注意事项
优点:
- 直观性强:通过margin参数直接控制正负样本之间的最小差距,使得模型在优化过程中有明确的目标。
- 灵活性高:支持不同的reduction方式,可以根据任务需求选择合适的损失计算方式。
注意事项:
- margin的选择:需要根据具体任务和数据分布来选择合适的margin值,过小可能导致模型训练不足,过大则可能增加训练难度。
- 数据预处理:在使用MarginRankingLoss时,需要确保输入数据(x1和x2)已经按照任务需求进行了适当的预处理和排序。
综上所述,MarginRankingLoss是一种在排序任务和对比学习任务中非常有用的损失函数,通过合理设置margin参数和输入数据,可以有效地优化模型性能。
六、 特定任务损失函数
1. KL散度损失-KLDivLoss
torch.nn.KLDivLoss(weight=None, size_average=True)
计算 KL 散度损失。
KL散度常用来描述两个分布的距离,并在输出分布的空间上执行直接回归是有用的。
KLDivLoss,全称为Kullback-Leibler Divergence Loss,中文称为KL散度损失,是一种在深度学习中常用的损失函数。以下是对KLDivLoss的详细解析:
定义与原理
KL散度(Kullback-Leibler Divergence)是一种测量两个概率分布之间差异的方法。在深度学习中,我们可以利用KL散度来衡量两个概率分布之间的距离。而KLDivLoss则是基于KL散度的一种损失函数,用于评估模型预测的概率分布与真实标签的概率分布之间的差异。
具体来说,假设有两个概率分布P和Q,它们的KL散度定义为:
注意,这里的KL散度是不对称的,即DKL(P∣∣Q)=DKL(Q∣∣P)。在KLDivLoss中,我们通常将真实标签的概率分布作为P,将模型预测的概率分布作为Q,然后计算DKL(P∣∣Q)作为损失值。
应用场景
KLDivLoss在深度学习中有着广泛的应用,特别是在以下场景中:
- 分类任务:在分类任务中,我们需要将输入数据分到不同的类别中。每个类别可以看作一个概率分布,而输入数据对应一个概率分布。KLDivLoss可以用来计算输入数据的概率分布与各个类别的概率分布之间的差异,从而指导模型的优化。
- 生成对抗网络(GANs):在GANs中,KLDivLoss常用于评估生成器生成的样本分布与真实数据分布之间的差异,从而指导生成器的训练。
- 变分自编码器(VAEs):在VAEs中,KLDivLoss用于衡量隐变量分布与先验分布之间的差异,以确保隐变量具有良好的泛化能力。
- 知识蒸馏:在知识蒸馏中,KLDivLoss用于衡量教师模型与学生模型之间的输出差异,从而将学生模型的性能提升到接近或超过教师模型的水平。
实现方式
在深度学习框架中(如PyTorch、TensorFlow等),KLDivLoss通常作为内置函数提供。以PyTorch为例,可以使用torch.nn.KLDivLoss
类来实现KLDivLoss。需要注意的是,在使用KLDivLoss时,通常需要将模型输出的原始预测值(即logits)先通过softmax函数转换为概率分布,然后再进行log运算(可以使用torch.nn.functional.log_softmax
函数直接实现),最后将得到的结果作为KLDivLoss的输入。
注意事项
- 输入要求:KLDivLoss的输入需要是概率分布的对数形式,因此在计算之前需要进行相应的转换。
- 目标分布:KLDivLoss中的目标分布(即真实标签的概率分布)通常是已知的,且不需要参与梯度计算(即
requires_grad=False
)。 - 超参数:在某些情况下,可能需要调整KLDivLoss中的超参数(如reduction方式)以适应不同的任务需求。
综上所述,KLDivLoss是一种在深度学习中广泛应用的损失函数,它通过测量两个概率分布之间的差异来指导模型的优化。在实际应用中,需要根据具体任务和数据特点来选择合适的损失函数和参数设置。
参考: