模型的过拟合
将模型在训练数据上拟合的比在潜在分布中更接近的现象称为过拟合(overfitting),出现这种现象的主要原因是训练数据中存在噪音或者训练数据太少。
可以看出在a中虽然完全的拟合了样本数据,但对于b中的测试数据分类准确度很差。而c虽然没有完全拟合样本数据,但在d中对于测试数据的分类准确度却很高。过拟合问题往往是由于训练数据少等原因造成的。
所以解决过拟合的方法之一就是增加数据量进而增加训练的数据量。除了增加数据量以外,还可以通过权重衰退和dropout来解决这个问题。
权重衰退
在训练参数化机器学习模型时, 权重衰减(weight decay)是最广泛使用的正则化的技术之一, 它通常也被称为
L2正则化.一种简单的方法是通过线性函数 f(x)=wx,中的权重向量的某个范数来度量其复杂性, 例如||w||^2
。 要保证权重向量比较小, 最常用方法是将其范数作为惩罚项加到最小化损失的问题中。 将原来的训练目标最小化训练标签上的预测损失, 调整为最小化预测损失和惩罚项之和。
硬性限制W
使用均方范数作为硬性限制:直接限制W的范数小于某个值
- 通常不限制b,因为b只是影响曲线的上下平移,对曲线的形状没有实际作用。
- θ越小,意味着这个正则项越强。
柔性限制W
性回归例子。 我们的损失由下式给出:
使用均方范数作为柔性限制:对于每个θ ,都可以找到一个λ 使得之前的目标函数等价于下式:
超参数λ 控制了正则项的重要程度
- λ =0:无作用
-
λ → ∞:表示w*→0
简洁实现
由于权重衰减在神经网络优化中很常用, 深度学习框架为了便于我们使用权重衰减, 将权重衰减集成到优化算法中,以便与任何损失函数结合使用。 此外,这种集成还有计算上的好处, 允许在不增加任何额外的计算开销的情况下向算法中添加权重衰减。 由于更新的权重衰减部分仅依赖于每个参数的当前值, 因此优化器必须至少接触每个参数一次。
在下面的代码中,我们在实例化优化器时直接通过weight_decay指定weight decay超参数。 默认情况下,PyTorch同时衰减权重和偏移。 这里我们只为权重设置了weight_decay,所以偏置参数b不会衰减。
def train_concise(wd):
net = nn.Sequential(nn.Linear(num_inputs, 1))
for param in net.parameters():
param.data.normal_()
loss = nn.MSELoss(reduction='none')
num_epochs, lr = 100, 0.003
# 偏置参数没有衰减
trainer = torch.optim.SGD([
{"params":net[0].weight,'weight_decay': wd},
{"params":net[0].bias}], lr=lr)
animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
xlim=[5, num_epochs], legend=['train', 'test'])
for epoch in range(num_epochs):
for X, y in train_iter:
trainer.zero_grad()
l = loss(net(X), y)
l.mean().backward()
trainer.step()
if (epoch + 1) % 5 == 0:
animator.add(epoch + 1,
(d2l.evaluate_loss(net, train_iter, loss),
d2l.evaluate_loss(net, test_iter, loss)))
print('w的L2范数:', net[0].weight.norm().item())
暂退法(Dropout)
当面对更多的特征而样本不足时,线性模型往往会过拟合。 相反,当给出更多样本而不是特征,通常线性模型不会过拟合。 不幸的是,线性模型泛化的可靠性是有代价的。 简单地说,线性模型没有考虑到特征之间的交互作用。 对于每个特征,线性模型必须指定正的或负的权重,而忽略其他特征。
然后在2014年,斯里瓦斯塔瓦等人 (Srivastava et al., 2014) 就如何将毕晓普的想法应用于网络的内部层提出了一个想法: 在训练过程中,他们建议在计算后续层之前向网络的每一层注入噪声。 因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性。
这个想法被称为暂退法(dropout)。 暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。 这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
在标准暂退法正则化中,通过按保留(未丢弃)的节点的分数进行规范化来消除每一层的偏差。 换言之,每个中间活性值h以暂退概率p由随机变量h'替换,如下所示:
根据此模型的设计,其期望值保持不变,即
图中表示1个隐藏层和5个隐藏单元的多层感知机,当我们将暂退法应用到隐藏层,以p的概率将隐藏单元置为零时, 结果可以看作一个只包含原始神经元子集的网络。删除了和, 因此输出的计算不再依赖于和,并且它们各自的梯度在执行反向传播时也会消失。 这样,输出层的计算不能过度依赖于到中的任何一个元素。
简洁实现
对于深度学习框架的高级API,我们只需在每个全连接层之后添加一个Dropout层, 将暂退概率作为唯一的参数传递给它的构造函数。 在训练时,Dropout层将根据指定的暂退概率随机丢弃上一层的输出(相当于下一层的输入)。 在测试时,Dropout层仅传递数据。
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
# 在第一个全连接层之后添加一个dropout层
nn.Dropout(dropout1),
nn.Linear(256, 256),
nn.ReLU(),
# 在第二个全连接层之后添加一个dropout层
nn.Dropout(dropout2),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);