专栏首页AI的那些事儿深度学习中常见的损失函数(摘自我的书)

深度学习中常见的损失函数(摘自我的书)

在深度学习分类任务中,我们经常会使用到损失函数,今天我们就来总结一下深度学习中常见的损失函数。

0-1损失函数

在分类问题中,可以使用函数的正负号来进行模式判断,函数值本身大小并不重要,该函数衡量的是预测值与真实值的符号是否相同,具体形式如下所示:

其等价于下述函数:

由于0-1损失函数只取决于正负号,是一个非凸的函数,在求解过程中,存在很多的不足,通常在实际应用中使用其替代函数。

对数(Log)损失函数

Log损失函数是0-1损失函数的一种替代函数,其形式如下:

运用Log损失函数的典型分类器是logistic(逻辑)回归算法。为什么逻辑回归不用平方损失呢?原因在于平方损失函数是线性回归在假设样本是高斯分布的条件下推导得到的(为什么假设高斯分布?其实就是依据中心极限定理)。而逻辑回归的推导中,它假设样本服从于伯努利分布(0-1分布),然后求得满足该分布的似然函数,接着求取对数等(Log损失函数中采用log就是因为求解过中使用了似然函数,为了求解方便而添加log,因为添加log并不改变其单调性)。但逻辑回归并没有极大化似然函数,而是转变为最小化负的似然函数,因此有了上式。

已知逻辑函数(sigmoid函数)为:

可以得到逻辑回归的Log损失函数:

则全体样本的经验风险函数为:

该式就是sigmoid函数的交叉熵,这也是上文说的在分类问题上,交叉熵的实质是对数似然函数。在深度学习中更普遍的做法是将softmax作为最后一层,此时常用的仍是对数似然损失函数,如下所示:

该式其实是式(1)的推广,正如softmax是sigmoid的多类别推广一样,在TensorFlow里面根据最后分类函数softmax和sigmoid就分为softmax交叉熵以及sigmoid的交叉熵,并对这两个功能进行统一封装。

先看tf.nn.sigmoid_cross_entropy_with_logits(logits,targets)函数,它的实现和之前的交叉熵算法定义是一样的,也是TensorFlow最早实现的交叉熵算法。这个函数的输入是logits和targets,logits就是神经网络模型中的W*X矩阵,注意不需要经过sigmoid,因为在函数中会对其进行sigmoid激活,而targets的shape和logtis相同,就是正确的label值。其计算过程大致如下:

tf.nn.softmax_cross_entropy_with_logits(logits,targets)同样是将softmax和交叉熵计算放到一起了,但是需要注意的是,每个样本只能属于一个类别,即要求分类结果是互斥的,因此该函数只适合单目标的二分类或多分类问题。补充一点,对于多分类问题,例如我们分为5类,并且将其人工编码为0,1,2,3,4,因为输出值是5维的特征,因此需要人工做onehot enconding,即分别编码为00001,00010,00100,01000,10000,才能作为该函数的输入。理论上不做onehot encoding也可以,做成和为1的概率分布也可以,但需要保证和为1,否则TensorFlow会检查这些参数,提醒用户更改。

TensorFlow还提供了一个softmax_cross_entropy_with_logits的易用版本,tf.nn.sparse_softmax_cross_entropy_with_logits(),除了输入参数不同,作用和算法实现都是一样的。softmax_cross_entropy_with_logits的输入必须是类似onehot encoding的多维特征,但像CIFAR-10、ImageNet和大部分分类场景都只有一个分类目标,label值都是从0编码的整数,每次转成onehot encoding比较麻烦,TensorFlow为了简化用户操作,在该函数内部高效实现类似onehot encoding,第一个输入函数和前面一样,shape是[batch_size,num_classes],第二个参数以前必须也是[batch_size,num_classes]否则无法做交叉熵,而这里将其改为[batch_size],但值必须是从0开始编码的int32或int64,而且值的范围是[0,num_class)。如果我们从1开始编码或者步长大于1,则会导致某些label值超过范围,代码会直接报错退出。其实如果用户已经做了onehot encoding,那就可以不使用该函数。

还有一个函数tf.nn.weighted_cross_entropy_with_logits(),是sigmoid_cross_entropy_with_logits的拓展版,输入和实现两者类似,与后者相比,多支持一个pos_weight参数,目的是可以增加或减小正样本在算交叉熵时的loss.其计算原理如下:

还有一个计算交叉熵的函数,sequence_loss_by_example (logits,targets,weights),用于计算所有examples(假设一句话有n个单词,一个单词及单词所对应的label就是一个example,所有examples就是一句话中所有单词)的加权交叉熵损失,logits的shape为[batch_size,num_decoder_symbols],返回值是一个1D float类型的tensor,尺寸为batch_size,其中每一个元素代表当前输入序列example的交叉熵。另外,还有一个与之类似的函数sequence_loss,它对sequence_loss_by_example函数的返回结果进行了一个tf.reduce_sum运算。

值得一提的是,当最后分类函数是sigmoid和softmax时,不采用平方损失函数除上文中提到的样本假设分布不同外,还有一个原因是如果采用平方损失函数,则模型权重更新非常慢,假设采用平方损失函数如下式所示:

采用梯度下降算法调整参数的话,则有

而如果采用交叉熵或者说对数损失函数,则参数更新梯度变为:

为什么一开始我们说log损失函数也是0-1损失函数的一种替代函数,因为log损失函数其实也等价于如下形式:

Hinge损失函数

