梳理L1、L2与Smooth L1

    技术2024-12-14  21

    关于L1、L2的范数、损失函数和正则化,之前一直混淆这几个概念,故对这几天看过的资料进行了学习总结。

    范数(norm)是数学中的一种基本概念。在泛函分析中,它定义在赋范线性空间中,并满足一定的条件,即①非负性;②齐次性;③三角不等式。它常常被用来度量某个向量空间(或矩阵)中的每个向量的长度或大小。

    首先以L2范数为例对范数做一个简单的说明:

    L2范数:

    假设 X X X是n维的特征 X = ( x 1 , x 2 , x 3 , … , x n ) X=(x_1,x_2,x_3,…,x_n) X=(x1,x2,x3,,xn)

    则L2范数: ∥ X ∥ 2 = ∑ i = 1 n x i 2 \left \|X\right \|_2=\sqrt{\sum_{i=1}^{n}x_i^2} X2=i=1nxi2

    一、作为损失函数

    本部分内容大多来源于回归损失函数1:L1 loss, L2 loss以及Smooth L1 Loss的对比。

    公式对比

    L 1 L_1 L1

    公式: L 1 = ∣ f ( x ) − Y ∣ L_1=\left|f(x)-Y\right| L1=f(x)Y

    求导: L 1 ′ = ± f ′ ( x ) L'_1=\pm{f'(x)} L1=±f(x)

    L 2 L_2 L2

    公式: L 2 = ∣ f ( x ) − Y ∣ 2 L_2=\left |f(x)-Y\right |^2 L2=f(x)Y2

    求导: L 2 ′ = 2 f ′ ( x ) ( f ( x ) − Y ) L'_2=2f'(x)(f(x)-Y) L2=2f(x)(f(x)Y)

    S m o o t h    L 1 Smooth\;L_1 SmoothL1

    公式:

    S m o o t h    L 1 = { 0.5 ( f ( x ) − Y ) 2  if  ∣ f ( x ) − Y ∣ < 1 ∣ f ( x ) − Y ∣ − 0.5  if  f ( x ) − Y < − 1    o r    f ( x ) − Y > 1 Smooth\;L_1=\begin{cases} 0.5(f(x)-Y)^2 & \text{ if } \left|f(x)-Y\right|<1 \\ \left|f(x)-Y\right|-0.5 & \text{ if } f(x)-Y<-1\; or\;f(x)-Y>1 \end{cases} SmoothL1={0.5(f(x)Y)2f(x)Y0.5 if f(x)Y<1 if f(x)Y<1orf(x)Y>1

    求导:

    S m o o t h    L 1 ′ = { f ′ ( x ) ( f ( x ) − Y )  if  ∣ f ( x ) − Y ∣ < 1 ± 1  if  f ( x ) − Y < − 1    o r    f ( x ) − Y > 1 Smooth\;L'_1=\begin{cases} f'(x)(f(x)-Y) & \text{ if } \left|f(x)-Y\right|<1 \\ \pm 1 & \text{ if } f(x)-Y<-1\; or\;f(x)-Y>1 \end{cases} SmoothL1={f(x)(f(x)Y)±1 if f(x)Y<1 if f(x)Y<1orf(x)Y>1

    上述三个函数的图像对比如下:

    L1 Loss

    L1 Loss即平均绝对误差(Mean Absolute Error,MAE) ,是指模型预测值𝑓(𝑥)和真实值𝑦之间距离的平均值,其公式如下: M A E = ∑ i = 1 n ∣ f ( x i ) − y i ∣ n MAE=\frac{\sum_{i=1}^{n}\left|f(x_i)-y_i\right|}{n} MAE=ni=1nf(xi)yi 𝐿1对𝑥的导数为常数,在训练的后期,预测值与ground truth差异很小时,𝐿1的导数的绝对值仍然为1,而 learning rate 如果不变,损失函数将在稳定值附近波动,难以继续收敛以达到更高精度。

    tips:

    平均绝对误差MAE

    在0处不可导

    对离群点不敏感

    梯度稳定,不会导致梯度爆炸(对很小的损失值,梯度也很大)

    L2 Loss

    L2 Loss即为均方误差(Mean Square Error,MSE),是模型预测值𝑓(𝑥)与真实样本值𝑦之间差值平方的平均值,其公式如下 M S E = ∑ i = 1 n ( f ( x i ) − y i ) 2 n MSE=\frac{\sum_{i=1}^{n}(f(x_i)-y_i)^2}{n} MSE=ni=1n(f(xi)yi)2 其中, y i y_i yi f ( x i ) f(x_i) f(xi)分别表示第𝑖个样本的真实值及其对应的预测值,𝑛为样本的个数。

    MSE的函数曲线光滑、连续,处处可导,便于使用梯度下降算法,是一种常用的损失函数。 而且,随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。

    当𝑦和𝑓(𝑥)也就是真实值和预测值的差值大于1时,会放大误差;而当差值小于1时,则会缩小误差,这是平方运算决定的。MSE对于较大的误差(>1)给予较大的惩罚,较小的误差(<1)给予较小的惩罚。也就是说,对离群点比较敏感,受其影响较大。

    tips:

    函数曲线连续,处处可导

    随着误差减小,梯度也减小,有利于快速收敛

    常用于解决回归问题

    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以及分位数损失等。

    Smooth L1 Loss

    在Faster R-CNN以及SSD中对边框的回归使用的损失函数都是Smooth L1 作为损失函数, S m o o t h    L 1 = { 0.5 ( f ( x ) − Y ) 2  if  ∣ f ( x ) − Y ∣ < 1 ∣ f ( x ) − Y ∣ − 0.5  otherwise  Smooth\;L_1=\begin{cases} 0.5(f(x)-Y)^2 & \text{ if } \left|f(x)-Y\right|<1 \\ \left|f(x)-Y\right|-0.5 & \text{ otherwise } \end{cases} SmoothL1={0.5(f(x)Y)2f(x)Y0.5 if f(x)Y<1 otherwise  Smooth L1 能从两个方面限制梯度:

    当预测框与 ground truth 差别过大时,梯度值不至于过大;当预测框与 ground truth 差别很小时,梯度值足够小。

    Smooth L1 实际上就是一个分段函数,在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题,在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题。

    Smooth L1 实现 (PyTorch)

    def _smooth_l1_loss(input, target, reduction='none'): # type: (Tensor, Tensor) -> Tensor t = torch.abs(input - target) ret = torch.where(t < 1, 0.5 * t ** 2, t - 0.5) if reduction != 'none': ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret) return ret

    也可以添加个参数beta 这样就可以控制,什么范围的误差使用MSE,什么范围内的误差使用MAE了。

    def smooth_l1_loss(input, target, beta=1. / 9, reduction = 'none'): """ very similar to the smooth_l1_loss from pytorch, but with the extra beta parameter """ n = torch.abs(input - target) cond = n < beta ret = torch.where(cond, 0.5 * n ** 2 / beta, n - 0.5 * beta) if reduction != 'none': ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret) return ret

    小结

    对于大多数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损失函数,对离群点、异常值不敏感,梯度变化相对更小,训练时不容易跑飞。

    二、作为正则项

    正则化为什么可以避免过拟合?

    正规化是防止过拟合的一种重要技巧。正则化通过降低模型的复杂性, 达到避免过拟合的问题。

    过拟合的时候,拟合函数的系数往往非常大,为什么?过拟合,就是拟合函数需要顾忌每一个点,最终形成的拟合函数波动很大。在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。

    L1正则

    L1正则常被用来进行特征选择,主要原因在于L1正则化会使得较多的参数为0,从而产生稀疏解,我们可以将0对应的特征遗弃,进而用来选择特征。一定程度上L1正则也可以防止模型过拟合。

    假设L(W)是未加正则项的损失,λ是一个超参,控制正则化项的大小。

    则最终的损失函数: L = L ( W ) + λ ∑ i = 1 n ∣ w i ∣ L=L(W)+\lambda \sum_{i=1}^{n}\left|w_i\right| L=L(W)+λi=1nwi

    L2正则

    梯度衰减(weights decay),主要用来防止模型过拟合,直观上理解就是L2正则化是对于大数值的权重向量进行严厉惩罚。

    则最终的损失函数: L = L ( W ) + λ ∑ i = 1 n w i 2 L=L(W)+\lambda \sum_{i=1}^{n}w_i^2 L=L(W)+λi=1nwi2

    为什么L1会产生稀疏解?

    可以通过损失函数对w求导来证明,当 w i w_i wi小于1的时候,L2的惩罚项会越来越小,而L1还是会非常大,所以L1会使参数为0,而L2很难。

    求导公式待补充

    参考资料

    L1和L2 详解(范数、损失函数、正则化)

    回归损失函数1:L1 loss, L2 loss以及Smooth L1 Loss的对比

    损失函数:L1 loss, L2 loss, smooth L1 loss

    Processed: 0.022, SQL: 9