常用的损失函数

Posted 夕阳之后的黑夜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常用的损失函数相关的知识,希望对你有一定的参考价值。

pytorch的源码: torch.nn — PyTorch 1.11.0 documentation

jittor的源码:jittor.nn — Jittor 1.3.2.6 文档 (tsinghua.edu.cn)

paddle的实现:Paddle框架下常用损失函数总结和实现 - 飞桨AI Studio (baidu.com)

目录

一、回归损失函数

1.1 MSE[均方误差Mean Square Error,二次损失Quadratic Loss,L2损失L2 Loss]

1.2 MAE[平均绝对误差Mean Absolute Error,L1损失L1 Loss]   

1.3 Huber Loss[平滑的平均绝对误差]

1.4 Log-Cosh Loss[预测误差的双曲余弦的对数]

1.5 Quantile Loss[分位数损失]

二、分类损失函数

2.1  Cross Entropy Loss(CE)[Log Loss、交叉熵损失]

2.1.1 Binary Cross Entropy Loss

2.1.2  Binary Cross Entropy With Logits Loss

2.2 Focal Loss

2.3 Relative Entropy [相对熵、KL散度(Kullback-Leibler divergence)]

2.4 Exponential Loss [指数损失] 

2.5 Hinge Loss [铰链损失、合页损失]


一、回归损失函数

1.1 MSE[均方误差Mean Square Error,二次损失Quadratic Loss,L2损失L2 Loss]

调用

jittor.nn.MSELoss()

源码

class MSELoss(Module):
    def __init__(self):
        pass
    def execute(self, output, target):
        return mse_loss(output, target)

def mse_loss(output, target):
    return (output-target).sqr().mean()

自定义实现如下

def mse(true, pred): 
    return np.sum((true - pred)**2)

损失范围为0至∞

如果离群点是会影响业务、而且是应该被检测到的异常值,那么我们应该使用MSE

1.2 MAE[平均绝对误差Mean Absolute Error,L1损失L1 Loss]   

调用

jittor.nn.L1Loss()

源码

class L1Loss(Module):
    def __init__(self):
        pass
    def execute(self, output, target):
        return l1_loss(output, target)

def l1_loss(output, target):
    return (output-target).abs().mean()

自定义实现

def mae(true, pred):
    return np.sum(np.abs(true - pred))

损失范围也是0到∞    

如果我们认为离群点仅仅代表数据损坏,那么我们应该选择MAE作为损失

1.3 Huber Loss[平滑的平均绝对误差]

调用

torch.nn.functional.huber_loss(input, target, reduction='mean', delta=1.0)

自定义实现如下

def huber(true, pred, delta):
    loss = np.where(np.abs(true-pred) < delta , 0.5*((true-pred)**2), delta*np.abs(true -     
             pred) - 0.5*(delta**2))
    return np.sum(loss)

当超参数(detail)趋近于0时,Huber Loss接近MAE,

当超参数(detail)趋近于∞时,Huber Loss接近MSE。


1.4 Log-Cosh Loss[预测误差的双曲余弦的对数]

         log(cosh(x))对于小的x来说,其大约等于 (x ** 2) / 2,而对于大的x来说,其大约等于 abs(x) - log(2)。这意味着'logcosh'的作用大部分与均方误差一样。它具有Huber Loss的所有优点,和Huber Loss不同之处在于,其处处二次可导。


自定义实现

def logcosh(input, target):
    loss = np.log(np.cosh(target - input))
    return np.sum(loss)

1.5 Quantile Loss[分位数损失]

        当我们有兴趣预测一个区间而不仅仅是预测一个点时,Quantile Loss函数就很有用。

        所谓的0.9分位数回归,就是希望回归曲线之下能够包含90%的数据点(y),这也是分位数的概念,分位数回归是把分位数的概念融入到普通的线性回归而已。

调用

loss = quantile_loss(pred, gt, 0.5)

实现

