[动手学习深度学习]12.权重衰退

1.介绍

权重衰退是常见的处理过拟合的方法

  • 控制模型容量方法
    1. 把模型控制的比较小,即里面参数比较少
    2. 使参数选择范围小
  • 约束就是正则项
    每个特征的权重都大会导致模型复杂,从而导致过拟合。
    控制权重矩阵范数可以使得减少一些特征的权重,甚至使他们权重为0,从而导致模型简单,减轻过拟合

使用均方范数作为硬性限制

权重衰退即是通过控制参数选择范围来控制模型容量的

  • 公式表达:
    m i n l ( w , b ) s u b j e c t t o ∣ ∣ w ∣ ∣ 2 ≤ θ min\ l(w,b)\ \ subject\ to ||w||^2 ≤ \theta min l(w,b) subject to∣∣w∣∣2≤θ
    l l l:损失函数
    w w w:参数
    b b b:偏移
    在最小化损失函数时加上限制,使参数的平方和小于一个特定的值,也就说明每个参数的值要小于 θ \theta θ开根
    通常不限制偏移b
    小的 θ \theta θ意味着更强的正则项

使用均方范数作为柔性限制

  • Df:对每个 θ \theta θ,都可以找到 λ \lambda λ使得之前的目标函数等价于下面:
    m i n l ( w , b ) + λ 2 ∣ ∣ w ∣ ∣ 2 min \ l(w,b)+\frac{\lambda}{2} || w||^2 min l(w,b)+2λ∣∣w∣∣2
    (可以通过拉格朗日乘子来证明)
  • 超参数 λ \lambda λ控制了正则项的重要程度
    • λ = 0 \lambda=0 λ=0:无作用(当 λ = 0 \lambda=0 λ=0时,即没有后面的限制,相当于上一个公式里 θ = ∞ \theta=\infty θ=∞)
    • λ → ∞ , w ∗ → 0 \lambda \rightarrow \infty, w^* \rightarrow0 λ→∞,w∗→0:相当于上面 θ → 0 \theta \rightarrow0 θ→0,也就使 w ∗ → 0 w^* \rightarrow0 w∗→0

想通过控制模型参数使模型不要太复杂时,可以通过增加 λ \lambda λ来满足需求(这里 λ \lambda λ是一个平滑的,不像以前的硬性限制)

  • 这里可以理解拉格朗日乘子法:
    • 拉格朗日乘子法原本是用于解决约束条件下的多元函数极值问题。举例,求f(x,y)的最小值,但是有约束C(x,y) = 0。乘子法给的一般思路是,构造一个新的函数g(x,y,λ) = f(x,y) +λC(x,y),当同时满足g'x = g'y = 0时,函数取到最小值。这件结论的几何含义是,当f(x,y)与C(x,y)的等高线相切时,取到最小值。
    • 具体到机器学习这里, C ( x , y ) = w 2 − θ C(x,y) = w^2 -θ C(x,y)=w2−θ。所以视频中的黄色圆圈,代表不同θ下的约束条件。θ越小,则最终的parameter离原点越近。
  • 绿色的线就是原始损失函数l的等高线,优化损失函数l的最优解(波浪号即最优解)在中心位置
  • 当原始损失加入 λ 2 \frac{\lambda}{2} 2λ项之后,这个项是一个二次项,假如w就两个值,x1(横轴)x2(纵轴),则在图上这个二次项的损失以原点为中心的等高线为橙色的图所示。所以合并后的损失为绿色和黄色的线加一起的损失
  • 当加上损失项后,可知原来最优解对应的二次项的损失特别大,因此原来的最优解不是加上二次项后的公式的最优解了。若沿着橙色的方向走,原有l损失值会大一些,但是二次项罚的损失会变小,当拉到平衡点以内时,惩罚项减少的值不足以原有l损失增大的值,这样w*就是惩罚项后的最优解
  • 损失函数加上正则项成为目标函数,目标函数最优解不是损失函数最优解。
    正则项就是防止达到损失函数最优导致过拟合,把损失函数最优点往外拉一拉。 鼓励权重分散,将所有特征运用起来,而不是依赖其中的少数特征,并且权重分散的话他的内积就小一点
  • l2正则项会对大叔之的权值进行惩罚

回顾平方损失:

相对原来的权重更新,再减去一个值后,使得这个权重更进一步减小,这样会导致这个权重所占的比例进一步减小

参数更新法则


2. 代码实现(手动实现)

python 复制代码
%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l

像以前一样生成一些人工数据:

python 复制代码
n_train, n_test, num_inputs, batch_size = 20,100,200,5
# 数据越简单,模型越复杂,越容易过拟合。
# num_inputs:特征维度

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)
test_data = d2l.synthetic_data(true_w, true_b, n_test)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)

# 初始化模型参数
def init_params():
    w=torch.normal(0,1,size=(num_inputs,1), requires_grad=True)
        # 均值为0,方差为1,长度时num_inputs*1的向量,需要梯度
    b=torch.zeros(1,requires_grad=True)
        # b:为全0的标量
    return [w,b]

# 定义L2范数惩罚项(核心)
def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2
    # 注意不要把lambda写进去,因为要写在外面

def train(lambd):
    w, b = init_params() # 初始化模型参数
    net, loss = lambda X:d2l.linreg(X,w,b), d2l.squared_loss
    # net做了个很简单的线性回归
    # 损失函数用平方损失
    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)  # L2范数惩罚项
            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))
            )
    print('w的L2范数是:', torch.norm(w).item())

3.简单实现(使用框架)

python 复制代码
def train_concise(wd):
    net=nn.Sequential(nn.Linear(num_inputs, 1))
    for param in net.parameters():
        param.data.normal_()
    loss = nn.MSELoss()
    num_epoch, lr = 100,0.003
    trainer = torch.optim.SGD(
        [{"params":net[0].weight,"weight_decay":wd},{'params':net[0].bias}], 
        lr=lr)
    # 惩罚项既可以写在目标函数里,也可以写在训练算法里,每一次更新之前把当前的w乘以衰退因子weight_decay

    animator=d2l.Animator(xlabel='epochs',ylabel='loss',yscale='log',xlim=[5, num_epoch],legend=['train','test'])
    for epoch in range(num_epoch):
        for X,y in train_iter:
            with torch.enable_grad():
                trainer.zero_grad()
                l = loss(net(X), y)
            l.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())
相关推荐
麻花地9 分钟前
RealTalk_AI:基于 Qwen3.5 LiveTranslate 的实时双向语音翻译工具
人工智能
张继雁11 分钟前
不同类型磨削液使用安全注意事项
人工智能·深度学习·安全·业界资讯·远程工作·空间计算
Mr数据杨11 分钟前
用会计信息做GDP增速预测的时序回归实战解析
人工智能·数据分析·kaggle竞赛
ClouGence13 分钟前
一句话直出可运行程序!GLM‑5.3 系列多任务实测
人工智能·agent·ai编程
Mr数据杨19 分钟前
企业年报文本变化预测实战 从文本回归到信息披露分析
人工智能·数据分析·kaggle竞赛
微软技术分享21 分钟前
Apache 2.4.68 编译安装与配置
人工智能·apache·知识图谱
一只小阿乐32 分钟前
java 语法学习 1
java·开发语言·学习
dear_bi_MyOnly32 分钟前
函数模块化:企业级项目高效之道
c++·后端·学习
QQ142207844933 分钟前
知识库管理和工作学习系统--guada_ai
学习