深度学习中的所有算法都依赖于最小化或最大化一个函数,我们称之为损失函数(loss function),或“目标函数”、“代价函数”。损失函数是衡量预测模型在预测预期结果方面做得有多好。求函数最小点最常用的方法是梯度下降法。损失函数就像起伏的山,梯度下降就像从山上滑下来到达最底部的点。
没有一个单一的损失函数可以完美适用于所有类型的数据。它取决于许多因素,包括异常值的存在、深度学习算法的选择、梯度下降的时间效率等等。本文的目的是了解不同的损失函数,以及它们的原理。
损失函数大致可分为两类:分类损失和回归损失,其中分类损失根据类别数量又可分为二分类损失和多分类损失。需要注意的是:回归函数预测数量,分类函数预测标签。
均方误差(MSE)是最常用的回归损失函数。MSE是目标变值和预测值之间距离的平方之和。
下图是MSE的函数图,真实目标值为100,预测值在-10000到10000之间。MSE损失(y轴)在预测(x轴)为100时达到最小值。范围是0到 ∞ \infty ∞。
平均绝对误差(MAE)是另一个用于回归模型的损失函数。MAE是目标值和预测值之间的绝对差的总和。所以它测量的是一系列预测的平均误差大小,而不考虑它们的方向,范围也是0到 ∞ \infty ∞。如果考虑方向,那将被称为平均偏差(Mean Bias Error,MBE),它是残差/误差的总和。
简言之:使用平方误差MSE更容易收敛,但使用绝对误差MAE对异常值更稳健。接下来理解一下原因。
当我们训练一个模型时,我们的目标是找到使损失函数取最小值的点。当然,当预测值与真实值完全相等时,两个函数都达到最小值。下面是这两种损失的python代码,我们可以编写自己的函数,也可以使用sklearn的内置函数:
# true: Array of true target variable
# pred: Array of predictions
def mse(true, pred):
return np.sum((true - pred)**2)
def mae(true, pred):
return np.sum(np.abs(true - pred))
# also available in sklearn
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
让我们看看两种情况下的MAE和均方根误差(RMSE,它只是MSE的平方根,使MSE与MAE在相同的尺度上)的值。在第一种情况下,预测值接近真实值,且在所有样本之间误差的方差很小。在第二种情况下,观察到有一个异常值,误差很大。
由于MSE平方误差(y-y_predicted = e
),当e>1
时,误差(e
)的值大大增加;如果数据中有一个离群值,e
的值就会很高,e^2
就会>>|e|
。这将使有MSE损失的模型比有MAE损失的模型给离群点更多的权值。在第2种情况下,以RMSE为损失函数的模型将以其他常见示例为代价进行调整,以最大程度减少单个异常的情况,这会降低模型的整体性能。
如果训练数据被异常值破坏(例如,我们在训练环境中错误地接收到巨大的负/正值,而不是在测试环境中),MAE损失是有用的。
直观地说,我们可以这样想:如果我们只需要对所有试图使MSE最小化的观察结果给出一个预测,那么这个预测应该是所有目标值的平均值。但如果我们试图最小化MAE,这个预测将是所有观测值的中值。我们知道中值比均值对离群值更稳健,这使得MAE比MSE对离群值更稳健。
使用MAE损失的一个大问题是,它的梯度始终是相同的,这意味着即使损失很小,梯度也会很大,这对学习没有好处。为了解决这个问题,我们可以使用动态学习率,它会随着我们接近最小值而降低。在这种情况下,MSE表现得很好,即使在一个固定的学习速率下也会收敛。当损失值越大,MSE损失的梯度越大,当损失接近0时,MSE损失的梯度越小,使得训练结束时MSE损失的梯度更精确(见下图)。
如果异常值代表的异常对业务是重要的,应该被检测到,那么我们应该使用MSE。另一方面,如果我们认为离群值仅仅代表损坏的数据,那么我们应该选择MAE作为损失。
L1损失对异常值具有更强的鲁棒性,但其导数不连续,使得求解效率低下。L2损失对异常值是敏感的,但给出了一个更稳定和封闭形式的解决方案(通过设置其导数为0)。
两者的问题:可能存在这两种损失函数都不能给出理想预测的情况。例如,如果我们的数据中90%的观察值的真实目标值为150,其余10%的目标值在0~30之间。然后,一个以MAE作为损失的模型可以为所有观测值预测150,忽略10%的异常情况,因为它将尝试向中值靠拢。在同样的情况下,一个使用MSE的模型会在0到30的范围内给出许多预测,因为它会偏向于离群值。这两种结果在许多商业案例中都是不可取的。
在这种情况下该怎么办?一个简单的解决方法是转换目标变量。另一种方法是尝试不同的损失函数。这就是我们的第三个损失函数,Huber Loss背后的动机。
与平方误差损失相比,Huber Loss对数据中的异常值不那么敏感。它在0处也是可微的。它基本上是绝对误差,当误差很小的时候就变成了二次值。误差有多小才能变成二次值取决于一个超参数 δ \delta δ,这个超参数是可以调整的。Huber损失在 δ \delta δ~ 0时接近MSE,在 δ \delta δ~ ∞ \infty ∞(大数值)时接近MAE。
delta的选择是至关重要的,因为它决定了你愿意将哪些内容视为异常值。大于delta的残差在L1中最小(L1对大的异常值不那么敏感),而小于delta的残差在L2中适当最小。
为什么使用Huber Loss?
使用MAE训练神经网络的一个大问题是它的持续大梯度,这可能会导致在使用梯度下降训练结束时丢失最小值。对于MSE,当损失接近其最小值时,梯度减小,使其更加精确。
在这种情况下,Huber损失是非常有用的,因为它在减小梯度的最小值附近弯曲。它比MSE对离群值更稳健。因此,它结合了MSE和MAE的优良性能。然而,Huber损失的问题是我们可能需要训练超参数delta,这是一个迭代过程。
Log-cosh是回归任务中使用的另一个比L2更平滑的函数。Log-cosh是预测误差的双曲余弦的对数。
**优点:**对于小的x, log(cosh(x))
近似等于(x ** 2) / 2
,对于大的x,近似等于abs(x) - log(2)
。这意味着“logcosh”的工作方式与均方误差类似,但不会受到偶尔出现的严重错误预测的强烈影响。它具有Huber损失的所有优点,而且它在任何地方都是可微的,不像Huber损失。
为什么需要二阶导数?许多ML模型实现如XGBoost使用牛顿法来寻找最优值,这就是为什么需要二阶导数(Hessian)。对于像XGBoost这样的ML框架,两次可微函数更合适。
但是Log-cosh损失并不是完美的。对于非常大的偏离目标的预测是恒定的,它仍然受到梯度和Hessian问题的困扰,因此导致XGBoost缺少分割。
在大多数现实世界的预测问题中,我们经常对我们预测中的不确定性感兴趣。了解预测的范围,而不是只了解点估计,可以显著改善许多业务问题的决策制定过程。
分位数损失函数在预测区间而不仅仅是预测点时是有用的。最小二乘回归的预测区间是基于残差(y-y_hat
)在自变量值之间具有恒定方差的假设。我们不能相信违背这一假设的线性回归模型。我们也不能抛弃拟合线性回归模型作为基线的想法,说这种情况总是可以用非线性函数或基于树的模型更好地建模。这就是分位数损失和分位数回归发挥作用的地方,因为基于分位数损失的回归甚至为方差非常或非正态分布的残差提供了合理的预测区间。
让我们看一个工作示例,以更好地理解为什么基于分位数损失的回归在异方差数据中表现良好。
基于分位数的回归是在预测变量给定值的情况下估计响应变量的条件分位数。分位数损失实际上只是MAE的一个扩展(当分位数是50%时,它就是MAE)。
其思想是选择分位数值是基于我们想给正误差更多的值还是负误差更多的值。损失函数试图根据所选择的分位数( γ \gamma γ)的值,对高估和低估给予不同的惩罚。例如,分位数损失函数( γ = 0.25 \gamma = 0.25 γ=0.25)对过高估计给予更多惩罚,并试图保持预测值略低于中值。
我们也可以使用这个损失函数来计算神经网络或基于树的模型的预测区间。下面是一个例子,Sklearn实现梯度增强树回归。
上图显示了sklearn库的GradientBoostingRegression中可用的分位数损失函数计算出的90%的预测区间。上界构造为 γ = 0.95 \gamma= 0.95 γ=0.95,下界构造为 γ = 0.05 \gamma = 0.05 γ=0.05。
将所有的损失绘制在一张图上。
这个名字很容易解释。二分类是指将一个对象分配到两个类中的一个。这种分类基于应用于输入特征向量的规则。例如,根据邮件的主题行将其分类为垃圾邮件或非垃圾邮件是二分类。
举个例子:我们希望根据平均半径、面积、周长等特征将肿瘤分类为恶性或良性。为了简化,我们只使用两个输入特征( X 1 X_1 X1和 X 2 X_2 X2)进行分类,即最差区域和平均对称。目标值Y可以是0(恶性)或1(良性)。下面是数据的散点图:
让我们从理解熵这个术语开始。通常,我们用熵来表示无序或不确定性,它是对概率分布为p(X)的随机变量X进行测量的:
负号是用来使总数量为正的。
一个概率分布的熵值越大,表明该分布的不确定性越大。同样,值越小,分布越确定。
这使得二元交叉熵适合作为损失函数来最小化它的值。对于输出概率p的分类模型,我们使用二元交叉熵损失。
元素属于1类(或正类)的概率= p那么,元素属于0类(或负类)的概率= 1 - p
那么,定义输出标签y(可以取0和1)和预测概率p的交叉熵损失为:
这也叫做对数损失。为了计算概率p,我们可以使用Sigmoid函数。这里,z是输入特征的函数:
Sigmoid函数的取值范围为[0,1]
,适合计算概率。
def update_weights_BCE(m1, m2, b, X1, X2, Y, learning_rate):
m1_deriv = 0
m2_deriv = 0
b_deriv = 0
N = len(X1)
for i in range(N):
s = 1 / (1 / (1 + math.exp(-m1*X1[i] - m2*X2[i] - b)))
# Calculate partial derivatives
m1_deriv += -X1[i] * (s - Y[i])
m2_deriv += -X2[i] * (s - Y[i])
b_deriv += -(s - Y[i])
# We subtract because the derivatives point in direction of steepest ascent
m1 -= (m1_deriv / float(N)) * learning_rate
m2 -= (m2_deriv / float(N)) * learning_rate
b -= (b_deriv / float(N)) * learning_rate
return m1, m2, b
使用不同的alpha值1000次迭代的权重更新规则,得到下图:
铰链损耗主要与分类标签为-1和1的支持向量机(SVM)分类器一起使用。因此,请确保将数据集中的负类的标签从0更改为-1。
Hinge损失不仅会惩罚错误的预测,也会惩罚不确定的正确预测。
输入输出对(x, y)的Hinge损失为:
def update_weights_Hinge(m1, m2, b, X1, X2, Y, learning_rate):
m1_deriv = 0
m2_deriv = 0
b_deriv = 0
N = len(X1)
for i in range(N):
# Calculate partial derivatives
if Y[i]*(m1*X1[i] + m2*X2[i] + b) <= 1:
m1_deriv += -X1[i] * Y[i]
m2_deriv += -X2[i] * Y[i]
b_deriv += -Y[i]
# else derivatives are zero
# We subtract because the derivatives point in direction of steepest ascent
m1 -= (m1_deriv / float(N)) * learning_rate
m2 -= (m2_deriv / float(N)) * learning_rate
b -= (b_deriv / float(N)) * learning_rate
return m1, m2, b
在使用三个不同的alpha值运行更新函数2000次迭代后,得到了这张图:
Hinge损失简化了支持向量机的数学运算,同时使损失最大化(与对数损失相比)。当我们要做出实时决策而又不急于提高准确性时,可以使用它。
一个例子:电子邮件不仅仅被划分为垃圾邮件或非垃圾邮件(这已经不是90年代了!)他们被分为工作、家庭、社交、晋升等不同的类别。这是一个多分类问题。
我们将使用Iris数据集来理解剩下的两个损失函数。我们将使用2个特征 X 1 X_1 X1即萼片长度和 X 2 X_2 X2花瓣宽度来预测鸢尾花Setosa, Versicolor或Virginica的级别。
我们的任务是使用神经网络模型和Keras中的内置Adam优化器来实现分类器。这是因为随着参数数量的增加,数学和代码将变得难以理解。
多类交叉熵损失是二元交叉熵损失的推广。输入向量 X i X_i Xi和对应的单编码目标向量 Y i Y_i Yi的损失为:
我们使用softmax函数来求概率 p i j p_{ij} pij:
“Softmax是通过输出层之前的神经网络层实现的。Softmax层必须有与输出层相同数量的节点。”
“Softmax is implemented through a neural network layer just before the output layer. The Softmax layer must have the same number of nodes as the output layer.”Google Developer’s Blog
最终,我们的输出是对给定输入具有最大概率的类。
我们使用输入层和输出层建立一个模型,并以不同的学习率训练它。在model.compile()语句中将损失参数指定为categorical_crossentropy。
# importing requirements
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import adam
# alpha = 0.001 as given in the lr parameter in adam() optimizer
# build the model
model_alpha1 = Sequential()
model_alpha1.add(Dense(50, input_dim=2, activation='relu'))
model_alpha1.add(Dense(3, activation='softmax'))
# compile the model
opt_alpha1 = adam(lr=0.001)
model_alpha1.compile(loss='categorical_crossentropy', optimizer=opt_alpha1, metrics=['accuracy'])
# fit the model
# dummy_Y is the one-hot encoded
# history_alpha1 is used to score the validation and accuracy scores for plotting
history_alpha1 = model_alpha1.fit(dataX, dummy_Y, validation_data=(dataX, dummy_Y), epochs=200, verbose=0)
下面是经过200个epoch训练后的成本和准确度的分别图:
Kullback-Liebler散度是一个概率分布与另一个分布差异的度量,KL散度为零表示分布是相同的。
注意散度函数是不对称的。这就是为什么KL散度不能用作距离度量的原因。
本文将描述使用KL散度作为损失函数的基本方法,而不涉及它的数学原理。由于KL散度是不对称的,我们可以用两种方法来做这件事:
第一种方法用于监督学习,第二种方法用于强化学习。KL散度在函数上类似于多类交叉熵,也称为P相对于Q的相对熵:
我们在compile()函数中指定kullback_leibler_divergence作为损失参数的值,就像前面处理多类交叉熵损失时所做的那样。
# importing requirements
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import adam
# alpha = 0.001 as given in the lr parameter in adam() optimizer
# build the model
model_alpha1 = Sequential()
model_alpha1.add(Dense(50, input_dim=2, activation='relu'))
model_alpha1.add(Dense(3, activation='softmax'))
# compile the model
opt_alpha1 = adam(lr=0.001)
model_alpha1.compile(loss='kullback_leibler_divergence', optimizer=opt_alpha1, metrics=['accuracy'])
# fit the model
# dummy_Y is the one-hot encoded
# history_alpha1 is used to score the validation and accuracy scores for plotting
history_alpha1 = model_alpha1.fit(dataX, dummy_Y, validation_data=(dataX, dummy_Y), epochs=200, verbose=0)
与多类分类相比,KL-散度更常用于近似复杂函数。 我们在使用深度自动生成模型(如变分自动编码器(VAE))时经常遇到KL-散度。
[1] https://heartbeat.fritz.ai/5-regression-loss-functions-all-machine-learners-should-know-4fb140e9d4b0
[2] https://medium.com/analytics-vidhya/a-detailed-guide-to-7-loss-functions-for-machine-learning-algorithms-26e11b6e700b