过拟合、欠拟合及其解决方案

模型选择、过拟合和欠拟合

训练误差和泛化误差:
在解释上述现象之前,我们需要区分训练误差(training error)和泛化误差(generalization error)。通俗来讲,前者指模型在训练数据集上表现出的误差,后者指模型在任意一个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。计算训练误差和泛化误差可以使用之前介绍过的损失函数,例如线性回归用到的平方损失函数和softmax回归用到的交叉熵损失函数。

机器学习模型应关注降低泛化误差。

模型选择

验证数据集:
从严格意义上讲,测试集只能在所有超参数和模型参数选定后使用一次。不可以使用测试数据选择模型,如调参。由于无法从训练误差估计泛化误差,因此也不应只依赖训练数据选择模型。鉴于此,我们可以预留一部分在训练数据集和测试数据集以外的数据来进行模型选择。这部分数据被称为验证数据集,简称验证集(validation set)。例如,我们可以从给定的训练集中随机选取一小部分作为验证集,而将剩余部分作为真正的训练集。

K折交叉验证

由于验证数据集不参与模型训练,当训练数据不够用时,预留大量的验证数据显得太奢侈。一种改善的方法是K折交叉验证(K-fold cross-validation)。在K折交叉验证中,我们把原始训练数据集分割成K个不重合的子数据集,然后我们做K次模型训练和验证。每一次,我们使用一个子数据集验证模型,并使用其他K-1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这K次训练误差和验证误差分别求平均。

过拟合和欠拟合

接下来,我们将探究模型训练中经常出现的两类典型问题:

一类是模型无法得到较低的训练误差,我们将这一现象称作欠拟合(underfitting);
另一类是模型的训练误差远小于它在测试数据集上的误差,我们称该现象为过拟合(overfitting)。 在实践中,我们要尽可能同时应对欠拟合和过拟合。虽然有很多因素可能导致这两种拟合问题,在这里我们重点讨论两个因素:模型复杂度和训练数据集大小。
模型复杂度
为了解释模型复杂度,我们以多项式函数拟合为例。给定一个由标量数据特征 和对应的标量标签 组成的训练数据集,多项式函数拟合的目标是找一个阶多项式函数来近似 。在上式中, 是模型的权重参数, 是偏差参数。与线性回归相同,多项式函数拟合也使用平方损失函数。特别地,一阶多项式函数拟合又叫线性函数拟合。

给定训练数据集,模型复杂度和误差之间的关系:

Image Name

训练数据集大小

影响欠拟合和过拟合的另一个重要因素是训练数据集的大小。一般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在计算资源允许的范围之内,我们通常希望训练数据集大一些,特别是在模型复杂度较高时,例如层数较多的深度学习模型。

梯度消失和梯度爆炸

深度模型有关数值稳定性的典型问题是消失(vanishing)和爆炸(explosion)。

当神经网络的层数较多时,模型的数值稳定性容易变差。

假设一个层数为 L 的多层感知机的第 l 层 H(l) 的权重参数为 W(l) ,输出层 H(L) 的权重参数为 W(L) 。为了便于讨论,不考虑偏差参数,且设所有隐藏层的激活函数为恒等映射(identity mapping) ϕ(x)=x 。给定输入 X ,多层感知机的第 l 层的输出 H(l)=XW(1)W(2)…W(l) 。此时,如果层数 l 较大, H(l) 的计算可能会出现衰减或爆炸。举个例子,假设输入和所有层的权重参数都是标量,如权重参数为0.2和5,多层感知机的第30层输出为输入 X 分别与 0.230≈1×10−21 (消失)和 530≈9×1020 (爆炸)的乘积。当层数较多时,梯度的计算也容易出现消失或爆炸。

随机初始化模型参数

在神经网络中,通常需要随机初始化模型参数。下面我们来解释这样做的原因。

回顾多层感知机一节描述的多层感知机。为了方便解释,假设输出层只保留一个输出单元 o1 (删去 o2 和 o3 以及指向它们的箭头),且隐藏层使用相同的激活函数。如果将每个隐藏单元的参数都初始化为相等的值,那么在正向传播时每个隐藏单元将根据相同的输入计算出相同的值,并传递至输出层。在反向传播中,每个隐藏单元的参数梯度值相等。因此,这些参数在使用基于梯度的优化算法迭代后值依然相等。之后的迭代也是如此。在这种情况下,无论隐藏单元有多少,隐藏层本质上只有1个隐藏单元在发挥作用。因此,正如在前面的实验中所做的那样,我们通常将神经网络的模型参数,特别是权重参数,进行随机初始化。

PyTorch的默认随机初始化

随机初始化模型参数的方法有很多。在线性回归的简洁实现中,我们使用torch.nn.init.normal_()使模型net的权重参数采用正态分布的随机初始化方式。不过,PyTorch中nn.Module的模块参数都采取了较为合理的初始化策略(不同类型的layer具体采样的哪一种初始化方法的可参考源代码),因此一般不用我们考虑。

Xavier随机初始化

还有一种比较常用的随机初始化方法叫作Xavier随机初始化。 假设某全连接层的输入个数为 a ,输出个数为 b ,Xavier随机初始化将使该层中权重参数的每个元素都随机采样于均匀分布

它的设计主要考虑到,模型参数初始化后,每层输出的方差不该受该层输入个数影响,且每层梯度的方差也不该受该层输出个数影响。

考虑环境因素 协变量偏移

这里我们假设,虽然输入的分布可能随时间而改变,但是标记函数,即条件分布P(y∣x)不会改变。虽然这个问题容易理解,但在实践中也容易忽视。

循环神经网络进阶

循环神经网络

本节介绍循环神经网络,下图展示了如何基于循环神经网络实现语言模型。我们的目的是基于当前的输入与过去的输入序列,预测序列的下一个字符。循环神经网络引入一个隐藏变量 H ,用 Ht 表示 H 在时间步 t 的值。 Ht 的计算基于 Xt 和 Ht−1 ,可以认为 Ht 记录了到当前字符为止的序列信息,利用 Ht 对序列的下一个字符进行预测。Image Name

循环神经网络的构造

我们先看循环神经网络的具体构造。假设 Xt∈Rn×d 是时间步 t 的小批量输入, Ht∈Rn×h 是该时间步的隐藏变量,则:

Ht=ϕ(XtWxh+Ht−1Whh+bh).

其中, Wxh∈Rd×h , Whh∈Rh×h , bh∈R1×h , ϕ 函数是非线性激活函数。由于引入了 Ht−1Whh , Ht 能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。由于 Ht 的计算基于 Ht−1 ,上式的计算是循环的,使用循环计算的网络即循环神经网络(recurrent neural network)。

在时间步 t ,输出层的输出为:

Ot=HtWhq+bq.

其中 Whq∈Rh×q , bq∈R1×q 。