import paddle.fluid as fluid

places = fluid.CPUPlace()
exe = fluid.Executor(places)

def quantile_loss(pred, label, gamma):
    dist = fluid.layers.abs(pred - label)
    cond = fluid.layers.greater_than(pred, label)
    ie = fluid.layers.IfElse(cond)
    with ie.true_block():
        loss = ie.input(dist)
        loss = (1 - gamma) * loss
        ie.output(loss)
    with ie.false_block():
        loss = ie.input(dist)
        loss = gamma * loss
        ie.output(loss)
    loss = ie()[0] # 返回的是一个list
    loss = fluid.layers.reduce_mean(loss)
    return loss

quantile_program = fluid.Program()
with fluid.program_guard(quantile_program):
    pred = fluid.data('pred', shape=[4,1], dtype='float32')
    gt = fluid.data('gt', shape=[4,1], dtype='float32')
    loss = quantile_loss(pred, gt, 0.5)

二、分类损失函数

2.1  Cross Entropy Loss(CE)[Log Loss、交叉熵损失]

        交叉熵主要是用来判定实际的输出与期望的输出的接近程度。

        信息量:它是用来衡量一个事件的不确定性的;一个事件发生的概率越大,不确定性越小,则它所携带的信息量就越小。

        熵:它是用来衡量一个系统的混乱程度的,代表一个系统中信息量的总和;信息量总和越大,表明这个系统不确定性就越大。

        也叫Log Loss,两者等价,标签为-1, 1时为log loss, 标签为0, 1时为交叉熵。在做分类(具体几类)训练的时候是非常有用的。

调用

jittor.nn.CrossEntropyLoss(weight=None, ignore_index=None)

源码

class CrossEntropyLoss(Module):
    def __init__(self, weight=None, ignore_index=None):
        self.weight = weight
        self.ignore_index = ignore_index
        
    def execute(self, output, target):
        return cross_entropy_loss(output, target, self.weight, self.ignore_index)

def cross_entropy_loss(output, target, weight=None, ignore_index=None,reduction='sum'):
    if len(output.shape) == 4:
        c_dim = output.shape[1]
        output = output.transpose((0, 2, 3, 1))
        output = output.reshape((-1, c_dim))

    target = target.reshape((-1, ))
    target_weight = jt.ones(target.shape[0], dtype='float32')
    if weight is not None:
        target_weight = weight[target]
    if ignore_index is not None:
        target_weight = jt.ternary(
            target==ignore_index,
            jt.array(0).broadcast(target_weight),
            target_weight
        )
    
    target = target.broadcast(output, [1])
    target = target.index(1) == target
    
    output = output - output.max([1], keepdims=True)
    logsum = output.exp().sum(1).log()
    loss = (logsum - (output*target).sum(1)) * target_weight
    if reduction == 'sum':
        return loss.sum() / target_weight.sum()
    elif reduction == 'mean':
        return loss.mean() / target_weight.mean()
    else:
        return loss / target_weight

2.1.1 Binary Cross Entropy Loss

可视化理解Binary Cross-Entropy - 知乎 (zhihu.com)

调用

jittor.nn.BCELoss(weight=None, size_average=True)

源码

class BCELoss(Module):
    def __init__(self, weight=None, size_average=True):
        self.weight = weight
        self.size_average = size_average
    def execute(self, output, target):
        return bce_loss(output, target, self.weight, self.size_average)

def bce_loss(output, target, weight=None, size_average=True):
    loss = - (target * jt.log(jt.maximum(output, 1e-20)) + (1 - target) *     
             jt.log(jt.maximum(1 - output, 1e-20)))

    if weight is not None:
        loss *= weight
    
    if size_average:
        return loss.mean()
    else:
        return loss.sum()

2.1.2  Binary Cross Entropy With Logits Loss

        有一个(类)损失函数名字中带了with_logits. 而这里的logits指的是,该损失函数已经内部自带了计算logit的操作,无需在传入给这个loss函数之前手动使用sigmoid/softmax将之前网络的输入映射到[0,1]之间 