Hinge损失函数也是0-1函数的替代函数,具体形式如下:

指数损失

具体形式如下:

这也是0-1函数的一种替代函数,主要用于AdaBoost算法。

感知机损失

这也是0-1函数的一种替代函数,具体形式如下:

运用感知机损失的典型分类器是感知机算法,感知机算法只需对每个样本判断其是否分类正确,只记录分类错误的样本,类似hinge损失,不同之处在于,hinge损失对判定边界附近的点的惩罚力度较高,而感知损失只要样本的类别判定正确即可,而不需要其离判别边界的距离,这样的变化使得其比hinge损失简单,但是泛化能力没有hinge损失强。

这几种损失函数形式如下,可以看出,除了0-1函数,其他函数都可认为是0-1函数的替代函数,目的在于使函数更平滑,提高计算性,如图所示。

平方(均方)损失函数

具体形式为:

平方损失函数较多应用于回归任务,它假设样本和噪声都是服从高斯分布的,是连续的。它有几个特点:计算简单方便;欧式距离是一种很好的相似度度量标准;在不同的表示域变换后特征性质不变。因此平方损失函数也是一种应用较多的形式。

在TensorFlow中计算平方损失,一般采用tf.pow(x,y),其返回值是x^y。举例来说:

loss = tf.reduce_mean(tf.pow(y-y_, 2))

绝对值损失函数

具体形式为:

绝对值损失函数与平方损失函数类似,不同之处在于平方损失函数更平滑,计算更简便,因此实际应用中更多地使用平方损失函数。

以上主要讲了损失函数的常见形式,在神经网络中应用较多的是对数损失函数(交叉熵)和平方损失函数。可以看出,损失函数的选择与模型是密切相关的,如果是square loss,就是最小二乘了,如果是hinge loss,就是SVM了;如果是exp-loss,那就是boosting了;如果是log loss,那就是logistic regression了,等等。不同的loss函数,具有不同的拟合特性,就需要具体问题具体分析。

自定义损失函数

Tensorflow不仅支持经典的损失函数,还可以优化任意的自定义损失函数。自定义的损失函数原则上满足上文中讲的两个条件即可。TensorFlow提供了很多计算函数,基本可以满足自定义损失函数可能会用到的计算操作。举例来说,预测商品销量时,假设商品成本为1元,销售价为10,如果预测少一个,意味着少挣9元,但预测多一个,意味只损失1元,希望利润最大化,因此损失函数不能采用均方误差,需要自定义损失函数,定义如下:

在TensorFlow中可以这样定义:其中tf.greater()用于比较输入两个张量每个元素的大小,并返回比较结果。Tf.select()会根据第一个输入是否为true,来选择第二个参数,还是第三个参数,类似三目运算符。

loss=tf.reduce_sum(tf.select(tf.greater(v1,v2),a*(v1-v2),b*(v2-v1)))

本文分享自微信公众号 - AI的那些事儿(clever-ai),作者:黄鸿波

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深度学习中常见的激活函数总结(摘自我写的书)

    Sigmoid函数,也称S曲线函数,是使用范围最广的一类激活函数,具有指数函数形状,在物理意义上最为接近生物神经元,在生物学中也是常见的S型函数,又称为S型生长...

    黄鸿波
  • 见微知著,你真的搞懂Google的Wide&Deep模型了吗?

    为什么在Google的Wide&Deep模型中,要使用带L1正则化项的FTRL作为wide部分的优化方法,而使用AdaGrad作为deep部分的优化方法?

    黄鸿波
  • (经验技巧)Python中与并发的并行

    python中的并发是同时发生的事情由线程,任务,进程调用(实际上还是按顺序运行的一系列指令)。宏观上看,线程,任务和进程是相同的,细节上他们代表不同的东西。事...

    黄鸿波
  • Python 3 函数

    函数能提高应用的模块性,和代码的重复利用率Python提供了许多内建函数,比如print()。但也可以自己创建函数,这被叫做用户自定义函数。

    用户6184845
  • 码如其人,小老弟,你能写一手漂亮的Python函数吗

    好的 Python 函数与蹩脚 Python 函数的区别是什么?「好」函数的定义之多让人惊讶。从我们的目的出发,我会把好的 Python 函数定义为符合以下清单...

    一墨编程学习
  • python语法之函数

    函数:   将特定功能代码编写在一个函数里   便于阅读和复用   对一组表达特定功能表达式的封装   使程序模块化 python内置函数:   ...

    py3study
  • Python之函数学习(八)

    python语言,即可以进行函数式的编程的语言,又是可以进行面向对象编程的语言,所谓函数,简单的理解就是将一些语句集合到一起,这样可以在程序中多次的...

    无涯WuYa
  • 写 Python 代码不可不知的函数式编程技术

    近来,越来越多人使用函数式编程(functional programming)。因此,很多传统的命令式语言(如 Java 和 Python)开始支持函数式编程技...

    OpenCV学堂
  • 五撩Python

    重复,再重复,你就是专家。 --曾子 1、起手 来说函数。 2、函数 函数就是整理好的一堆可重用的代码,有输入,然后就有输出。 比如我们计算折扣,输入是消费者的...

    企鹅号小编
  • 写 Python 代码不可不知的函数式编程技术

    近来,越来越多人使用函数式编程(functional programming)。因此,很多传统的命令式语言(如 Java 和 Python)开始支持函数式编程技...

    CDA数据分析师

扫码关注云+社区

领取腾讯云代金券