权重衰退-代码

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

通过限制参数值的选择范围来控制模型容量:

min \iota (w,b)  subject to  \left \| w \right \|^{2}\leq \theta

  • 通常不限制偏移b(限不限制都差不多)
  • 小的\theta意味着更强的正则项

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

对每个\theta,都可以找到\lambda使得之前的目标函数等价于下面

min\iota (w,b)+\frac{\lambda }{2}\left \| w \right \|^{2}

可以通过拉格朗日乘子来证明

超参数\lambda控制了正则项的重要程度

  • \lambda=0:无作用
  • \lambda \rightarrow \infty ,w^{*}\rightarrow 0

之所以为柔性限制,是因为相比于硬性限制,可以通过调整超参数\lambda的值来增大或减小对loss的罚,控制正则项的重要程度,而非单纯的小于某个值。

三 参数更新法则

通常\eta \lambda < 1,在深度学习中通常叫做权重衰退。

不难发现,它与普通的w的时间t更新不同之处在于,w_{t}前面是1-\eta \lambda而非为1,1-\eta \lambda< 1即首先对 w_{t} 进行缩小,这就是所谓的权重衰退。

总结

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

代码:从零开始实现

导入必要的模块

%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  #训练数据集很小,特征数设置较大,这样容易过拟合
true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
#设置人造训练和测试数据集
train_data = d2l.synthetic_data(true_w, true_b, n_train)
test_data = d2l.synthetic_data(true_w, true_b, n_test)
#把训练和测试数据集loader进来
train_iter = d2l.load_array(train_data, batch_size)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)

初始化模型参数

def init_params():
    #w均值为0,方差为1,大小为200*1向量,获得梯度
    w = torch.normal(0, 1, size = (num_inputs, 1), requires_grad = True)
    b = torch.zeros(1, requires_grad = True) #b为1的标量,获得梯度
    return [w, b]

定义L2范数惩罚

def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2  #pow是阶乘,在阶乘后再求和,最后除以2,就是L2范数的定义

定义训练代码实现

def train(lambd): #超参数lambd,后面会指定
    w, b = init_params()  #初始化w和b
    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:  #在训练集数据迭代器中取出数据 X和真实标签 y
            l = loss(net(X), y) + lambd * l2_penalty(w) #变化的地方就是多加了一项:超参数lambd * L2惩罚项
            l.sum().backward() 
            d2l.sgd([w, b], lr, batch_size) #sgd优化,传入参数、学习率
        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())

忽略正则化直接训练

train(lambd = 0)  #没有规约(罚)

L2范数代表模型的复杂程度,L2越大越容易过拟合。
可以发现训练损失一直在减小,一直在拟合噪音,导致过于拟合,而测试损失几乎没有变化,说明模型在新的数据上的泛化能力较弱,两者之间的gap在越拉越大 。

使用权重衰减

train(lambd = 3) #添加罚

可以发现此时的测试损失在一直往下降,两者之间的gap在减小,过拟合现象有所减弱。
尝试多迭代一些轮数 或 把lambd设置的更大一些,效果可能会更好。

代码:简洁实现

前面的代码都一样。

def train_concise(wd):
    net = nn.Sequential(nn.Linear(num_inputs, 1)) #一个线性层的网络
    for param in net.parameters():
        param.data.normal_() 
    loss = nn.MSELoss() #均方差损失
    num_epochs, lr = 100, 0.003
    #使用torch.optim.SGD定义一个优化器trainer
    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'])
    # 在每个epoch中,遍历train_iter,对每个小批量数据(X, y)进行以下操作
    for epoch in range(num_epochs):
        for X, y in train_iter:
            trainer.zero_grad()  #优化器梯度清零
            l = loss(net(X), y) #计算损失
            l.backward()  #损失反向传播
            trainer.step() #再使用优化器来更新模型参数
        # 每5个epoch,计算训练和测试集上的损失,用animator将损失可视化
        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())

对于此行:trainer = torch.optim.SGD([{"params": net[0].weight,'weight_decay': wd},{"params": net[0].bias}],  lr=lr)

由于权重衰减在神经网络优化中很常用, 深度学习框架为了便于我们使用权重衰减,将权重衰减集成到优化算法中,以便与任何损失函数结合使用。 

使用torch.optim.SGD定义一个优化器trainer,该优化器的参数包括网络的权重、偏差和权重衰减系数wd,以及学习率lr。在实例化优化器时直接通过weight_decay指定weight decay超参数。 默认情况下,PyTorch会同时衰减权重weight和偏移bias,但这里只为权重设置了weight_decay,所以偏置参数b不会衰减。对于一个二维曲线,bias只是让曲线整体上下移动,并不能减小模型的复杂度,所以通常不需要对bias做正则化。

忽略正则化直接训练

#lambd = 0时
train_concise(0)

 使用权重衰减

#lambd = 3时
train_concise(3)

可以发现效果与从零开始实现时差不多 

更多权重衰退理论知识可以看这里->权重衰退

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值