调用

jittor.nn.BCEWithLogitsLoss(weight=None, pos_weight=None, size_average=True)

torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean',         
                           pos_weight=None)

源码

class BCEWithLogitsLoss(Module):
    def __init__(self, weight=None, pos_weight=None, size_average=True):
        self.pos_weight = pos_weight
        self.weight = weight
        self.size_average = size_average

    def execute(self, output, target):
        return  binary_cross_entropy_with_logits(output,target,
                                                 self.weight,
                                                 self.pos_weight,
                                                 self.size_average)

def binary_cross_entropy_with_logits(output, target, weight=None, pos_weight=None,     
                                     size_average=True):
    # 将输入input张量每个元素的夹紧到区间 [min,max][min,max],并返回结果到一个新张量
    # x = x.maximum(min_v)
    max_val = jt.clamp(-output,min_v=0)
    if pos_weight is not None:
        log_weight = (pos_weight-1)*target + 1
        loss = (1-target)*output+(log_weight*(((-max_val).exp()+(-output -     
                max_val).exp()).log()+max_val))
    else:
        loss = (1-target)*output+max_val+((-max_val).exp()+(-output -max_val).exp()).log()
    if weight is not None:
        loss *=weight

    if size_average:
        return loss.mean()
    else:
        return loss.sum()

实例

loss = nn.BCEWithLogitsLoss()
input = torch.randn(3, requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(input, target)
output.backward()

2.2 Focal Loss

        Focal loss的思路就是给损失加上权值,使当模型对样本的判断错误时,损失的权重更大,从而增加”困难样本“对模型的影响

#二分类情况:分别计算y_true为1处和y_true为0处的损失,相加
from keras import backend as K 
def focal_loss(gamma=2., alpha=0.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
#多分类情况:计算y_true为1处的损失(不考虑y_true为0处的y_pred值)
from keras import backend as K 
def focal_loss(gamma=2.): 
    def focal_loss_fixed(y_true, y_pred): 
        pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred)) 
        return -K.sum( K.pow(1. - pt_1, gamma) * K.log(pt_1)) 
    return focal_loss_fixed

2.3 Relative Entropy [相对熵、KL散度(Kullback-Leibler divergence)]

        信息熵代表的是随机变量或整个系统的不确定性,熵越大,随机变量或系统的不确定性就越大。根据真实分布,我们能够找到一个最优策略,以最小的代价消除系统的不确定性,而这个代价大小就是信息熵,记住,信息熵衡量了系统的不确定性,而我们要消除这个不确定性,所要付出的【最小努力】(猜题次数、编码长度等)的大小就是信息熵。

         交叉熵,其用来衡量在给定的真实分布下,使用非真实分布所指定的策略消除系统的不确定性所需要付出的努力的大小。交叉熵越低,这个策略就越好,最低的交叉熵也就是使用了真实分布所计算出来的信息熵。因为此时交叉熵 = 信息熵。这也是为什么在机器学习中的分类算法中,我们总是最小化交叉熵,因为交叉熵越低,就证明由算法所产生的策略最接近最优策略,也间接证明我们算法所算出的非真实分布越接近真实分布。

         相对熵,其用来衡量两个取值为正的函数或概率分布之间的差异。现在,假设我们想知道某个策略和最优策略之间的差异,我们就可以用相对熵来衡量这两者之间的差异。即,相对熵 = 某个策略的交叉熵 - 信息熵(根据系统真实分布计算而得的信息熵,为最优策略)。

调用

torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean', log_target=False)

torch.nn.functional.kl_div(input, target, size_average=None, reduce=None, reduction='mean',         
                           log_target=False)

实例

kl_loss = nn.KLDivLoss(reduction="batchmean")

