一、过拟合欠拟合及其解决方案
过拟合和欠拟合是模型训练中经常出现的两类典型问题。在实践中,我们要尽可能同时应对欠拟合和过拟合。虽然有很多因素可能导致这两种拟合问题,但在这里我们将要重点讨论两个因素:模型复杂度和训练数据集大小。最后将提出几种解决方案。
欠拟合
我们将模型无法得到较低的训练误差这一现象称作欠拟(underfitting)。
让我们观察一下欠拟合现象:
与正常拟合现象相比:
我们可以看到,正常拟合情况下,训练误差和测试误差都很小,且曲线接近重合,模型训练的相当好,有较好的泛化能力,而欠拟合情况下,训练误差和测试误差都很大,更离谱的是测试误差还远比训练误差小,可见此种情况下在训练集上模型都未拟合,训练得出的模型很失败。
针对欠拟合现象,一个解决方案是尝试增加模型的复杂度,模型太简单就会导致欠拟合,举个例子:训练1阶多项式(线性模型)怎样也拟合不了三阶多项式(曲线)拟合的数据吧,势必会发生欠拟合现象,这时改变模型为三阶多项式训练才能较好的拟合数据。
给定数据集,我们来看一下模型复杂度与误差的关系:
通过这个图片我们可以清楚地发现模型复杂度对·过拟合欠拟合现象产生的影响,选择合适复杂度的模型就能一定程度上避免或减弱两种现象的发生。过拟合
我们将模型的训练误差远小于它在测试数据集上的误差这一现象称为过拟合(overfitting)。
让我们看看过拟合现象:
我们可以看到在这种情况下,虽然训练误差和测试误差都在减小,但是训练误差远小于测试误差,二者甚至不再同一个数量级上,可见训练得到的模型在训练集上拟合的较好,而泛化能力极差。
如何应对过拟合现象的发生呢?
1、增加训练数据数量。一般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在计算资源允许的范围之内,我们通常希望训练数据集大一些,特别是在模型复杂度较高时,例如层数较多的深度学习模型。我们上面的过拟合现象就是在训练数据过少,甚至远小于模型参数的情况下发生的。
2、减小模型复杂度。我们上面的模型复杂度与误差关系一图已清晰地告诉我们当模型过于复杂时就会对训练数据过度拟合而发生过拟合现象。
3、权重衰减(L2范数正则化)。正则化通过为模型损失函数添加惩罚项使学出的模型参数值较小,是应对过拟合的常用手段。范数惩罚项指的是模型权重参数每个元素的平方和与一个正的常数的乘积。以线性回归中的线性回归损失函数为例
其中是权重参数,
是偏差参数,样本
的输入为
,标签为
,样本数为
。将权重参数用向量
表示,带有
范数惩罚项的新损失函数为
其中超参数。当权重参数均为0时,惩罚项最小。当
较大时,惩罚项在损失函数中的比重较大,这通常会使学到的权重参数的元素较接近0。当
设为0时,惩罚项完全不起作用。上式中
范数平方
展开后得到
。
有了范数惩罚项后,在小批量随机梯度下降中,我们将线性回归一节中权重
和
的迭代方式更改为
可见,范数正则化令权重
和
先自乘小于1的数,再减去不含惩罚项的梯度。因此,
范数正则化又叫权重衰减。权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制,这可能对过拟合有效。
让我们看一下代码:
自定义实现如下:
#定义L2范数惩罚项
def l2_penalty(w):
return (w**2).sum() / 2
loss = torch.nn.MSELoss()
# 添加了L2范数惩罚项
l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
对于Pytorch简洁实现:
建立两个优化器实例分别对w、b更新梯度,不必手动在损失函数后加惩罚项。
optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 对权重参数衰减
optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr) # 不对偏差参数衰减
l = loss(net(X), y).mean()
未添加惩罚项下的训练结果:
惩罚系数lamda为3下的训练结果:
可见过拟合现象得到了抑制。
4、丢弃法。多层感知机中神经网络图描述了一个单隐藏层的多层感知机。其中输入个数为4,隐藏单元个数为5,且隐藏单元(
)的计算表达式为
这里是激活函数,
是输入,隐藏单元
的权重参数为
,偏差参数为
。当对该隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为
,那么有
的概率
会被清零,有
的概率
会除以
做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量
为0和1的概率分别为
和
。使用丢弃法时我们计算新的隐藏单元
由于,因此
即丢弃法不改变其输入的期望值。让我们对之前多层感知机的神经网络中的隐藏层使用丢弃法,一种可能的结果如图所示,其中和
被清零。这时输出值的计算不再依赖
和
,在反向传播时,与这两个隐藏单元相关的权重的梯度均为0。由于在训练中隐藏层神经元的丢弃是随机的,即
都有可能被清零,输出层的计算无法过度依赖
中的任一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了拿到更加确定性的结果,一般不使用丢弃法
代码实现:
def dropout(X, drop_prob):
X = X.float()
assert 0 <= drop_prob <= 1
keep_prob = 1 - drop_prob
# 这种情况下把全部元素都丢弃
if keep_prob == 0:
return torch.zeros_like(X)
mask = (torch.rand(X.shape) < keep_prob).float()
return mask * X / keep_prob
X = torch.arange(16).view(2, 8)
dropout(X, 0)
tensor([[ 0., 1., 2., 3., 4., 5., 6., 7.],
[ 8., 9., 10., 11., 12., 13., 14., 15.]])
dropout(X, 0.5)
tensor([[ 0., 0., 4., 6., 8., 0., 0., 14.],
[ 0., 0., 20., 22., 0., 26., 28., 30.]])
可见 大概0.5的被丢弃。
Pytorch简洁实现,把Dropout层添加到激活层和下一个层之间,例:
nn.ReLU(),
nn.Dropout(drop_prob1),
nn.Linear(num_hiddens1, num_hiddens2),
二、梯度消失、梯度爆炸
深度模型有关数值稳定性的典型问题是消失(vanishing)和爆炸(explosion)。
当神经网络的层数较多时,模型的数值稳定性容易变差。
假设一个层数为的多层感知机的第
层
的权重参数为
,输出层
的权重参数为
。为了便于讨论,不考虑偏差参数,且设所有隐藏层的激活函数为恒等映射(identity mapping)
。给定输入
,多层感知机的第
层的输出
。此时,如果层数
较大,
的计算可能会出现衰减或爆炸。举个例子,假设输入和所有层的权重参数都是标量,如权重参数为0.2和5,多层感知机的第30层输出为输入
分别与
(消失)和
(爆炸)的乘积。当层数较多时,梯度的计算也容易出现消失或爆炸。
参考:https://www.kesci.com/notebooks/run/5e4a19f580da780037bb4986?label=5e410c983aed37002c3fbbdc&image=5e41555d24e362002dd8fa95&org=5e09de57d2a1cc002ddc2e0e