【神经网络】权重衰减(weight-decay)
权重衰减(weight-decay)
- 权重衰减机制
-
-
基于高维线性回归的实验研究
-
从零构建实现框架
-
模型参数的初始化步骤
- 引入L2范数的惩罚项,用于减少模型复杂度
- 通过训练集和测试集的划分,实现数据的分段使用
- 分析模型在训练与测试数据上的性能差异,观察过拟合现象
- 应用权重衰减机制,优化模型的泛化能力
-
简洁实现
-
小结
-
-
权重衰减
在上一节中,我们研究了过拟合现象,即模型在训练集上的误差显著低于其在测试集上的误差。尽管增加训练数据集的规模可能有助于缓解过拟合,但获取额外数据通常会带来较高的成本。本节将介绍应对过拟合问题的常见方法:权重衰减(weight decay)。
方法
权重衰减机制 等同于 L_2范数正则化技术(regularization)。正则化技术 通过引入惩罚项到模型的损失函数中,使得模型参数的取值范围较小,从而有效缓解模型过拟合问题。我们先详细阐述基于L_2范数的正则化方法,进而进一步解释其为何被称为权重衰减机制。
基于模型原损失函数的基础上,L_2范数正则化通过添加L_2范数惩罚项,从而实现对训练目标的最小化求解。L_2范数惩罚项即为模型权重参数每个元素平方和与一个正数的乘积。以3.1节(线性回归)中的线性回归损失函数为例。
\ell(w_1, w_2, b) = \frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right)^2
其中,w_1, w_2是权重参数,b是偏差参数,样本i的输入为x_1^{(i)}, x_2^{(i)},标签为y^{(i)},样本数为n。用向量\boldsymbol{w} = [w_1, w_2]表示权重参数,加入L_2范数惩罚项后的新损失函数为
\ell(w_1, w_2, b) + \frac{\lambda}{2n} \|\boldsymbol{w}\|^2,
其中超参数 \lambda > 0。当所有权重参数均为0时,惩罚项达到最小值。当λ值较大时,惩罚项在损失函数中的权重显著增加,这通常会导致学习到的权重参数元素趋向于0。当λ设为0时,惩罚项完全无法发挥作用。
在公式中,L_2范数的平方\|\boldsymbol{w}\|^2展开后可得w_1^2 + w_2^2。引入了L_2范数惩罚项后,在小批量随机梯度下降算法中,我们对线性回归一节中权重w_1和w_2的更新规则进行了优化,使其迭代方式发生了变化:
\begin{aligned} w_1 &:= \left(1 - \frac{\eta\lambda}{|\mathcal{B}|}\right)w_1 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} x_1^{(i)} \left(x_1^{(i)}w_1 + x_2^{(i)}w_2 + b - y^{(i)}\right),\\ w_2 &:= \left(1 - \frac{\eta\lambda}{|\mathcal{B}|}\right)w_2 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} x_2^{(i)} \left(x_1^{(i)}w_1 + x_2^{(i)}w_2 + b - y^{(i)}\right). \end{aligned}
通过L_2范数正则化,权重w_1和w_2在操作过程中先被自乘一个小于1的数值,然后从梯度计算中排除这些未加惩罚的梯度项。
因此,这一技术被称为权重衰减技术。权重衰减技术通过施加惩罚作用于绝对值较大的模型参数,为需要学习的模型施加了约束条件,这一做法可能有助于缓解过拟合问题。在实际应用中,我们通常在损失函数中加入偏差项的平方和作为惩罚项。
高维线性回归实验
在介绍过拟合问题时,我们采用高维线性回归模型作为示例,并通过权重衰减方法来缓解过拟合现象。假设数据样本的特征维度为p,对于来自训练集和测试集的任一实例,其特征为x_1, x_2, \ldots, x_p,我们通过线性函数来生成其标签。
y = 0.05 + \sum_{i = 1}^p 0.01x_i + \epsilon
其中噪声项\epsilon遵循均值为0、标准差为0.01的正态分布。为了便于考察过拟合现象,我们设置了一个高维线性回归问题,令维度p=200;同时,为了便于观察,我们将训练数据集的样本数量设为较小值,即20。
%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
n_train, n_test, num_inputs = 20, 100, 200
true_w, true_b = torch.ones(num_inputs, 1) * 0.01, 0.05
features = torch.randn((n_train + n_test, num_inputs))
labels = torch.matmul(features, true_w) + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
train_features, test_features = features[:n_train, :], features[n_train:, :]
train_labels, test_labels = labels[:n_train], labels[n_train:]
AI助手
从零开始实现
系统性阐述从零基础实现权重衰减的方法。在目标函数之后,我们引入了L_2范数惩罚项,以实现权重衰减。
初始化模型参数
首先,定义随机初始化模型参数的函数。该函数为每个参数都附上梯度。
def init_params():
w = torch.randn((num_inputs, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
return [w, b]
AI助手
定义L_2范数惩罚项
下面定义L_2范数惩罚项。这里只惩罚模型的权重参数。
def l2_penalty(w):
return (w**2).sum() / 2
AI助手
定义训练和测试
下面定义如何在训练数据集 和测试数据集 上分别训练和测试模型。
不同于前面几节的内容,这里在计算损失函数时,增加了L_2范数的正则化项。
batch_size, num_epochs, lr = 1, 100, 0.003
net, loss = d2l.linreg, d2l.squared_loss
dataset = torch.utils.data.TensorDataset(train_features, train_labels)
train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)
def fit_and_plot(lambd):
w, b = init_params()
train_ls, test_ls = [], []
for _ in range(num_epochs):
for X, y in train_iter:
# 添加了L2范数惩罚项
l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
l = l.sum()
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
d2l.sgd([w, b], lr, batch_size)
train_ls.append(loss(net(train_features, w, b), train_labels).mean().item())
test_ls.append(loss(net(test_features, w, b), test_labels).mean().item())
d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
range(1, num_epochs + 1), test_ls, ['train', 'test'])
print('L2 norm of w:', w.norm().item())
AI助手
观察过拟合
接下来,我们进行高维线性回归模型的训练与测试。当λ设置为0时,模型未施加权重衰减。结果显示,训练误差远低于测试集误差。
这是典型的过拟合现象 。
fit_and_plot(lambd=0)
AI助手
输出:
L2 norm of w: 15.114808082580566
AI助手