# 输入应该是对数空间中的分布
input = F.log_softmax(torch.randn(3, 5, requires_grad=True))
# 对一批分布进行抽样。 通常这将来自数据集
target = F.softmax(torch.rand(3, 5))
output = kl_loss(input, target)

log_target = F.log_softmax(torch.rand(3, 5))
output = kl_loss(input, log_target, log_target=True)

2.4 Exponential Loss [指数损失] 

        对离群点、噪声非常敏感。主要用于 Adaboost 集成学习 算法,它是前向分步加法算法的特例,是一个加和模型。

        指数损失是0-1损失函数的一种代理函数。

        代码实现还未找到(待更新)。

2.5 Hinge Loss [铰链损失、合页损失]

        在机器学习中常用于"最大间隔(maximum-margin)"的分类任务中,如支持向量机SVM。尽管不可微,但它是一个凸函数,因此可以轻而易举地使用机器学习领域中常用的凸优化器。

        对于当前的一组分数,对应于不同的类别,我们希望属于真实类别的那个分数比其他的类别的分数要高,并且最好要高出一个margin,这样才是安全的。反映在这个函数中,就是0的那一项的取值范围,Syi表示的是xi样本的真实类别所得到的分数,而Sj指的是其他的类别的分数,如果真实类别的分数是最大的,且大一个margin 1,那么就表示满意,也就是说不惩罚,这一项的loss为0。如果不满足这一条件,我们用下面的一段,也就是Sj - Syi + 1, 1在这里是一个常数,可以使得函数连续起来,这个Sj - Syi表示,Syi,也就是真实样本的分数比别的少的越多,我们越不满意。

代码实现:kazuto1011/svm-pytorch: Linear SVM with PyTorch (github.com)

代码实现

output = model(x).squeeze()
weight = model.weight.squeeze()

loss = torch.mean(torch.clamp(1 - y * output, min=0))
loss += args.c * (weight.t() @ weight) / 2.0

10个常用的损失函数及Python代码实现

本文深入理解并详细介绍了10个常用的损失函数及Python代码实现。点赞收藏吧~

什么是损失函数?

损失函数是一种衡量模型与数据吻合程度的算法。损失函数测量实际测量值和预测值之间差距的一种方式。损失函数的值越高预测就越错误,损失函数值越低则预测越接近真实值。对每个单独的观测(数据点)计算损失函数。将所有损失函数(loss function)的值取平均值的函数称为代价函数(cost function),更简单的理解就是损失函数是针对单个样本的,而代价函数是针对所有样本的。

损失函数与度量指标

一些损失函数也可以被用作评价指标。但是损失函数和度量指标(metrics)有不同的目的。虽然度量指标用于评估最终模型并比较不同模型的性能,但损失函数在模型构建阶段用作正在创建的模型的优化器。损失函数指导模型如何最小化误差。

也就是说损失函数是知道模型如何训练的,而度量指标是说明模型的表现的

为什么要用损失函数?

由于损失函数测量的是预测值和实际值之间的差距,因此在训练模型时可以使用它们来指导模型的改进(通常的梯度下降法)。在构建模型的过程中,如果特征的权重发生了变化得到了更好或更差的预测,就需要利用损失函数来判断模型中特征的权重是否需要改变,以及改变的方向。

我们可以在机器学习中使用各种各样的损失函数,这取决于我们试图解决的问题的类型、数据质量和分布以及我们使用的算法,下图为我们整理的10个常见的损失函数:

回归问题

1、均方误差(MSE)

均方误差是指所有预测值和真实值之间的平方差,并将其平均值。常用于回归问题。

def MSE (y, y_predicted):
   sq_error = (y_predicted - y) ** 2
   sum_sq_error = np.sum(sq_error)
   mse = sum_sq_error/y.size
   return mse

2、平均绝对误差(MAE)

作为预测值和真实值之间的绝对差的平均值来计算的。当数据有异常值时,这是比均方误差更好的测量方法。

