跟着李沐学AI学习笔记--权重衰退

%matplotlib inline# 导包
import torch
from torch import nn
from d2l import torch as d2l
# 定义所需的train个数,test个数,输入的维度,样本数量,由于这里是想使用正则化来减少过拟合,所以训练数据比较小
n_train,n_test,num_input,batch_size = 20,100,200,5
# 产生权重和偏置的大小
true_w,true_b = torch.ones((num_input,1)) * 0.01 ,0.05
# 产生训练和测试数据,d2l.synthetic_data:产生n_train个y,y = true_w * x + true_b + noise,返回x,y
train_data = d2l.synthetic_data(true_w,true_b,n_train)
test_data = d2l.synthetic_data(true_w,true_b,n_test)
# 数据集进行mini_batch,自动分成batch_size个样本
train_iter = d2l.load_array(train_data,batch_size)
test_iter = d2l.load_array(test_data,batch_size,is_train=False)
# 定义所需参数
def init_params():
    # 产生平均值为0,偏差为1,维度大小为(num_input行,1列)的权重,是否需要建立梯度为True
    w = torch.normal(0,1,size=(num_input,1),requires_grad=True)
    # 产生一个值为0的偏置,是否需要建立梯度为True
    b = torch.zeros(1,requires_grad=True)
    return w,b
# 定义l2项的正则,用来一会在loss函数后面加上
def l2_penalty(w):
    return torch.sum(w ** 2)/2
"""
minloss = (y_hat - y) + w ** 2 * lambd
定义训练函数
这里简化了loss函数和正则项,loss为(y_hat - y),l2正则项为(w ** 2 * lambd)
lambd是正则化罚的力度,这个值越大,那么loss中l2正则项也会越大,
为了使loss尽可能min,那么就要将l2尽可能小下来,而超参数lambd固定,更新时就只好降低w的值
w的值逐渐降低,模型的弹性也会逐渐降低,可以理解为由于参数对于输出的影响逐渐下降导致模型的“参数”变少,使得模型复杂度降低,从而减少过拟合
当w下降到一定值时,由于降低l2所带来的loss的减少无法弥补由于模型弹性减少带来的弥合减少造成loss的增加,在反向更新中又会加大w的值,
最终趋于稳定,w停留在一个值上,
而之所以不直接去掉产生w,是因为w可以给模型增加一定的弹性,这个弹性可以在训练中有机器自己决定一部分的弹性,而非全部又人为定,
另外个人认为w还可以使得loss曲线更加平滑,而不是崎岖不平
"""
def train(lambd):
    # w,b赋值
    w,b = init_params()
    # 使用匿名函数返回net(一个线性函数),loss(损失函数)两个实例,
    # 匿名函数:定义一个临时函数,输入值为lambda后面的X,输出为后面的函数体,不需要加return,这里返回的是两个实例
    net,loss = lambda X:d2l.linreg(X,w,b),d2l.squared_loss
    # 定义训练轮数和学习率
    num_epochs,lr = 100,0.003
    # 实例化一个d2l库中的绘图函数,具体参数不了解
    animator = d2l.Animator(xlabel='num_epochs',ylabel='loss',yscale='log',
                            xlim=[5,num_epochs],legend=['train','test',])
    # 开始训练
    for epoch in range(num_epochs):
        # 取出train_iter中的x,y
        for x,y in train_iter:
            # 计算loss,并在项中加入正则
            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)))
    # 打印最后一轮结束后w的范数,就是w这个张量的值的大小
    print("w的l2范数是:",torch.norm(w).item())
train(lambd=4)
#简洁表达的过程大体上没什么不一样,lambd改成了wd
def train_concise(wd):
    #net直接使用nn库中自带的linear,传入输入维度(num_input)和输出维度1
    net = nn.Sequential(nn.Linear(num_input, 1))
    #取出net中的参数,按照正态分布对参数进行赋值
    for param in net.parameters():
        param.data.normal_()
    #实例一个损失函数,直接使用nn中的MSELoss,reduction=‘none’表示直接返回n分样本的loss
    loss = nn.MSELoss(reduction='none')
    #轮数,学习率
    num_epochs, lr = 100, 0.003
    #优化器直接使用optim中的sgd,只不过将wd传入weight_decay表示正则,这里的包含关系是SGD(【参数{参数w,罚},{参数b}】,学习率)
    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:
            #d2l中的训练函数已经帮我们清空过梯度了,但是这里的优化器是用的nn中自带的sgd,不会帮我们自动清空,所以我们要先手动清零
            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())
train(lambd=5)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值