李沐之权重衰退

目录

1.理论

2.代码

2.1从零开始实现权重衰减

2.1.1忽略正则化直接训练

2.1.2使用权重衰减

2.2 简洁实现


1.理论

李沐之权重衰退_第1张图片

李沐之权重衰退_第2张图片

权重衰退就是控制参数值的范围来控制模型容量的。w的每个元素的值都应该小于\theta开根号。当\theta等于0时,所有的偏移都等于0

李沐之权重衰退_第3张图片

\lambda趋向于无穷的时候,等价于前面的\theta趋向于0,使得最优解^{^{}}也趋向于0。假设想让模型的复杂度比较低,就可以增加\lambda来满足我的需求

李沐之权重衰退_第4张图片

坐标轴表示的是w1和w2,黄色圆圈是惩罚项(L2 loss有平方项,相当于2次函数,x1^2+x2^2取最小值),绿色圆圈是原来的损失项。对于黄线来说绿色的中心点,也就是原来损失的最优解很大。在原点处,L2  loss对于损失的值的拉伸是比较小的,梯度比较小。对于离原点很远的点,L2  loss损失函数对损失的拉伸是比较大的,可以快速的使损失变小,梯度较大。所以罚会将原来的绿色中心的最优解往下拉,拉到两个颜色的线如图相交的地方。这个地方是一个平衡点,往下拉罚的损失会变小但是绿色的损失过于大,以至于罚的损失不足以抵消增加的绿色的损失。往上走同理。

引用视频的评论解释:(拉格朗日乘子法原本是用于解决约束条件下的多元函数极值问题。举例,求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 -θ。所以视频中的黄色圆圈,代表不同θ下的约束条件。θ越小,则最终的parameter离原点越近。)

李沐之权重衰退_第5张图片

李沐之权重衰退_第6张图片

主要的变化就在-\eta \lambdaw_{t},其余和以前相比没有变化。一般来说,\eta学习率是一个小于1 的正常数。先把w_{t}乘以一个小于1 的正常数,也就是把w_{t}的值先变小一点再沿着梯度的反方向再走一下。为什么叫做权重衰退,每次更新的时候因为\lambda的引入,使得在更新前我们先把当前的权重作了一次放小,所以认为有一次衰退。

李沐之权重衰退_第7张图片

2.代码

2.1从零开始实现权重衰减

"""高维线性回归"""
%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l

#像以前一样生成一些数据
n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
#把训练数据选择20很小容易发生过拟合,测试选择100,输入特征的维度200(w),批量大小5
true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
#真实的w是大小全为0.01的形状为200*1的向量,b是大小是0.05的形状是200*1的向量
train_data = d2l.synthetic_data(true_w, true_b, n_train)
#d2l.synthetic_data的参数有w,b和生成样本数量
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)
    #w初始化成均值为0,方差为1,大小为200*1的向量,需要梯度
    b = torch.zeros(1, requires_grad=True)
    #b是全0的标量
    return [w, b]

"""定义2范数惩罚"""
def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2
    #2范数就是平方,求和,开根号,这里不需要开根号是因为根据公式我们求的是范数的平方
    #除以2是因为求梯度的时候刚好消去这个2(也是根据公式来的)


"""定义训练代码实现"""
def train(lambd):
    w, b = init_params()
    #出时还权重和偏置
    net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
    #网络模型用以前定义的线性回归,损失用平方损失函数(因为这是个回归问题)
    #lambda用于定义匿名函数的关键字,语法:lambda 传入参数:一行执行代码
    num_epochs, lr = 100, 0.003
    #训练数据比较小20个样本所以训练周期选长一点迭代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):
    #迭代100次
        for X, y in train_iter:
        #从训练数据迭代器取出输入X和标签y
            # 增加了L2范数惩罚项,
            # 广播机制使l2_penalty(w)成为一个长度为batch_size的向量
            l = loss(net(X), y) + lambd * l2_penalty(w)
            #把网络模型的输出和标签y算损失,唯一的区别就是加上超参数lambd乘以L2loss惩罚
            l.sum().backward()
            #算梯度
            d2l.sgd([w, b], lr, batch_size)
            #更新参数
        if (epoch + 1) % 5 == 0:
        #每5个周期显示一次
            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())
    #打印一下l2范数,def norm(input, p="fro", dim=None, keepdim=False, out=None, dtype=None):
    #默认是l2范数,注意这里的l2范数没有公式里的平方了



2.1.1忽略正则化直接训练

train(lambd=0)

结果输出:

w的L2范数是: 13.97721004486084

李沐之权重衰退_第8张图片

训练的损失一直往下降,但是测试的时候损失完全没有变,在这两个损失之间的距离太大,明显发生了过拟合。

2.1.2使用权重衰减

train(lambd=3)
输出结果:w的L2范数是: 0.3624069094657898

李沐之权重衰退_第9张图片

有一定效果,罚使得W不会变得特别大,对于过拟合,训练损失到了一定的地方就不会往下走了。测试还是在继续往下降,因为是比较小的20个样本的训练集,可能过拟合是无法避免的。后续可以尝试多迭代很多次看测试损失是否会变得更小,或者把\lambda调的更大一点,认为还有一定的过拟合,意味着范数还是太大。但是\lambda太大,导致w平衡点更小,容易欠拟合。

2.2 简洁实现

def train_concise(wd):
    net = nn.Sequential(nn.Linear(num_inputs, 1))
    for param in net.parameters():
        param.data.normal_()
        ##初始化参数 默认mean=0,std=1
    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:
            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())

罚可以写在目标函数里面,也可以写在训练算法里面。(简洁实现:1.建立网络 2. 损失函数 3. 优化器(根据反向传播求得梯度 用优化器更具体都来更新参数) 4. 从训练集取出数据,进行训练,先梯度清0,算损失,反向传播,然后优化)

train_concise(0)

输出结果:

w的L2范数: 13.727912902832031

李沐之权重衰退_第10张图片

train_concise(3)

输出结果:

w的L2范数: 0.3890590965747833

李沐之权重衰退_第11张图片

参考:

Python中torch.norm()用法解析_python_脚本之家
PyTorch官方中文文档:torch.optim 优化器参数_torch.optim.lbfgs参数-CSDN博客

你可能感兴趣的:(深度学习,算法,机器学习,人工智能)