参考文章:
https://blog.csdn.net/zhangdongren/article/details/83377272
https://blog.csdn.net/yukinoai/article/details/84197930
https://www.jianshu.com/p/b3dbf913b421
https://www.jiqizhixin.com/articles/2018-06-21-3
目录
本文只是对上面几篇文章的总结和整理。
损失函数(Loss Function):针对单个样本,衡量单个样本的预测值y1与真实值y之间的差距。
损失函数:可分为经验风险损失函数和结构风险损失函数,经验风险损失函数指预测结果和实际结果的差别,结构风险损失函数是在经验损失函数上加上正则项。
代价函数(Cost Function):针对多个样本,衡量多个样本的预测值与真实值
之间的差距。
目标函数(Objective Function):梯度下降等优化算法就是针对目标函数来进行的。其实代价函数就可以是一种目标函数,换句话说,目标函数直接选用代价函数。但是我们经常给代价函数添加一个正则项,最终作为模型的目标函数。
一,分类代价函数
1.1.二次代价函数(quadratic cost)
其中,C表示代价函数,x表示样本,y表示实际值,a表示输出值,n表示样本的总数。为简单起见,使用一个样本为例进行说明,此时代价函数为:
其中a=σ(z),,σ()是激活函数。
TensorFlow中:
tf.reduce_mean(tf.square(y-prediction))
二次代价函数的局限性
首先来说一下二次代价函数的局限性,看下面这张图:
假设现在使用的激活函数是sigmoid,并且代价函数是二次代价函数,我们收敛得目标是1,那么A点的梯度比较大,B点由于离目标比较近梯度小,所以是合理的;但是如果我们的收敛目标是0,那么A点离目标比较近,所以梯度应该要小于B点,但是图中明明是A点的梯度大于B点,这是不合理的。
1.2.交叉熵代价函数(cross-entropy)
正是由于二次代价函数的这个局限性,我们需要引入交叉熵。看下面公式:
其中,C表示代价函数,x表示样本,y表示实际值,n表示样本的总数,a表示输出值,且a=σ(z),,σ()是激活函数,σ()的导数σ'()=σ()*(1-σ())。
对w和b进行求导后得到下面的式子:
由上面偏导数公式可知,w与b的调整与σ'()无关,另外,梯度公式中的σ(z)-y表示预测值和实际值得误差。所以当误差越大时,梯度就越大,参数w和b的调整就越快,训练速度也就越快。
如果输出神经元是线性的,那么二次代价函数就是一种合适的选择。如果输出神经元是S型函数【(Sigmoid function)是BP神经网络中常用的非线性作用函数,即sigmoid函数】,那么比较适合用交叉熵代价函数。
TensorFlow中:
tf.nn.sigmoid_cross_entropy_with_logits()#来表示跟sigmoid搭配使用的交叉熵。
1.3.对数似然代价函数(log-likelihood cost)
先说softmax,什么是softmax? 看下面公式:
在机器学习尤其是深度学习中,softmax是个非常常用而且比较重要的函数,尤其在多分类的场景中使用广泛。他把一些输入映射为0-1之间的实数,并且归一化保证和为1,这个形式跟一个离散的概率分布状态非常相似,所以可以强行解释成概率。
另外,如果把原始张量看做一个layer的输入,softmax也可以考虑作为该layer的输出。与针对每个神经元进行S型函数激活不同,softmax的每个输出元素不止与对应的神经元输入有关,而是与整个layer的输入有关。
对数似然代价函数的公式:
其中,表示第i个神经元的输出值,
表示第i个神经元对应的真实值,取值为0或1,一般y是one-hot形式,这时C=-
*log
(假设
=1,其他为0)。
所以说,softmax、对数似然代价、one-hot三者是绝配。
一般 输出层神经元是sigmoid函数,可以采用交叉熵代价函数;对数似然代价函数常用来作为softmax回归的代价函数。而深度学习中更普遍的做法是将softmax作为最后一层,此时常用的代价函数是对数似然代价函数。对数似然代价函数与softmax的组合和交叉熵与sigmoid函数的组合非常相似。对数似然代价函数在二分类时可以化简为交叉熵代价函数的形式。
TensorFlow中:
tf.nn.softmax_cross_entropy_with_logits()#来表示跟softmax搭配使用的交叉熵。
分类代价函数总结:
- 二次代价函数:输出神经元是线性的就选用该代价函数
- 交叉熵代价函数:输出神经元是S型的就选用该代价函数,例如sigmoid激活函数中(tf.nn.sigmoid_cross_entropy_with_logits())
- 对数似然代价函数:输出神经元是softmax的时候选用(tf.nn.softmax_cross_entropy_with_logits())
二,回归代价函数
2.1.均方误差MSE(L2损失)
均方误差(Mean Square Error,MSE)是模型预测值f(x) 与真实样本值y 之间差值平方的平均值,其公式如下:
其中,yi和f(xi)分别表示第i个样本的真实值及其对应的预测值,n为样本的个数。忽略下标i ,设n=1,以f(x)−y为横轴,MSE的值为纵轴,得到函数的图形如下:
MSE的函数曲线光滑、连续,处处可导,便于使用梯度下降算法,是一种常用的损失函数。而且,随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。
当y和f(x)也就是真实值和预测值的差值大于1时,会放大误差;而当差值小于1时,则会缩小误差,这是平方运算决定的。MSE对于较大的误差(>1)给予较大的惩罚,较小的误差(<1)给予较小的惩罚。也就是说,对离群点比较敏感,受其影响较大。
如果样本中存在离群点,MSE会给离群点更高的权重,这就会牺牲其他正常点数据的预测效果,最终降低整体的模型性能。 如下图:
可见,使用 MSE 损失函数,受离群点的影响较大,如上图所示,虽然样本中只有5个离群点,但是拟合的直线还是比较偏向于离群点。
python实现MSE:
def mse(true, pred):
return np.sum((true - pred)**2)
2.2.平均绝对值误差MAE(L1损失)
平均绝对误差(Mean Absolute Error,MAE) 是指模型预测值f(x)和真实值y之间距离的平均值,其公式如下:
忽略下标i ,设n=1,以f(x)−y为横轴,MAE的值为纵轴,得到函数的图形如下:
MAE曲线连续,但是在y−f(x)=0处不可导。而且 MAE 大部分情况下梯度都是相等的,这意味着即使对于小的损失值,其梯度也是大的。这不利于函数的收敛和模型的学习。但是,无论对于什么样的输入值,都有着稳定的梯度,不会导致梯度爆炸问题,具有较为稳健性的解。
相比于MSE,MAE有个优点就是,对于离群点不那么敏感。因为MAE计算的是误差y−f(x)的绝对值,对于任意大小的差值,其惩罚都是固定的。
针对上面带有离群点的数据,MAE的效果要好于MSE,如下图所示:
显然,使用 MAE 损失函数,受离群点的影响较小,拟合直线能够较好 地表征正常数据的分布情况。
python实现MAE:
def mae(true, pred):
return np.sum(np.abs(true - pred))
MSE和MAE的选择
-
从梯度的求解以及收敛上,MSE是由于MAE的。MSE处处可导,而且梯度值也是动态变化的,能够快速的收敛;而MAE在0点处不可导,且其梯度保持不变。对于很小的损失值其梯度也很大,在深度学习中,就需要使用变化的学习率,在损失值很小时降低学习率。
-
对离群(异常)值得处理上,MAE要明显好于MSE。
如果离群点(异常值)需要被检测出来,则可以选择MSE作为损失函数;如果离群点只是当做受损的数据处理,则可以选择MAE作为损失函数。
总之,MAE作为损失函数更稳定,并且对离群值不敏感,但是其导数不连续,求解效率低。另外,在深度学习中,收敛较慢。MSE导数求解速度高,但是其对离群值敏感,不过可以将离群值的导数设为0(导数值大于某个阈值)来避免这种情况。
在某些情况下,上述两种损失函数都不能满足需求。例如,若数据中90%的样本对应的目标值为150,剩下10%在0到30之间。那么使用MAE作为损失函数的模型可能会忽视10%的异常点,而对所有样本的预测值都为150。这是因为模型会按中位数来预测。而使用MSE的模型则会给出很多介于0到30的预测值,因为模型会向异常点偏移。
这种情况下,MSE和MAE都是不可取的,简单的办法是对目标变量进行变换,或者使用别的损失函数,例如:Huber,Log-Cosh以及分位数损失等
2.3.Smooth L1 Loss
在Faster R-CNN以及SSD中对边框的回归使用的损失函数都是Smooth L1作为损失函数。
其中,x=f(xi)−yi 为真实值和预测值的差值。
Smooth L1能从两个方面限制梯度:
- 当预测框与 ground truth 差别过大时,梯度值不至于过大;
- 当预测框与 ground truth 差别很小时,梯度值足够小。
对比L1 Loss 、 L2 Loss、Smooth L1 Loss,其中x为预测框与groud truth之间的差异x=f(xi)−yi :
(1)
(2)
(3)
上面损失函数对xx的导数为:
(4)
(5)
(6)
上面导数可以看出:
-
L2损失,根据公式(4),当x增大时,L2的损失也增大。 在训练初期,预测值与 groud truth 差异过于大时,损失函数对预测值的梯度十分大,这会导致训练不稳定。
-
L1损失,根据公式(5),L1对x的导数为常数,在训练的后期,预测值与ground truth差异很小时,L1的导数的绝对值仍然为1,而 learning rate 如果不变,损失函数将在稳定值附近波动,难以继续收敛以达到更高精度。
-
Smooth L1损失,根据公式(6),Smotth L1在x较小时,对x的梯度也会变小。 而当x较大时,对x的梯度的上限为1,也不会太大以至于破坏网络参数。SmoothL1完美的避开了L1和L2作为损失函数的缺陷。
L1 Loss ,L2 Loss以及SmoothL1 放在一起的函数曲线对比:
从上面可以看出,该函数实际上就是一个分段函数,在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题,在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题。
python实现SmoothL1:
def smooth_l1_loss(y_true, y_pred):
diff = K.abs(y_true - y_pred)
less_than_one = K.cast(K.less(diff, 1.0), "float32")
loss = (less_than_one * 0.5 * diff**2) + (1 - less_than_one) * (diff - 0.5)
return loss
对于大多数CNN网络,我们一般是使用L2-loss而不是L1-loss,因为L2-loss的收敛速度要比L1-loss要快得多。
对于边框预测回归问题,通常也可以选择平方损失函数(L2损失),但L2范数的缺点是当存在离群点(outliers)的时候,这些点会占loss的主要组成部分。比如说真实值为1,预测10次,有一次预测值为1000,其余次的预测值为1左右,显然loss值主要由1000决定。所以FastRCNN采用稍微缓和一点绝对损失函数(smooth L1损失),它是随着误差线性增长,而不是平方增长。
Smooth L1 和 L1 Loss 函数的区别在于,L1 Loss 在0点处导数不唯一,可能影响收敛。Smooth L1的解决办法是在 0 点附近使用平方函数使得它更加平滑。
Smooth L1的优点
- 相比于L1损失函数,可以收敛得更快。
- 相比于L2损失函数,对离群点、异常值不敏感,梯度变化相对更小
2.4. Huber损失
Huber Loss 是一个用于回归问题的带参损失函数, 优点是能增强平方误差损失函数(MSE, mean square error)对离群点的鲁棒性。
- 当预测偏差小于 δ 时,它采用平方误差,
- 当预测偏差大于 δ 时,采用的线性误差。
相比于最小二乘的线性回归,HuberLoss降低了对离群点的惩罚程度,所以 HuberLoss 是一种常用的鲁棒的回归损失函数。
Huber Loss 定义如下:
(感觉和smoothL1的公式类似)
a = y−f(x) ,y是真实值,f(x)是模型的预测值。
python实现Huber:
# huber 损失
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)
2.5.Log-Cosh损失
Log-Cosh是应用于回归任务中的另一种损失函数,它比L2损失更平滑。Log-cosh是预测误差的双曲余弦的对数。
Log-Cosh函数公式:
Log-Cosh函数图像:
优点:
对于较小的X值,log(cosh(x))约等于(x ** 2) / 2;对于较大的X值,则约等于abs(x) - log(2)。这意味着Log-cosh很大程度上工作原理和平均方误差很像,但偶尔出现错的离谱的预测时对它影响又不是很大。它具备了Huber损失函数的所有优点,但不像Huber损失,它在所有地方都二次可微。
python实现Log-cosh:
# log cosh 损失
def logcosh(true, pred):
loss = np.log(np.cosh(pred - true))
return np.sum(loss)
2.6.分位数损失(Quantile Loss)[这个不太明白]
在大多数现实预测问题中,我们常常很想知道我们的预测值的不确定性。对于很多业务问题而言,相对于知道某个预测点,了解预测值范围能够大幅优化决策过程。最小二乘回归的预测区间基于我们假设残差值(y — y_hat)在所有独立变量值上的变化保持一致。
如果我们是想预测某个区间而非某个点,Quantile损失函数会非常有用。违背此假设的回归模型是不可信的。当然我们也不能认为这种情况下用非线性函数或基于树的模型能更好的建模,把拟合线性模型作为基准的理念扔在一边就完了。这时,我们就可以用到Quantile损失和Quantile回归,因为基于Quantile损失的回归能够提供更明智的预测区间,即便是有非常量方差和非正常分布的误差来说,效果同样不错。
基于 Quantile 的回归模型目的是根据预测变量的特定值,预测反应变量的条件分位数。 Quantile 损失实际上就是 MAE 的延伸(当分位数为第50个百分位数时,它就是MAE)。
其理念就是根据我们是否想增加正误差或负误差的分量选择合适的分位数值。损失函数会根据所选分位数(γ)的值,为估计过高或估计不足做出不同的处罚。例如,γ=0.25的Quantile损失函数会向估计过高做出更多的惩罚,将预测值保持在略微低于平均值的状态。
γ就是所需的分位数,值范围在0和1之间。