使用权重衰减
在本研究中,我们采用权重衰减这一技术手段。通过实验结果表明,尽管训练误差有所上升,但测试集误差却有所下降。一定程度上缓解了过拟合问题。
另外,相较于未启用权重衰减的情况,权重层 的L_2范数更小,这些权重参数的值更趋近于0。
fit_and_plot(lambd=3)
AI助手
输出:
L2 norm of w: 0.035220853984355927
AI助手

简洁实现
在构造优化器实例时,我们通过指定weight_decay超参数来配置权重衰减参数。
在默认情况下,PyTorch 会同时进行权重和偏差的衰减。我们可以通过分别为权重和偏差单独创建优化器实例,从而实现仅对权重进行衰减。
def fit_and_plot_pytorch(wd):
# 对权重参数衰减。权重名称一般是以weight结尾
net = nn.Linear(num_inputs, 1)
nn.init.normal_(net.weight, mean=0, std=1)
nn.init.normal_(net.bias, mean=0, std=1)
optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 对权重参数衰减
optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr) # 不对偏差参数衰减
train_ls, test_ls = [], []
for _ in range(num_epochs):
for X, y in train_iter:
l = loss(net(X), y).mean()
optimizer_w.zero_grad()
optimizer_b.zero_grad()
l.backward()
# 对两个optimizer实例分别调用step函数,从而分别更新权重和偏差
optimizer_w.step()
optimizer_b.step()
train_ls.append(loss(net(train_features), train_labels).mean().item())
test_ls.append(loss(net(test_features), test_labels).mean().item())
d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
range(1, num_epochs + 1), test_ls, ['train', 'test'])
print('L2 norm of w:', net.weight.data.norm().item())
AI助手
如同从零开始实现权重衰减的实验现象,使用权重衰减能够在一定程度上缓解过拟合问题。
fit_and_plot_pytorch(0)
AI助手
输出:
L2 norm of w: 12.86785888671875
AI助手

fit_and_plot_pytorch(3)
AI助手
输出:
L2 norm of w: 0.09631537646055222
AI助手

小结
- 正则化 通过向模型损失函数中加入惩罚项,使得模型参数的值保持较小,这是防止模型过拟合的常见方法。
- 权重衰减 等同于L2范数正则化,其效果是使学习得到的权重参数元素趋向于接近0。
- 权重衰减 通常配置为优化器中的
weight decay参数。 - 可以创建多个优化器实例,分别对模型的不同参数部分采用不同的迭代策略。
注:本节除了代码之外,内容与原书基本相同。原书传送门
为学习目的,本人引用了该书内容。非商业用途,推荐大家阅读此书,共同学习。
加油!
感谢!
努力!
