# 绝对损失和 Huber 损失
> 原文:[https://www.ste 科书.ds100.org/ch/10/modeling_abs_huber.html](https://www.ste 科书.ds100.org/ch/10/modeling_abs_huber.html)
```
# HIDDEN
# Clear previously defined variables
%reset -f
# Set directory for data loading to work properly
import os
os.chdir(os.path.expanduser('~/notebooks/10'))
```
```
# HIDDEN
import warnings
# Ignore numpy dtype warnings. These warnings are caused by an interaction
# between numpy and Cython and can be safely ignored.
# Reference: https://stackoverflow.com/a/40846742
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
%matplotlib inline
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, interact_manual
import nbinteract as nbi
sns.set()
sns.set_context('talk')
np.set_printoptions(threshold=20, precision=2, suppress=True)
pd.options.display.max_rows = 7
pd.options.display.max_columns = 8
pd.set_option('precision', 2)
# This option stops scientific notation for pandas
# pd.set_option('display.float_format', '{:.2f}'.format)
```
```
# HIDDEN
tips = sns.load_dataset('tips')
tips['pcttip'] = tips['tip'] / tips['total_bill'] * 100
```
```
# HIDDEN
def mse_loss(theta, y_vals):
return np.mean((y_vals - theta) ** 2)
def abs_loss(theta, y_vals):
return np.mean(np.abs(y_vals - theta))
```
```
# HIDDEN
def compare_mse_abs(thetas, y_vals, xlims, figsize=(10, 7), cols=3):
if not isinstance(y_vals, np.ndarray):
y_vals = np.array(y_vals)
rows = int(np.ceil(len(thetas) / cols))
plt.figure(figsize=figsize)
for i, theta in enumerate(thetas):
ax = plt.subplot(rows, cols, i + 1)
sns.rugplot(y_vals, height=0.1, ax=ax)
plt.axvline(theta, linestyle='--',
label=rf'$ \theta = {theta} $')
plt.title(f'MSE = {mse_loss(theta, y_vals):.2f}\n'
f'MAE = {abs_loss(theta, y_vals):.2f}')
plt.xlim(*xlims)
plt.yticks([])
plt.legend()
plt.tight_layout()
```
为了拟合模型,我们选择了一个损失函数,并选择了使损失最小化的模型参数。在上一节中,我们介绍了均方误差(mse)损失函数:
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n}(y_i - \theta)^2\\ \end{aligned} $$
我们使用了一个常量模型来预测数据集中所有条目的相同数字$\theta$。当我们使用 MSE 损失来拟合这个模型时,我们发现$\hat \theta=\text mean(\textbf y)$。在 Tips 数据集中,我们发现拟合常数模型将预测$16.08\%$因为$16.08\%$是 Tip 百分比的平均值。
在本节中,我们介绍了两个新的损耗函数,即**平均绝对误差**损耗函数和**huber**损耗函数。
### 平均绝对误差
现在,我们将保持我们的模型相同,但切换到一个不同的损失函数:平均绝对误差(MAE)。这个损失函数取的是绝对差,而不是每个点的平方差和我们的预测值:
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n} |y_i - \theta| \\ \end{aligned} $$
### 比较 mse 和 mae[?](#Comparing-MSE-and-MAE)
为了更好地了解 MSE 和 MAE 的比较方式,让我们比较它们在不同数据集中的损失。首先,我们将使用一个点的数据集:$\textbf y=[14]$。
```
# HIDDEN
compare_mse_abs(thetas=[11, 12, 13, 14, 15, 16],
y_vals=[14], xlims=(10, 17))
```
![](https://img.kancloud.cn/bd/bb/bdbbe4531c814a1cd4acc50ba6703220_695x479.jpg)
我们发现 MSE 通常高于 MAE,因为误差是平方的。让我们看看当有五个点时会发生什么:$\textbf y=[12.1,12.8,14.9,16.3,17.2]。$
```
# HIDDEN
compare_mse_abs(thetas=[12, 13, 14, 15, 16, 17],
y_vals=[12.1, 12.8, 14.9, 16.3, 17.2],
xlims=(11, 18))
```
![](https://img.kancloud.cn/aa/f9/aaf9a7f5e9e74730e975a1830ddb3c4f_695x479.jpg)
请记住,实际损失值本身对我们不是很有趣;它们只对比较不同的$theta$值有用。一旦我们选择了一个损失函数,我们将寻找产生最小损失的$\hat \theta$,即$\theta$。因此,我们感兴趣的是损失函数是否产生不同的$\hat \theta$。
到目前为止,这两个损失函数似乎在$\hat \theta 上达成一致。然而,如果我们再近一点看,就会发现一些差异。我们首先计算损失,并将它们与我们尝试的 6 个$\theta$值的$theta$进行比较。
```
# HIDDEN
thetas = np.array([12, 13, 14, 15, 16, 17])
y_vals = np.array([12.1, 12.8, 14.9, 16.3, 17.2])
mse_losses = [mse_loss(theta, y_vals) for theta in thetas]
abs_losses = [abs_loss(theta, y_vals) for theta in thetas]
plt.scatter(thetas, mse_losses, label='MSE')
plt.scatter(thetas, abs_losses, label='MAE')
plt.title(r'Loss vs. $ \theta $ when $ \bf{y}$$= [ 12.1, 12.8, 14.9, 16.3, 17.2 ] $')
plt.xlabel(r'$ \theta $ Values')
plt.ylabel('Loss')
plt.legend();
```
![](https://img.kancloud.cn/29/89/298996aa2e904664de6429d3678f042c_444x303.jpg)
然后,我们计算更多的$\theta$值,使曲线平滑:
```
# HIDDEN
thetas = np.arange(12, 17.1, 0.05)
y_vals = np.array([12.1, 12.8, 14.9, 16.3, 17.2])
mse_losses = [mse_loss(theta, y_vals) for theta in thetas]
abs_losses = [abs_loss(theta, y_vals) for theta in thetas]
plt.plot(thetas, mse_losses, label='MSE')
plt.plot(thetas, abs_losses, label='MAE')
plt.title(r'Loss vs. $ \theta $ when $ \bf{y}$$ = [ 12.1, 12.8, 14.9, 16.3, 17.2 ] $')
plt.xlabel(r'$ \theta $ Values')
plt.ylabel('Loss')
plt.legend();
```
![](https://img.kancloud.cn/91/94/9194079853bfa6d531e4b6dfcaabf21d_444x303.jpg)
然后,我们放大 Y 轴上 1.5 到 5 之间的区域,以更清楚地看到最小值的差异。我们用虚线标出了最小值。
```
# HIDDEN
thetas = np.arange(12, 17.1, 0.05)
y_vals = np.array([12.1, 12.8, 14.9, 16.3, 17.2])
mse_losses = [mse_loss(theta, y_vals) for theta in thetas]
abs_losses = [abs_loss(theta, y_vals) for theta in thetas]
plt.figure(figsize=(7, 5))
plt.plot(thetas, mse_losses, label='MSE')
plt.plot(thetas, abs_losses, label='MAE')
plt.axvline(np.mean(y_vals), c=sns.color_palette()[0], linestyle='--',
alpha=0.7, label='Minimum MSE')
plt.axvline(np.median(y_vals), c=sns.color_palette()[1], linestyle='--',
alpha=0.7, label='Minimum MAE')
plt.title(r'Loss vs. $ \theta $ when $ \bf{y}$$ = [ 12.1, 12.8, 14.9, 16.3, 17.2 ] $')
plt.xlabel(r'$ \theta $ Values')
plt.ylabel('Loss')
plt.ylim(1.5, 5)
plt.legend()
plt.tight_layout();
```
![](https://img.kancloud.cn/06/82/0682234bd58a67f36a7780744b9eb6d3_478x335.jpg)
我们从经验上发现,MSE 和 MAE 可以为同一个数据集生成不同的$\hat \theta。一个更仔细的分析揭示了它们何时会不同,更重要的是,它们为什么会不同。
### 离群值[¶](#Outliers)
我们可以在上面的损失图和$\theta$图中看到的一个区别在于损失曲线的形状。绘制均方根误差会导致损失函数中平方项产生抛物线。
另一方面,绘制 MAE 会产生一系列连接的线条。当我们考虑到绝对值函数是线性的时,这是有意义的,因此取许多绝对值函数的平均值应该产生一个半线性函数。
由于 MSE 有一个平方误差项,所以它对异常值更为敏感。如果$\theta=10$且一个点位于 110,则该点的毫秒误差项将为$(10-110)^2=10000$而在 mae 中,该点的误差项将为$10-110=100$。我们可以用一组三点来说明这一点,即$textbf y=[12,13,14]$并绘制 MSE 和 MAE 的损失与$theta$曲线。
使用下面的滑块将第三个点移动到远离其余数据的位置,并观察损失曲线会发生什么。(由于 MSE 的值大于 MAE,所以我们已经缩放了曲线以保持这两个曲线都在视图中。)
```
# HIDDEN
def compare_mse_abs_curves(y3=14):
thetas = np.arange(11.5, 26.5, 0.1)
y_vals = np.array([12, 13, y3])
mse_losses = [mse_loss(theta, y_vals) for theta in thetas]
abs_losses = [abs_loss(theta, y_vals) for theta in thetas]
mse_abs_diff = min(mse_losses) - min(abs_losses)
mse_losses = [loss - mse_abs_diff for loss in mse_losses]
plt.figure(figsize=(9, 2))
ax = plt.subplot(121)
sns.rugplot(y_vals, height=0.3, ax=ax)
plt.xlim(11.5, 26.5)
plt.xlabel('Points')
ax = plt.subplot(122)
plt.plot(thetas, mse_losses, label='MSE')
plt.plot(thetas, abs_losses, label='MAE')
plt.xlim(11.5, 26.5)
plt.ylim(min(abs_losses) - 1, min(abs_losses) + 10)
plt.xlabel(r'$ \theta $')
plt.ylabel('Loss')
plt.legend()
```
```
# HIDDEN
interact(compare_mse_abs_curves, y3=(14, 25));
```
<button class="js-nbinteract-widget">Loading widgets...</button>
我们已经显示了下面$y_3=14$和$y_3=25$的曲线。
```
# HIDDEN
compare_mse_abs_curves(y3=14)
```
![](https://img.kancloud.cn/11/12/1112fb31163f7439d5ac08ae9dd51f5d_560x181.jpg)
```
# HIDDEN
compare_mse_abs_curves(y3=25)
```
![](https://img.kancloud.cn/d2/83/d283961f43baa658e5056d200a1511e3_560x181.jpg)
当我们将该点移离其他数据时,MSE 曲线也随之移动。当$y_=14$时,mse 和 mae 都有$that \theta=13$。然而,当$y_=25$时,MSE 损失产生的是$hat \theta=16.7$而 MAE 产生的是$hat \theta=13$,与以前没有变化。
### 最小化 mae[?](#Minimizing-the-MAE)
既然我们对 MSE 和 MAE 的区别有了定性的认识,我们就可以最小化 MAE,使这一区别更加精确。如前所述,我们将取损失函数对$\theta$的导数,并将其设为零。
然而,这一次我们必须处理这样一个事实:绝对函数并不总是可微的。当$x>;0$时,$\frac \部分\部分 x x=1$时。当$x<;0$时,$\frac \部分\部分 x x=-1$时。虽然$x 在$x=0$时在技术上是不可微的,但是我们将设置$\frac \ partial \ partial x x=0$以便方程更容易处理。
回想一下,MAE 的方程是:
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n}|y_i - \theta|\\ &= \frac{1}{n} \left( \sum_{y_i < \theta}|y_i - \theta| + \sum_{y_i = \theta}|y_i - \theta| + \sum_{y_i > \theta}|y_i - \theta| \right)\\ \end{aligned} $$
在上面的行中,我们将求和分为三个单独的求和:一个是每$y_i<;\theta$有一个术语,一个是每$y_i=\theta$有一个术语,一个是每$y_i>;\theta$有一个术语。为什么求和看起来更复杂?如果我们知道$y_i<;\theta$我们也知道$y_i-\theta<;0$因此之前的$frac \ partial \ partial \theta y_i-\theta=-1$上面的每个术语都有类似的逻辑,以使取导数更容易。
现在,我们取与$\theta$相关的导数,并将其设为零:
$$ \begin{aligned} \frac{1}{n} \left( \sum_{y_i < \theta}(-1) + \sum_{y_i = \theta}(0) + \sum_{y_i > \theta}(1) \right) &= 0 \\ \sum_{y_i < \theta}(-1) + \sum_{y_i > \theta}(1) &= 0 \\ -\sum_{y_i < \theta}(1) + \sum_{y_i > \theta}(1) &= 0 \\ \sum_{y_i < \theta}(1) &= \sum_{y_i > \theta}(1) \\ \end{aligned} $$
上面的结果是什么意思?在左侧,对于每个小于$\theta$的数据点,我们有一个术语。在右边,对于每个大于$\theta$的数据点,我们都有一个。然后,为了满足这个方程,我们需要为$\theta$选择一个值,该值具有相同数量的较小和较大的点。这是一组数字的 _ 中位数 _ 的定义。因此,MAE 的$theta$的最小值是$that\theta=\text 中位数(\textbf y)$。
当我们有奇数个点时,当点按排序顺序排列时,中间值就是中间点。我们可以看到,在下面的例子中,当$\theta$位于中间值时,损失最小:
```
# HIDDEN
def points_and_loss(y_vals, xlim, loss_fn=abs_loss):
thetas = np.arange(xlim[0], xlim[1] + 0.01, 0.05)
abs_losses = [loss_fn(theta, y_vals) for theta in thetas]
plt.figure(figsize=(9, 2))
ax = plt.subplot(121)
sns.rugplot(y_vals, height=0.3, ax=ax)
plt.xlim(*xlim)
plt.xlabel('Points')
ax = plt.subplot(122)
plt.plot(thetas, abs_losses)
plt.xlim(*xlim)
plt.xlabel(r'$ \theta $')
plt.ylabel('Loss')
points_and_loss(np.array([10, 11, 12, 14, 15]), (9, 16))
```
![](https://img.kancloud.cn/3a/b7/3ab73896414c8052e71f34893f347b4e_560x181.jpg)
但是,当我们有偶数个点时,当$\theta$是两个中心点之间的任何值时,损失最小。
```
# HIDDEN
points_and_loss(np.array([10, 11, 14, 15]), (9, 16))
```
![](https://img.kancloud.cn/f1/a8/f1a857b423d781a07226df6d6c67de68_560x181.jpg)
当我们使用 MSE 时,情况并非如此:
```
# HIDDEN
points_and_loss(np.array([10, 11, 14, 15]), (9, 16), mse_loss)
```
![](https://img.kancloud.cn/fd/d4/fdd457e06d18a97d283a2e2235b6ec0b_560x181.jpg)
### mse 与 mae 比较[?](#MSE-and-MAE-Comparison)
我们的研究和上述推导表明,MSE 比 MAE 更容易区分,但对异常值更敏感。对于 MSE,$\hat \theta=\text mean(\textbf y)$,而对于 mae \hat \theta=\text mean(\textbf y)$。注意中位数受异常值的影响比平均值小。这一现象源于我们对两个损失函数的构造。
我们还发现 MSE 有一个唯一的$\hat \theta$,而平均绝对值在有偶数个数据点的情况下可以是多个可能的$\hat \theta$值。
### Huber 损失
第三个损失函数 huber loss 结合了 mse 和 mae,创建了一个对离群值具有可微性 _ 和 _ 的损失函数。Huber 损失通过类似于接近最小值的$\theta$值的 mse 函数和远离最小值的$\theta$值的绝对损失来实现这一点。
和往常一样,我们通过获取数据集中每个点的 Huber 损失的平均值来创建一个损失函数。
让我们看看当我们改变$\theta$时,huber loss 函数为一个数据集输出了什么样的结果。
```
# HIDDEN
def huber_loss(est, y_obs, alpha = 1):
d = np.abs(est - y_obs)
return np.where(d < alpha,
(est - y_obs)**2 / 2.0,
alpha * (d - alpha / 2.0))
thetas = np.linspace(0, 50, 200)
loss = huber_loss(thetas, np.array([14]), alpha=5)
plt.plot(thetas, loss, label="Huber Loss")
plt.vlines(np.array([14]), -20, -5,colors="r", label="Observation")
plt.xlabel(r"Choice for $\theta$")
plt.ylabel(r"Loss")
plt.legend()
plt.savefig('huber_loss.pdf')
```
![](https://img.kancloud.cn/c8/17/c817062e84d47ba4e0aeb65091470c04_420x290.jpg)
我们可以看到 Huber 损失是平稳的,不像 Mae。Huber 损失也以线性速率增加,与均方损失的二次速率不同。
然而,Huber 损失确实有一个缺点。注意,一旦$\theta$离这一点足够远,它就会从 MSE 过渡到 MAE。我们可以调整这个“足够远”来得到不同的损失曲线。例如,我们可以在离观察点只有一个单位远的地方进行一次$theta$转换:
```
# HIDDEN
loss = huber_loss(thetas, np.array([14]), alpha=1)
plt.plot(thetas, loss, label="Huber Loss")
plt.vlines(np.array([14]), -20, -5,colors="r", label="Observation")
plt.xlabel(r"Choice for $\theta$")
plt.ylabel(r"Loss")
plt.legend()
plt.savefig('huber_loss.pdf')
```
![](https://img.kancloud.cn/4c/13/4c133b71cb18d564b2b212cc411b803f_420x290.jpg)
或者我们可以在离观察点 10 个单位远的地方进行转换:
```
# HIDDEN
loss = huber_loss(thetas, np.array([14]), alpha=10)
plt.plot(thetas, loss, label="Huber Loss")
plt.vlines(np.array([14]), -20, -5,colors="r", label="Observation")
plt.xlabel(r"Choice for $\theta$")
plt.ylabel(r"Loss")
plt.legend()
plt.savefig('huber_loss.pdf')
```
![](https://img.kancloud.cn/46/90/4690863396c637885a83fc33f2de4683_420x290.jpg)
此选择会导致不同的损失曲线,因此可能会导致不同的值$\hat\theta$。如果我们想使用 Huber 损失函数,我们还有一个额外的任务,就是将这个转换点设置为合适的值。
Huber 损失函数的数学定义如下:
$$ L_\alpha(\theta, \textbf{y}) = \frac{1}{n} \sum_{i=1}^n \begin{cases} \frac{1}{2}(y_i - \theta)^2 & | y_i - \theta | \le \alpha \\ \alpha ( |y_i - \theta| - \frac{1}{2}\alpha ) & \text{otherwise} \end{cases} $$
它比以前的损失函数更复杂,因为它结合了 MSE 和 MAE。附加参数$\alpha$设置 Huber 损失从 MSE 过渡到绝对损失的点。
尝试求 Huber 损失函数的导数是繁琐的,不会产生像 mse 和 mae 这样优雅的结果。相反,我们可以使用一种称为梯度下降的计算方法来找到$\theta$的最小值。
### 摘要[¶](#Summary)
在本节中,我们介绍了两个损失函数:平均绝对误差和 Huber 损失函数。我们展示了一个使用 mae 拟合的常数模型,$\hat \theta=\text 中位数(\textbf y)$。
- 一、数据科学的生命周期
- 二、数据生成
- 三、处理表格数据
- 四、数据清理
- 五、探索性数据分析
- 六、数据可视化
- Web 技术
- 超文本传输协议
- 处理文本
- python 字符串方法
- 正则表达式
- regex 和 python
- 关系数据库和 SQL
- 关系模型
- SQL
- SQL 连接
- 建模与估计
- 模型
- 损失函数
- 绝对损失和 Huber 损失
- 梯度下降与数值优化
- 使用程序最小化损失
- 梯度下降
- 凸性
- 随机梯度下降法
- 概率与泛化
- 随机变量
- 期望和方差
- 风险
- 线性模型
- 预测小费金额
- 用梯度下降拟合线性模型
- 多元线性回归
- 最小二乘-几何透视
- 线性回归案例研究
- 特征工程
- 沃尔玛数据集
- 预测冰淇淋评级
- 偏方差权衡
- 风险和损失最小化
- 模型偏差和方差
- 交叉验证
- 正规化
- 正则化直觉
- L2 正则化:岭回归
- L1 正则化:LASSO 回归
- 分类
- 概率回归
- Logistic 模型
- Logistic 模型的损失函数
- 使用逻辑回归
- 经验概率分布的近似
- 拟合 Logistic 模型
- 评估 Logistic 模型
- 多类分类
- 统计推断
- 假设检验和置信区间
- 置换检验
- 线性回归的自举(真系数的推断)
- 学生化自举
- P-HACKING
- 向量空间回顾
- 参考表
- Pandas
- Seaborn
- Matplotlib
- Scikit Learn