【动手学深度学习v2李沐】学习笔记07:权重衰退、正则化

前文回顾:模型选择、欠拟合和过拟合

文章目录

  • 一、权重衰退
    • 1.1 硬性限制
    • 1.2 柔性限制(正则化)
    • 1.3 参数更新法则
    • 1.4 总结
  • 二、代码实现
    • 2.1 从零开始实现
      • 2.1.1 人工数据集
      • 2.1.2 模型参数
      • 2.1.3 L 2 L_2 L2 范数惩罚
      • 2.1.4 训练
    • 2.2 简洁实现

一、权重衰退

1.1 硬性限制

  • 在上一篇文章中,我们讲到了控制模型容量的两种方法:
    • 使用较小的参数(使得模型变小)
    • 使参数可选择的值比较少
  • 权重衰退通过限制参数值的选择范围来控制模型容量。
    • 例如,我们可以在最小化损失函数的时候增加一个限制,防止权重过大:
      min ⁡ l ( w ⃗ , b ) subject to ∣ ∣ w ⃗ ∣ ∣ 2 ≤ θ \min l(\vec{w}, b) \quad \text{subject to} \quad ||\vec{w}||^2 \leq \theta minl(w ,b)subject to∣∣w 2θ本例中,我们限制 w ⃗ \vec{w} w L 2 L_2 L2 损失不大于 θ \theta θ
    • 我们通常不限制偏移 b b b(限不限制都差不多)
    • 选择一个小的 θ \theta θ 意味着更强的正则项

1.2 柔性限制(正则化)

我们通常不会采用上一小节中那样的硬性限制,而是通过正则化这种柔性限制来控制模型容量。

  • L 2 L_2 L2 正则化:
    • 对每个 θ \theta θ,都可以找到 λ \lambda λ 使得之前的目标函数等价于下式:
      min ⁡ l ( w ⃗ , b ) + λ 2 ∣ ∣ w ⃗ ∣ ∣ 2 \min{l(\vec w,b)}+\frac {\lambda}{2}||\vec w||^2 minl(w ,b)+2λ∣∣w 2
    • 可以通过拉格朗日乘子来证明
    • 超参数 λ \lambda λ 控制了正则项的重要程度
      • λ = 0 \lambda=0 λ=0 时,正则项不起作用
      • λ → ∞ \lambda \rightarrow \infty λ 时, w ⃗ → 0 ⃗ \vec w \rightarrow\vec 0 w 0
  • L 1 L_1 L1 正则化:
    • 使得大部分模型参数的值等于0,已达到模型稀疏化的目的。
    • 其公式为:
      min ⁡ l ( w ⃗ , b ) + λ ∣ ∣ w ⃗ ∣ ∣ 1 \min{l(\vec w,b)}+\lambda||\vec w||_1 minl(w ,b)+λ∣∣w 1
  • 演示:
    我们以 L 2 L_2 L2 正则化为例进行演示,下图中:
    w ⃗ ∗ = arg ⁡ min ⁡ l ( w ˉ , b ) + λ 2 ∣ ∣ w ˉ ∣ ∣ 2 w ⃗ ~ ∗ = arg ⁡ min ⁡ l ( w ˉ ~ , b ) \begin{aligned}&\vec w*=\arg\min{l(\bar w,b)+\frac{\lambda}2||\bar w||^2} \\ &\tilde{\vec w}*=\arg\min{l(\tilde{\bar w},b)}\end{aligned} w =argminl(wˉ,b)+2λ∣∣wˉ2w ~=argminl(wˉ~,b)
    【动手学深度学习v2李沐】学习笔记07:权重衰退、正则化_第1张图片
    绿色的曲线为只优化损失值的情况,黄色曲线为加入了正则项的情况。正则项会将权重的值从原本离原点较远的较大值,拉扯到离原点较近的较小值,从而实现对参数大小的控制。

