正则化的基本概念之前博客已有记录, 这里仅对正则化的实现做一点介绍
权重衰减(weight decay)
模型的复杂性——如何衡量函数与0的距离——Lp范数
L2正则化线性模型构成经典的岭回归(ridge regression)算法, L1正则化线性回归通常被称为套索回归(lasso regression)。实践中多使用L2范数。
使用L2范数的一个原因是它对权重向量的大分量施加了巨大的惩罚, 这使得学习算法偏向于在大量特征上均匀分布权重的模型。
在实践中,这可能使它们对单个变量中的观测误差更为稳定。 相比之下,L1惩罚会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零。
L2正则化线性模型,使用验证数据拟合:
L2正则化回归的小批量随机梯度下降更新如下式:
从零实现
将线性模型从零实现的损失函数做一些修改即可
简洁实现
线性模型从零实现的优化算法需要修改:
trainer = torch.optim.SGD([ {"params":net[0].weight,'weight_decay': wd}, {"params":net[0].bias}], lr=lr)
在实例化优化器时直接通过weight_decay
指定weight decay超参数。
默认情况下,PyTorch同时衰减权重和偏移。 这里只为权重设置了weight_decay
,所以偏置参数b不会衰减。
暂退法(dropout)
模型简单性的另一个角度是平滑性,即函数不应该对其输入的微小变化敏感。
在训练过程中,在计算后续层之前向网络的每一层注入噪声。 当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性。 这个想法被称为暂退法(dropout)。
暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。 这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
从零实现
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) num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256 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)))) if self.training == True: H1 = dropout_layer(H1, dropout1) H2 = self.relu(self.lin2(H1)) if self.training == True: H2 = dropout_layer(H2, dropout2) out = self.lin3(H2) return out net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)
num_epochs, lr, batch_size = 10, 0.5, 256 loss = nn.CrossEntropyLoss(reduction='none') train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) trainer = torch.optim.SGD(net.parameters(), lr=lr) d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
简洁实现
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 256), nn.ReLU(), nn.Dropout(dropout1), nn.Linear(256, 256), nn.ReLU(), 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)
trainer = torch.optim.SGD(net.parameters(), lr=lr) d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)