def MAE (y, y_predicted):
   error = y_predicted - y
   absolute_error = np.absolute(error)
   total_absolute_error = np.sum(absolute_error)
   mae = total_absolute_error/y.size
   return mae

3、均方根误差(RMSE)

这个损失函数是均方误差的平方根。如果我们不想惩罚更大的错误,这是一个理想的方法。

def RMSE (y, y_predicted):
   sq_error = (y_predicted - y) ** 2
   total_sq_error = np.sum(sq_error)
   mse = total_sq_error/y.size
   rmse = math.sqrt(mse)
   return rmse

4、平均偏差误差(MBE)

类似于平均绝对误差但不求绝对值。这个损失函数的缺点是负误差和正误差可以相互抵消,所以当研究人员知道误差只有一个方向时,应用它会更好。

def MBE (y, y_predicted):
   error = y_predicted -  y
   total_error = np.sum(error)
   mbe = total_error/y.size
   return mbe

5、Huber损失

Huber损失函数结合了平均绝对误差(MAE)和均方误差(MSE)的优点。这是因为Hubber损失是一个有两个分支的函数。一个分支应用于符合期望值的MAE,另一个分支应用于异常值。Hubber Loss一般函数为:

这里的

def hubber_loss (y, y_predicted, delta)
   delta = 1.35 * MAE
   y_size = y.size
   total_error = 0
   for i in range (y_size):
      erro = np.absolute(y_predicted[i] - y[i])
      if error < delta:
         hubber_error = (error * error) / 2
      else:
         hubber_error = (delta * error) / (0.5 * (delta * delta))
      total_error += hubber_error
   total_hubber_error = total_error/y.size
   return total_hubber_error

二元分类

6、最大似然损失(Likelihood Loss/LHL)

该损失函数主要用于二值分类问题。将每一个预测值的概率相乘,得到一个损失值,相关的代价函数是所有观测值的平均值。让我们用以下二元分类的示例为例,其中类别为[0]或[1]。如果输出概率等于或大于0.5,则预测类为[1],否则为[0]。输出概率的示例如下:

[0.3 , 0.7 , 0.8 , 0.5 , 0.6 , 0.4]

对应的预测类为:

[0 , 1 , 1 , 1 , 1 , 0]

而实际的类为:

[0 , 1 , 1 , 0 , 1 , 0]

现在将使用真实的类和输出概率来计算损失。如果真类是[1],我们使用输出概率,如果真类是[0],我们使用1-概率:

((1–0.3)+0.7+0.8+(1–0.5)+0.6+(1–0.4)) / 6 = 0.65

Python代码如下:

def LHL (y, y_predicted):
   likelihood_loss = (y * y_predicted) + ((1-y) * (y_predicted))
   total_likelihood_loss = np.sum(likelihood_loss)
   lhl = - total_likelihood_loss / y.size
   return lhl

7、二元交叉熵(BCE)

这个函数是对数的似然损失的修正。对数列的叠加可以惩罚那些非常自信但是却错误的预测。二元交叉熵损失函数的一般公式为:

让我们继续使用上面例子的值:

  1. 输出概率= [0.3、0.7、0.8、0.5、0.6、0.4]

  2. 实际的类= [0,1,1,0,1,0]

  • (0 . log (0.3) + (1–0) . log (1–0.3)) = 0.155

  • (1 . log(0.7) + (1–1) . log (0.3)) = 0.155

  • (1 . log(0.8) + (1–1) . log (0.2)) = 0.097

  • (0 . log (0.5) + (1–0) . log (1–0.5)) = 0.301

  • (1 . log(0.6) + (1–1) . log (0.4)) = 0.222

  • (0 . log (0.4) + (1–0) . log (1–0.4)) = 0.222

那么代价函数的结果为:

(0.155 + 0.155 + 0.097 + 0.301 + 0.222 + 0.222) / 6 = 0.192

Python的代码如下:

