Advertisement

【神经网络】权重衰减(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_1w_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_1w_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参数。
  • 可以创建多个优化器实例,分别对模型的不同参数部分采用不同的迭代策略。

注:本节除了代码之外,内容与原书基本相同。原书传送门

为学习目的,本人引用了该书内容。非商业用途,推荐大家阅读此书,共同学习。


加油!

感谢!

努力!

全部评论 (0)

还没有任何评论哟~