通过实验我们知道,使用不同复杂度的模型训练参数,会在测试数据上得到不同的误差,而且越复杂的模型并不代表着越低的误差。误差主要有两个来源,一个是偏差(Bias), 另一个是方差(Variance). 了解并学会分析误差来源其实很重要,例如你训练好一个模型,得到了训练误差和测试误差,接下来要如何根据它们去改进现有模型呢?如果你对偏差和方差很熟悉,就可以选择合适的方法去处理。

在我们使用线性回归预测宝可梦 CP 值的例子中,理论上有一个最佳的函数 $\hat{f}$, 这个函数只有负责实现游戏逻辑的程序员才知道。实际上我们并不知道这个理想函数 $\hat{f}$, 而只能够根据已有的训练数据去找到最好的函数 $f^{\ast}$, 我们并不能做到令 $f^{\ast}=\hat{f}$, 因此 $f^{\ast}$ 可以看作是 $\hat{f}$ 的一个估计(Estimator).

这就好像在打靶一样,理想函数 $\hat{f}$ 是中心点,我们通过训练数据找到的最好的函数 $f^{\ast}$ 可能在靶子上的另外一个位置,$\hat{f}$ 和 $f^{\ast}$ 之间的距离可能来自两件事情,即偏差与方差。我们举一个概率论中的例子:

假设现在有一个变量 $x$, 它的均值(mean)是 $\mu$, 方差是 $\sigma^2$.

当我们想要估计 $x$ 的均值 $\mu$ 时,我们会抽样 $N$ 个点 $\left\{x^{1}, x^{2}, \ldots, x^{N}\right\}$, 并计算它们的平均值 $m=\frac{1}{N} \sum_{n} x^{n} $. 除非抽样无穷多个点,否则在大多数情况下 $n \neq \mu$. 进行多次试验,可能会得到 $m_1, m_2, m_3, \ldots $, 这些 $m$ 可能都不等于 $\mu$.

但是如果我们计算期望 $E[m]=E\left[\frac{1}{N} \sum_{n} x^{n}\right]=\frac{1}{N} \sum_{n} E\left[x^{n}\right]=\mu$, 则可以得到 $\mu$. 此时用 $E[m]$ 来估计 $\mu$ 是无偏的(unbiased), 就好像打靶的时候准星瞄准了 $\mu$, 但是由于其它原因的干扰,打靶点最终会散落在瞄准位置的周围。而 $m$ 这种散布开的程度用它的方差 $\operatorname{Var}[m]=\frac{\sigma^{2}}{N}$ 来表示,$N$ 越大时方差越小。即如果每次试验抽样了比较多的点,$m$ 在 $\mu$ 周围的散布会比较集中;否则会比较散开。

当我们想要估计 $x$ 的方差 $\sigma^2$ 时,首先用刚才的方法估测 $m=\frac{1}{N} \sum_{n} x^{n}$, 再计算 $s^{2}=\frac{1}{N} \sum_{n}\left(x^{n}-m\right)^{2}$. 你会发现 $s^2$ 散布在 $\sigma^2$ 的周围,但此时是有偏估计(Biased Estimator), 即期望值 $E\left[s^{2}\right]=\frac{N-1}{N} \sigma^{2} \neq \sigma^{2}$. 如果抽样数量 $N$ 比较大,这种估测的差距会变小。

我们现在要估测靶的中心 $\hat{f}$, 某次使用训练数据学习到的 $f^{\ast}$ 可能在上图中的位置,与红心之间存在着误差。根据瞄准的位置,我们可以判断这次的估计是否有偏,具体来说则是假设可以做很多次实验,计算出 $f^{\ast}$ 的期望 $E\left[f^{*}\right]=\overline{f}$. 如果 $\overline{f}$ 不在靶心,则说明一开始就没有瞄准, $\overline{f}$ 与 $\hat{f}$ 之间形成了偏差。误差还有一种来源,即瞄准了位置后,子弹射出去产生了偏移,导致找出来的 $f^{\ast}$ 与瞄准的位置 $\overline{f}$ 不一致,则 $f^{\ast}$ 与 $\overline{f}$ 之间存在着方差。

理想状况下,我们希望偏差和方差尽可能小,即瞄得准又射得准,像左上角的情况。

平行宇宙——多次实验假设

我们在使用训练数据时,只能找到一个最优的 $f^{\ast}$, 而上面的例子中有很多点代表着不同的 $f^{\ast}$, 该怎么理解呢?我们用平行宇宙的概念来理解,在每个平行宇宙中,我们都想估计宝可梦进化后的 CP 值,但在不同的宇宙中抓到的宝可梦是不一样的,作为训练数据就存在着差异。

