来源:原始 Jupyter Notebook。内容已转换为网页阅读格式;下载原文件。
前面我们学习了通过最小二乘法求解回归模型,既可以求解线性回归,也可以扩展到非线性回归。最小二乘法是机器学习中的最基础的算法,这种求解损失函数最小值的思路可以延伸到更多的机器学习算法中,包括分类和聚类问题。
介绍损失函数时我们提到了两种损失函数,一种是 绝对损失(Absolute Loss),一种是 平方损失(Squared Loss)。
绝对损失函数:
平方损失函数:
绝对损失函数因为不是处处可导,计算麻烦,所以我们选取了平方损失,最小二乘法就是求解平方损失函数的最小值。但是平方损失有一个很严重的问题,它对异常值非常敏感,在前面的例子中,我们使用的训练数据基本上都是正常的,也就是和模型大致是吻合的。如果我们在训练数据中加入异常值,这在实际场景中是经常遇到的,那么采用前面的最小二乘法,得到的模型会是什么样的呢?
import numpy as np
import matplotlib.pyplot as plt
plt.xlim(30, 60)
plt.ylim(0, 600)
X = np.array([39.93, 42.05, 43.18, 44.68, 49.87, 53.57, 55.28])
Y = np.array([199, 290, 298, 310, 399, 420, 30])
plt.scatter(X, Y)
plt.show()
运行结果

可以看到 (55.28, 30) 这个点明显有问题,对这个数据集进行训练:
import numpy as np
X = np.matrix([
[1, 1, 1, 1, 1, 1, 1],
[39.93, 42.05, 43.18, 44.68, 49.87, 53.57, 55.28]]).T
Y = np.matrix([199, 290, 298, 310, 399, 420, 30]).T
a = (X.T * X).I * X.T * Y
print("a = {0}".format(a))
运行结果
a = [[370.98773744]
[ -1.98111201]]
得到线性模型模型:
import numpy as np
import matplotlib.pyplot as plt
plt.xlim(30, 60)
plt.ylim(0, 600)
X = np.array([39.93, 42.05, 43.18, 44.68, 49.87, 53.57, 55.28])
Y = np.array([199, 290, 298, 310, 399, 420, 30])
plt.scatter(X, Y)
x = np.linspace(30, 60, 200)
y = -1.98111201*x + 370.98773744
plt.plot(x, y)
plt.show()
运行结果

可以看到,这个异常值直接导致模型完全变了样,这是因为在最小二乘法中我们使用的损失函数为平方损失:
其中, 叫做残差,表示训练样本和预测值之间的差值,显然异常值的残差比较大,为了让平方损失最小,模型不得不在正常值和异常值之间找一个平衡,这样残差越大,得出来的模型越不准确。有没有什么好的方法,能让学习算法对异常值不敏感?
一般来说,有两种不同的思路,一种方法是在训练之前首先使用异常检测的方法将异常值剔除,这在我们后面学习无监督学习时会介绍到;另一种方法是保留异常值,但是采用结果不受异常值影响的算法,这种对异常值也能保持稳定可靠的性质,称为 鲁棒性,具有这样性质的学习算法称为 鲁棒学习。
可以看出,平方损失对异常值敏感的原因在于损失的大小随着残差呈平方级増长。所以鲁棒学习的关键就是找到某种损失函数,可以在残差增长的时候,保证损失不会变得太大。一个很容易下想到的损失函数就是绝对损失:
这里损失的大小随着残差呈线性增长,相比平方损失具有更好的鲁棒性。
另一种鲁棒性很好的损失函数是 Huber 损失:
如果残差的绝对值 小于阈值 的话,上式就变成了平方损失;如果残差的绝对值 大于阈值 的话,就变成了绝对损失,在绝对损失中减去常数项 是为了和平方损失平滑的连接。当阈值 非常小时,Huber 损失退化为绝对损失,所以求解绝对损失最小化可以转换为求解 Huber 损失最小化。
Huber 损失最小化学习的求解方法是迭代算法,将其转换为加权最小二乘的形式:
其中,权重 为:
然后通过 反复加权最小二乘学习法 进行求解(参加《图解机器学习》p.61)。
另一个具有很好鲁棒性的损失函数为 Tukey 损失(图基损失):
它也可以转换为加权最小二乘的形式,权重 为: