机器学习——正则化、权重衰减、暂退法
正则化
正则化(Regularization)是机器学习中的一类技术,其通过对模型添加惩罚项来解决过拟合问题,从而提高模型的泛化能力。
正则化的主要思想是在损失函数中引入模型复杂度的惩罚项,强制模型保持一定的简单性和平滑性。
比较常见的正则化方法包括:
- L1正则化:对权重参数的绝对值之和进行惩罚,可以产生稀疏权重矩阵。
- L2正则化:又称权重衰减,对权重参数的平方和进行惩罚,可以减少过拟合。
- 数据增强:通过对训练数据进行变换来增加样本数量,一定程度上也起到正则化效果。
- Early Stopping:在验证集误差不再下降时提前结束训练,防止过度拟合。
- DropOut:在训练过程中随机丢弃一部分神经元,减少模型依赖性。
🤔️L1正则化和L2正则化
使用范数的一个原因是它对权重向量的大分量施加了巨大的惩罚。 这使得我们的学习算法偏向于在大量特征上均匀分布权重的模型。 在实践中,这可能使它们对单个变量中的观测误差更为稳定。 相比之下,<span class="math notranslate nohighlight">惩罚会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零。 这称为<em>特征选择</em>(feature selection),这可能是其他场景下需要的。
🤔️正则化权重衰减是如何保持模型的简单性和平滑性的呢?(简单性的另一个角度是平滑性,即函数不应该对其输入的微小变化敏感。 例如,当我们对图像进行分类时,我们预计向像素添加一些随机噪声应该是基本无影响的。)
权重绝对值越大,对应的输入特征对输出的影响也越大(对输入数据更加敏感)。这会使得模型对训练数据的依赖性越强。
较大的权重会使激活函数工作在非线性区间,增大模型的非线性特征,使得模型函数更复杂。
较小的权重倾向于让模型学到简单的线性映射,而较大的权重则会促使模型学到复杂的非线性。
权重的平方和可以表示模型复杂度的大小,这是正则化的基础。
权重衰减
权重衰减(Weight Decay)是正则化的一种技术,是针对神经网络权重参数的正则化手段。其通过为损失函数添加权重参数的L2范数来实现。在优化神经网络时,权重衰减会惩罚权重参数值过大,从而达到正则化的效果。
常见的权重衰减在损失函数中以如下形式添加:
loss = 损失函数 + λ * 权重L2范数
其中λ是超参数,控制权重衰减的力度。权重L2范数即各个权重参数的平方和。
由于权重衰减在神经网络优化中很常用, 深度学习框架为了便于我们使用权重衰减, 将权重衰减集成到优化算法中,以便与任何损失函数结合使用。 此外,这种集成还有计算上的好处, 允许在不增加任何额外的计算开销的情况下向算法中添加权重衰减。(以pyTorch框架为例)
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())
暂退法
其主要思想是:
1. 在每次迭代中,随机选择一部分神经元,暂时不让其激活,即dropout。
2. 剩下的神经元则需要完成原任务,避免过拟合。
3. 每次迭代dropout的节点不同,强迫整个网络协作,避免对特定节点的依赖。
4. 在预测时则不使用dropout。
通过这种“暂退”的随机排除节点,可以正则化模型,减少过拟合。这就是深度学习中常用的Dropout正则化技术。
暂退法的具体实现代码
import torch from torch import nn from d2l import torch as d2l def dropout_layer(X, dropout): assert 0 <= dropout <= 1 # 在本情况中,所有元素都被丢弃 if dropout == 1: return torch.zeros_like(X) # 在本情况中,所有元素都被保留 if dropout == 0: return X mask = (torch.rand(X.shape) > dropout).float() return mask * X / (1.0 - dropout)
这里输出时除以(1.0 - dropout)是为了使得随机dropout后输出的期望保持不变
例如,一个层有4个神经元,dropout 概率为0.5。则有:- 神经元1:以0.5概率dropout,否则原始值a1
- 神经元2:以0.5概率dropout,否则原始值a2
- ...所以整个层的输出为一个随机向量[r1, r2, r3, r4],其中ri是0或ai。这个随机向量的期望就是原始的输出[a1, a2, a3, a4]。
我们可以将暂退法应用于每个隐藏层的输出(在激活函数之后), 并且可以为每一层分别设置暂退概率: 常见的技巧是在靠近输入层的地方设置较低的暂退概率。 下面的模型将第一个和第二个隐藏层的暂退概率分别设置为0.2和0.5, 并且暂退法只在训练期间有效。
(以pyTorch为例)
dropout1, dropout2 = 0.2, 0.5 class Net(nn.Module): def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2, is_training = True): super(Net, self).__init__() self.num_inputs = num_inputs self.training = is_training self.lin1 = nn.Linear(num_inputs, num_hiddens1) self.lin2 = nn.Linear(num_hiddens1, num_hiddens2) self.lin3 = nn.Linear(num_hiddens2, num_outputs) self.relu = nn.ReLU() def forward(self, X): H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs)))) # 只有在训练模型时才使用dropout if self.training == True: # 在第一个全连接层之后添加一个dropout层 H1 = dropout_layer(H1, dropout1) H2 = self.relu(self.lin2(H1)) if self.training == True: # 在第二个全连接层之后添加一个dropout层 H2 = dropout_layer(H2, dropout2) out = self.lin3(H2) return out net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)
对于深度学习框架的高级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);