1.3 参数更新法则

  • 计算梯度:
    ∂ ∂ w ⃗ ( l ( w ⃗ , b ) + λ 2 ∣ ∣ w ⃗ ∣ ∣ 2 ) = ∂ l ( w ⃗ , b ) ∂ w + λ w ⃗ \frac{\partial}{\partial \vec w}\Big( l(\vec w,b)+\frac{\lambda}2||\vec w||^2 \Big)=\frac{\partial l(\vec w, b)}{\partial w}+\lambda \vec w w (l(w ,b)+2λ∣∣w 2)=wl(w ,b)+λw
  • 更新参数(时间 t):
    w ⃗ t + 1 = ( 1 − η λ ) w ⃗ t − η ∂ l ( w ⃗ t , b t ) ∂ w ⃗ t \vec w_{t+1}=(1-\eta \lambda)\vec w_t-\eta\frac{\partial l(\vec w_t, b_t)}{\partial \vec w_t} w t+1=(1ηλ)w tηw tl(w t,bt)
    • 通常 η λ < 1 \eta \lambda<1 ηλ<1,在深度学习中通常叫作权重衰退。这意味着每次更新参数时,现将原本的参数值缩小一些,再沿着梯度方向更新。

1.4 总结

  • 权重衰退通过 L 2 L_2 L2 正则项使得模型参数不会过大,从而控制模型复杂度。
  • 正则项权重是控制模型复杂度的超参数。

二、代码实现

2.1 从零开始实现

2.1.1 人工数据集

权重衰退是最广泛使用的正则化的技术之一。

import torch
from torch import nn
from d2l import torch as d2l

生成人工数据集:
y = 0.05 + ∑ i = 1 d 0.01 x i + ϵ where ϵ ∼ N ( 0 , 0.0 1 2 ) y=0.05+\sum_{i=1}^d0.01x_i + \epsilon \quad \text{where} \quad \epsilon\sim \mathcal{N}(0, 0.01^2) y=0.05+i=1d0.01xi+ϵwhereϵN(0,0.012)

n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
train_data = d2l.synthetic_data(true_w, true_b, n_train)
train_iter = d2l.load_array(train_data, batch_size, is_train=True)
test_data = d2l.synthetic_data(true_w, true_b, n_test)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)

2.1.2 模型参数

初始化模型参数

# 初始化模型参数
def init_params():
    w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)
    b = torch.zeros(1, requires_grad=True)
    return [w, b]

2.1.3 L 2 L_2 L2 范数惩罚

定义 L 2 L_2 L2 范数惩罚

# 定义L2范数惩罚
def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2

2.1.4 训练

本次的训练函数和之前训练函数的最大区别是:增加了输入参数lambd。我们用超参数lambd来控制正则项的重要程度。当lambd等于0时,相当于没有正则化;当lambd趋近于无穷时,相当于权重趋近于0.

# 训练函数
def train(lambd):
    w, b = init_params()
    net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
    num_epochs, lr = 100, 0.003
    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:
            # with torch.enable_grad():
            l = loss(net(X), y) + lambd * l2_penalty(w)
            l.sum().backward()
            d2l.sgd([w, b], lr, batch_size)
        if (epoch + 1) % 5 == 0:
            animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
                                     d2l.evaluate_loss(net, test_iter, loss)))
    d2l.plt.show()
    print('w的L2范数是:', torch.norm(w).item())

首先,我们令lambd=0,忽视正则化直接进行训练。

train(lambd=0)

此时发生了严重的过拟合,训练误差不断减小,但测试误差一直很高。结果如下图所示:
【动手学深度学习v2李沐】学习笔记07:权重衰退、正则化_第2张图片
使用权重衰减后,解决了过拟合的问题。

train(lambd=3)

【动手学深度学习v2李沐】学习笔记07:权重衰退、正则化_第3张图片

2.2 简洁实现

L 2 L_2 L2 正则化可以写在目标函数中,也可以写在训练算法里面
在简洁实现中,我们将权重衰减写在训练算法中

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:
            # with torch.enable_grad():
            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())
    d2l.plt.show()

类似从零开始实现,我们也分别在不使用和使用正则化的情况下进行训练。

train_concise(0)
train_concise(3)

不使用正则化的结果如下图所示:
【动手学深度学习v2李沐】学习笔记07:权重衰退、正则化_第4张图片

使用正则化的结果如下图所示:
【动手学深度学习v2李沐】学习笔记07:权重衰退、正则化_第5张图片
下一篇:【动手学深度学习v2李沐】学习笔记08:丢弃法

你可能感兴趣的:(深度学习笔记整理,深度学习,学习,机器学习)