动手学深度学习13-权重衰减

摘要:
正则化将惩罚项添加到模型损失函数中,以使学习到的模型参数值更小,这是处理过度拟合的常用方法。我们首先描述L2范数正则化,然后解释为什么称为权重衰减。当λ惩罚项较大时,损失函数中惩罚项的权重较大,这通常使学习的权重参数的元素接近0;当λ设置为0时,惩罚项完全无效。高维线性回归实验高纬度线性过拟合数据,并使用权重衰减处理过拟合。

上一节中提提到的过拟合现象,在模型的训练误差远小于测试集上的误差。虽然增大训练接数据集可以减轻过拟合,但是获得额外的训练数据往往代价过大,本节介绍过拟合常用的方式:权重衰减(weight decay)。

权重衰减

权重衰减等价于L2范数正则化(regularzation)。正则化通过模型损失函数添加惩罚项使学到的模型参数值较小,是应对过拟合的常用方法,我们先描述L2范数正则化,再解释它为何称为权重衰减。
L2范数正则化在模型原损失函数的基础上添加L2范数惩罚项,从而得到训练得到所需的最小化函数。L2范数惩罚项是模型权重参数每个元素的平凡和与一个正的常数的乘积。以线性回归中线性回归损失函数
(l(w_{1},w_{2},b )= frac{1}{n}sum_{i=1}^{n}frac{1}{2}(x_{1}^{(i)}w_{1}+x_{2}^{(i)}w_{2} +b - y^{(i)})^{2}) ,其中w1,w2是权重参数,b为偏置,样本i的输入为(x_{1}^{(i)},x_{2}^{(i)},标签是y^{(i)},样本数为n,将权重参数向量)w=[w_{1},w_{2}]表示,$带有L2范数惩罚项的新损失函数为

[l(w_{1},w_{2},b )+frac{lambda}{2n}|oldsymbol{w}|^{2} ]

其中超参数为λ>0。当权重参数均为0时,惩罚项最小。当λ较大时,惩罚项在损失函数中的权重较大,通常会使学习到的权重参数的元素较接近0;当λ设为0时,惩罚项完全不起作用。上式中L2范数平方(|w|^{2}展开后得到w_{1}^{2}+w_{2}^{2})
有了L2范数惩罚项后,在小批量随机梯度下降汇总,我们将线性回归一节中权重w1和w2的迭代方式更改为

[egin{array} { l } { w _ { 1 } leftarrow left( 1 - frac { eta lambda } { | mathcal { B } | } ight) 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 ) } ight) } \ { w _ { 2 } leftarrow left( 1 - frac { eta lambda } { | mathcal { B } | } ight) 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 ) } ight) } end{array} ]

可见,L2范数正则化令权重w1和w2先自乘一个小于1的数,再减去不含惩罚项的梯度。因此L2范数正则化又叫做权重衰减。有时候在惩罚项中也添加偏置元素的平方和。

高维线性回归实验

高纬线性过拟合数据,并使用权重衰减来应对过拟合。设定特征的维度为p,对于训练和测试数据中特征为(x_{1},x_{2},x_{3},,,,,x_{p})的任一样本,我们使用如下的形象函数来生成该样本的标签:
(y = 0.05+sum_{i=1}^{p}0.01x_{i}+epsilon, 其中噪音项epsilon)服从均值为0,标准差为0.01的正态分布。为了较容易的观察过拟合,我们考虑高纬线性回归问题,设定维度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:]
从零开始实现

从零开始实现权重衰减的方法,在目标函数后添加L2范数惩罚项来实现权重衰减。

初始化模型参数
def init_params():
    w = torch.randn((num_inputs,1),requires_grad=True)
    b = torch.zeros(1,requires_grad=True)
    return [w,b]
定义L2范数惩罚项
def l2_penalty(w):
    return (w**2).sum()/2
def linreg(X,w,b):
    return torch.mm(X,w)+b


def squared_loss(y_hat,y):
    return (y_hat-y.view(y_hat.size()))**2/2

def sgd(params,lr,batch_size):
    for param in params:
        param.data -=lr* param.grad/batch_size  

        
        #定义作图函数 semiplgy 其中y轴使用了对数尺度
def semilogy(x,y,x_label,y_label,x2=None,y2=None,legend=None,figsize=(3.5,2.5)):
    d2l.set_figsize(figsize)
    d2l.plt.xlabel(x_label)
    d2l.plt.ylabel(y_label)
    d2l.plt.semilogy(x,y)
    if x2 and y2:
        d2l.plt.semilogy(x2,y2,linestyle=':')
        d2l.plt.legend(legend)