def BCE (y, y_predicted):
   ce_loss = y*(np.log(y_predicted))+(1-y)*(np.log(1-y_predicted))
   total_ce = np.sum(ce_loss)
   bce = - total_ce/y.size
   return bce

8、Hinge Loss 和 Squared Hinge Loss (HL and SHL)

Hinge Loss被翻译成铰链损失或者合页损失,这里还是以英文为准。

Hinge Loss主要用于支持向量机模型的评估。错误的预测和不太自信的正确预测都会受到惩罚。所以一般损失函数是:

这里的t是真实结果用[1]或[-1]表示。

使用Hinge Loss的类应该是[1]或-1。为了在Hinge loss函数中不被惩罚,一个观测不仅需要正确分类而且到超平面的距离应该大于margin(一个自信的正确预测)。如果我们想进一步惩罚更高的误差,我们可以用与MSE类似的方法平方Hinge损失,也就是Squared Hinge Loss。

如果你对SVM比较熟悉,应该还记得在SVM中,超平面的边缘(margin)越高,则某一预测就越有信心。如果这块不熟悉,则看看这个可视化的例子:

如果一个预测的结果是1.5,并且真正的类是[1],损失将是0(零),因为模型是高度自信的。

loss= Max (0,1 - 1* 1.5) = Max (0, -0.5) = 0

如果一个观测结果为0(0),则表示该观测处于边界(超平面),真实的类为[-1]。损失为1,模型既不正确也不错误,可信度很低。

如果一次观测结果为2,但分类错误(乘以[-1]),则距离为-2。损失是3(非常高),因为我们的模型对错误的决策非常有信心(这个是绝不能容忍的)。

python代码如下:

#Hinge Loss 
def Hinge (y, y_predicted): 
   hinge_loss = np.sum(max(0 , 1 - (y_predicted * y))) 
   return hinge_loss 

#Squared Hinge Loss 
def SqHinge (y, y_predicted): 
   sq_hinge_loss = max (0 , 1 - (y_predicted * y)) ** 2 
   total_sq_hinge_loss = np.sum(sq_hinge_loss) 
   return total_sq_hinge_loss

多分类

9、交叉熵(CE)

在多分类中,我们使用与二元交叉熵类似的公式,但有一个额外的步骤。首先需要计算每一对[y, y_predicted]的损失,一般公式为:

如果我们有三个类,其中单个[y, y_predicted]对的输出是:

这里实际的类3(也就是值=1的部分),我们的模型对真正的类是3的信任度是0.7。计算这损失如下:

为了得到代价函数的值,我们需要计算所有单个配对的损失,然后将它们相加最后乘以[-1/样本数量]。代价函数由下式给出:

使用上面的例子,如果我们的第二对:

那么成本函数计算如下:

使用Python的代码示例可以更容易理解:

def CCE (y, y_predicted): 
   cce_class = y * (np.log(y_predicted)) 
   sum_totalpair_cce = np.sum(cce_class) 
   cce = - sum_totalpair_cce / y.size 
   return cce

10、Kullback-Leibler 散度 (KLD)

又被简化称为KL散度,它类似于分类交叉熵,但考虑了观测值发生的概率。如果我们的类不平衡,它特别有用。

def KL (y, y_predicted): 
   kl = y * (np.log(y / y_predicted)) 
   total_kl = np.sum(kl) 
   return total_kl

以上就是常见的10个损失函数,希望对你有所帮助。

以上是关于常用的损失函数的主要内容,如果未能解决你的问题,请参考以下文章

Pytorch常用的交叉熵损失函数CrossEntropyLoss()详解

Pytorch常用的交叉熵损失函数CrossEntropyLoss()详解

Deeplearning常用的损失函数

常用的损失函数

无法在pytorch python中使用多目标损失函数

Pytorch常用损失函数nn.BCEloss();nn.BCEWithLogitsLoss();nn.CrossEntropyLoss();nn.L1Loss(); nn.MSELoss();(代码