用不同的输入数据丢进同一个模型 $y=b+w \cdot x_{c p}$, 得到的最好的函数 $f^{\ast}$ 也会有所不同。此时我们假设用同一个模型做了 100 次实验,每次抽样 10 个训练数据,最后得到的 100 个 $f^{\ast}$ 的分布可能如下图:

换用不同的模型,观察各自 100 个 $f^{\ast}$ 的曲线分布,可以发现:简单的模型曲线分布比较集中,此时方差较小;复杂的模型曲线散布很飘逸,此时的方差较大。这是因为简单模型的形状不容易受到差异数据的影响,而复杂模型中如果出现异常点或是噪声点,则容易出现明显的扭曲。

接下来看偏差,即如果我们对所有的 $f^{\ast}$ 求平均,看它离 $\hat{f}$ 有多接近,此时不在意 $f^{\ast}$ 散得有多开。假设我们用黑色曲线表示 $\hat{f}$, 做 5000 次实验,每次从黑色曲线上抽样 10 个点作为训练输入,因此会得到 5000 条 $f^{\ast}$, 并用红色曲线表示。最后用蓝色曲线表示平均后得到的曲线。

我们会发现简单的模型会有比较大的偏差,而复杂的模型虽然方差很大,但偏差较小。这是因为模型确定后,函数集合的范围就确定了。如果是一个简单的模型,它的范围可能比较小,甚至没有将目标函数给包括进去,这个时候不论在参数范围中如何寻找 $f^{\ast}$, 最后的平均都不可能是 $\hat{f}$. 如果模型比较复杂,虽然 $f^{\ast}$ 散布得空间很大,但更有可能包含了目标函数,只是难以确定 $\hat{f}$ 的具体位置,尤其是每次训练数据分布差异很大的时候,每次得到的 $f^{\ast}$ 位置都会不同。

误差诊断与处理策略

如果模型的训练误差很大,表示没能很好拟合训练样本,说明偏差很大,整体欠拟合(Underfitting). 收集更多的数据是没有帮助的,因为模型已经不对。这个时候应该考虑去重新设计模型,让它更复杂或者更合理一些,比如考虑更多的特征,使用更高的次幂项。

如果训练误差很小,但是测试误差很大,这意味着模型的方差比较大,在训练集上过拟合(Overfitting). 收集更多的数据的确可以很有效地防止过拟合现象的产生,但是在实际情况中很难去做到,还会带来更高的训练成本。一种取巧的办法是做数据增广,生成一些新的数据:手写数字的图像可以左转 15° 或右转 15°;图象识别则可以将原图进行水平翻转;语音数据则可以使用变声器改变音调… 还有各种各样的方法。实际上,人们更多地考虑使用正则化(Regularization) 技术,在损失函数中加入惩罚项,使所有的曲线变得更加平滑,因此起到了降低方差的作用,但惩罚系数需要合理设置,不然在降低方差的同时会明显增大偏差,这也是不可取的。

模型选择

我们希望能在偏差和方差之间能够达到平衡,选择一个模型均衡两种误差并使总体误差尽可能小。

测试误差只能衡量模型在测试数据上表现的好坏,而我们最终在意的是在真实数据(从未见过的新数据) 上的表现。就比如在 Kaggle 数据科学竞赛中,数据被分为了训练数据和测试数据,而测试数据又被分为了 Public 和 Private 两种,上传训练模型到榜单中只能看到公开测试数据上的得分,而真正用于最终评定的测试数据,只有在截止期后才会被用于最终测试。

因此大部分时候,我们会将训练数据又划分为训练数据和验证数据(Validation, 有时候也叫开发数据 Develop). 训练数据用于训练模型,验证数据用于选择模型,测试数据用于误差分析。虽然不建议为了降低测试误差而去做一些特意的调整(这样测试数据将不能很好代表真实数据的分布情况), 但人们总是无法避免不去这样做,比如在发论文时刷所谓的基准(Bench-mark), 这样才能证明自己的方法是比较有效的。

将模型扔到真实数据中接受检验,得到的测试误差通常会比自己训练模型时最终得到的测试误差要高。

N 折交叉验证

为了避免对训练集进行划分可能带来的影响,可以将训练集划分 $N$ 块后进行 $N$ 次交叉验证,每次验证数据都来自划分结果不同的一块。最终使用多次验证误差的平均,来代表模型的误差。