定义训练和测试
batch_size,num_epochs ,lr =1,100,0.003
net,loss = linreg,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:

            #l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
            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()
            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())
        
    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())
fit_and_plot(lambd=0)
L2 norm of w 13.360041618347168

动手学深度学习13-权重衰减第1张

使用权重衰减
fit_and_plot(lambd=3)
# 训练集的误差有所提高,但是测试集上的误差同样下降,过拟合的现象在一定程度上缓解,
#另外权重参数L2范数比不适用权重衰减更小,此时的权重参数更加接近0
L2 norm of w 0.0406213141977787

动手学深度学习13-权重衰减第2张

pytorch简洁实现

这里我们直接在构造优化器实例时通过weight_decay参数来指定权重衰减超参数,默认是pytorch会对权重和偏置同事衰减。我们可以分别对权重和偏置购置优化器实例,从而只对权重衰减。

def fit_and_plot_pytorch(wd):
    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_b.step()
            optimizer_w.step()
            
        train_ls.append(loss(net(train_features),train_labels).mean().item())
        test_ls.append(loss(net(test_features),test_labels).mean().item())
    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())
fit_and_plot_pytorch(0)
L2 norm of w 13.489049911499023

动手学深度学习13-权重衰减第3张

fit_and_plot_pytorch(3)
L2 norm of w 0.13610510528087616

动手学深度学习13-权重衰减第4张

小结
  • 正则化通过为模型损失函数中添加惩罚项使学习到的模型参数值较小,是对应过拟合的常用方法
  • 权重衰减等于L2范数正则化,通常使学习到的权重参数接近0
  • 权重衰减可以通过优化器的weight_decay超参数来指定
  • 可以定义多个优化器实例对不同的模型参数使用不同的迭代方法

免责声明:文章转载自《动手学深度学习13-权重衰减》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇java修饰符的总结httprunner下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

K8s集群认证之RBAC

kubernetes认证,授权概括总结: RBAC简明总结摘要:API Server认证授权过程:subject(主体)----->认证----->授权【action(可做什么)】------>准入控制【Object(能对那些资源对象做操作)】认证:有多种方式,比较常用的:token,tls,user/password账号:k8s中账号的...

JavaScript 流程控制-循环

1.循环 循环目的 在实际问题中,有许多具有规律性的重复操作,因此在程序中要完成这类操作就需要重复执行某些语句 JS中的循环 在JS中,主要有三种类型的循环语句: for循环 while循环 do...while循环 2.for 循环 在程序中,一组被重复执行的语句被称之为循环体,能否继续重复执行,取决于循环的终止条件。由循环体及循环的终止条件组成的语...

图像风格迁移原理

所谓图像风格迁移,是指利用算法学习著名画作的风格,然后再把这种风格应用到另外一张图片上的技术。著名的图像处理应用Prisma是利用风格迁移技术,普通用户的照片自动变换为具有艺术家风格的图片。 一、图像风格迁移的原理 1、原始图像风格迁移的原理 在学习原始的图像风格迁移之前,可以在先看看ImageNet图像识别模型VGGNet(微调(Fine-tune)原理...

TestNG入门——注解之Before/After

注解是java 5新增的功能,可使用于类,方法,变量,testNG包提供的注解功能请见下表 1、@BeforeSuite or @AfterSuite  被注解的方法,将在整个测试套件之前 or 之后执行。 2、@BeforeTest or @AfterTest 被注解的方法,将在测试套件内所有用例执行之前 or 之后执行。 3、@BeforeGroups...

如何用webpack搭建vue项目?本文案例详解

前言:都2020年了,感觉是时候该学一波webpack了,趁着最近有时间,就学了一下,等把官网上的webpack结构和模块大概看了一遍之后,就感觉可以开始搭个项目实战一下了,从0开始,一步步记录下来 使用版本: webpack4.x 1.包含插件和loader * html: html-webpack-plugin clean-webpack...

Bucardo使用文档-lottu

官网地址 一、Bucardo介绍 Bucardo 是基于表复制的系统 Bucardo 可以实现PostgreSQL数据库的双master/多master的方案 Bucardo的核心是一个Perl守护进程,它侦听通知请求并对其进行操作,方法是连接到远程数据库并来回复制数据。 守护进程需要的所有特定信息都存储在主bucardo数据库中,包括复制所涉及的所有数据...