• pytorch深度学习实战lesson13


    第十三课 权重衰退

    目录

    理论部分

    实践部分

    从零开始:

    简洁实现:


    理论部分

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

    之前讲过控制模型容量的方法是:

    1、把模型变小点,这样参数就少;

    2、让每个参数值的可选范围小一些。

    那么权重衰退就是上述的第二种方法。

    相比于“使用均方范数作为硬性限制”,其实最常用的是“使用均方范数作为柔性限制”。

    上面的公式没有约束条件了

    λ控制着正则项的重要程度,当λ为零时,正则项也就是0了,也就是说正则项不起作用了,此时等价于“使用均方范数作为硬性限制”中的θ趋于无穷(小的θ才意味着更强的正则项);

    反之,当λ为无穷时,正则项也就是无穷了,也就是说正则项非常起作用,此时等价于“使用均方范数作为硬性限制”中的θ等于0,因为当θ等于零时,对于前者来说它的w也是0了。

    下面将演示一下,权重衰退对最优解的影响。

    假设绿线是损失l,对于损失来说绿点是最优解。黄线是罚也就是(λ/2)*||w||^2黄线的横纵坐标分别是w1和w2。

    在这种情况下,虽然w~*对于损失来说是最优的,但对罚来说不是最优解,也就是说罚会对w~*有一个左下的拉动力,,直到拉到w*处时,损失对w*以及罚对w*的拉动力平衡了,这才是两者的平衡点。

    罚的引入会将最优解拉向原点,对于最优的值,它的绝对值会变小,一旦绝对值变小,且把所有的最优解都向原点拉伸的话,对于整个模型来讲,模型的复杂度就会降低。

    权重衰退名字的由来或许可以从上图中看出来。

    我们可以回忆一下之前求梯度的方法。对带罚的式子求梯度的话会多出一项λw。

    然后

    把梯度的结果代入进去,得到Wt+1的整体式子。

    从式子中我们可以发现,其主要“权重衰退”的“衰退”体现在wt前的系数是小于1的,因此当权重乘上一个小于1的数的话,就会做到相应的衰退。

    实践部分

    从零开始:

    代码:

    1. #权重衰减是最广泛使用的正则化的技术之一
    2. #%matplotlib inline
    3. import torch
    4. #from torch import nn
    5. from d2l import torch as d2l
    6. import matplotlib.pyplot as plt
    7. #像以前一样生成一些数据(人工数据集)
    8. #训练样本,测试样本,输入数量(特征维度),批量大小
    9. n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
    10. #真实的w和b,分别是0.01*全1的向量,b是0.05
    11. true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
    12. train_data = d2l.synthetic_data(true_w, true_b, n_train)#构造训练数据集
    13. train_iter = d2l.load_array(train_data, batch_size)#给训练数据集分配批量
    14. test_data = d2l.synthetic_data(true_w, true_b, n_test)#构造测试数据集
    15. test_iter = d2l.load_array(test_data, batch_size, is_train=False)#给测试数据集分配批量
    16. #初始化模型参数
    17. def init_params():
    18. w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)#均值为0,方差为1,长度为200*1向量的初始化w。
    19. b = torch.zeros(1, requires_grad=True)#
    20. return [w, b]
    21. #定义L2范数惩罚
    22. def l2_penalty(w):
    23. return torch.sum(w.pow(2)) / 2
    24. #定义训练代码实现
    25. def train(lambd):
    26. w, b = init_params()#初始化权重和偏移
    27. net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss#lambda定义了一个 net(X)函数
    28. num_epochs, lr = 100, 0.003
    29. animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
    30. xlim=[5, num_epochs], legend=['train', 'test'])
    31. for epoch in range(num_epochs):#每次数据迭代
    32. for X, y in train_iter:#每次拿出x和y
    33. #with torch.enable_grad():
    34. l = loss(net(X), y) + lambd * l2_penalty(w)#带罚的损失
    35. l.sum().backward()
    36. d2l.sgd([w, b], lr, batch_size)
    37. if (epoch + 1) % 5 == 0:
    38. animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
    39. d2l.evaluate_loss(net, test_iter, loss)))
    40. print('w的L2范数是:', torch.norm(w).item())
    41. #忽略正则化直接训练
    42. train(lambd=0)#当没有罚时,范数是12.877
    43. plt.show()#从它生成的图中,我们可以看到,训练误差在一直减小,但测试误差却没怎么变,也就是说,
    44. # 当没有罚时,它甚至把噪声都拟合得很好了,因此会导致测试误差居高不下。
    45. #使用权重衰减
    46. train(lambd=3)#当有罚时,范数是0.380
    47. plt.show()#从它生成的图中,我们可以看到,训练误差在一直减小,测试误差也进行了相应的减小,也就是说,
    48. # 当有罚时,权重的可选范围变小了,因此会过滤掉多余的噪声,这就导致测试误差会有相应的减小。
    49. # 但当lambd参数调的过大的话,权重的可选范围会少之又少,导致本该用到的权值参数都被过滤了,这会导致欠拟合。

    简洁实现:

    代码:

    1. #简洁实现
    2. import torch
    3. from torch import nn
    4. from d2l import torch as d2l
    5. import matplotlib.pyplot as plt
    6. #像以前一样生成一些数据
    7. n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
    8. true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
    9. train_data = d2l.synthetic_data(true_w, true_b, n_train)
    10. train_iter = d2l.load_array(train_data, batch_size)
    11. test_data = d2l.synthetic_data(true_w, true_b, n_test)
    12. test_iter = d2l.load_array(test_data, batch_size, is_train=False)
    13. def train_concise(wd):
    14. net = nn.Sequential(nn.Linear(num_inputs, 1))
    15. for param in net.parameters():
    16. param.data.normal_()
    17. loss = nn.MSELoss()
    18. num_epochs, lr = 100, 0.003
    19. trainer = torch.optim.SGD([{
    20. "params": net[0].weight,
    21. 'weight_decay': wd}, {#这个相当于是λ,这就不用手动加罚了
    22. "params": net[0].bias}], lr=lr)
    23. animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
    24. xlim=[5, num_epochs], legend=['train', 'test'])
    25. for epoch in range(num_epochs):
    26. for X, y in train_iter:
    27. with torch.enable_grad():
    28. trainer.zero_grad()
    29. l = loss(net(X), y)
    30. l.backward()
    31. trainer.step()
    32. if (epoch + 1) % 5 == 0:
    33. animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
    34. d2l.evaluate_loss(net, test_iter, loss)))
    35. print('w的L2范数:', net[0].weight.norm().item())
    36. #这些图看起来和我们从零开始实现权重衰减时的图相同
    37. train_concise(0)
    38. plt.show()
    39. train_concise(3)
    40. plt.show()

    拓展:

    把L2范数换成L1范数,可以尝试练习一下。

    代码:

    1. #权重衰减是最广泛使用的正则化的技术之一
    2. #%matplotlib inline
    3. import torch
    4. #from torch import nn
    5. from d2l import torch as d2l
    6. import matplotlib.pyplot as plt
    7. #像以前一样生成一些数据(人工数据集)
    8. #训练样本,测试样本,输入数量(特征维度),批量大小
    9. n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
    10. #真实的w和b,分别是0.01*全1的向量,b是0.05
    11. true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
    12. train_data = d2l.synthetic_data(true_w, true_b, n_train)#构造训练数据集
    13. train_iter = d2l.load_array(train_data, batch_size)#给训练数据集分配批量
    14. test_data = d2l.synthetic_data(true_w, true_b, n_test)#构造测试数据集
    15. test_iter = d2l.load_array(test_data, batch_size, is_train=False)#给测试数据集分配批量
    16. #初始化模型参数
    17. def init_params():
    18. w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)#均值为0,方差为1,长度为200*1向量的初始化w。
    19. b = torch.zeros(1, requires_grad=True)#
    20. return [w, b]
    21. #定义L1范数惩罚
    22. def l1_penalty(w):
    23. return torch.sum(torch.abs(w))
    24. #定义训练代码实现
    25. def train(lambd):
    26. w, b = init_params()#初始化权重和偏移
    27. net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss#lambda定义了一个 net(X)函数
    28. num_epochs, lr = 100, 0.003
    29. animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
    30. xlim=[5, num_epochs], legend=['train', 'test'])
    31. for epoch in range(num_epochs):#每次数据迭代
    32. for X, y in train_iter:#每次拿出x和y
    33. #with torch.enable_grad():
    34. l = loss(net(X), y) + lambd * l1_penalty(w)#带罚的损失
    35. l.sum().backward()
    36. d2l.sgd([w, b], lr, batch_size)
    37. if (epoch + 1) % 5 == 0:
    38. animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
    39. d2l.evaluate_loss(net, test_iter, loss)))
    40. print('w的L2范数是:', torch.norm(w).item())
    41. #忽略正则化直接训练
    42. train(lambd=0)#当没有罚时,范数是12.877
    43. plt.show()#从它生成的图中,我们可以看到,训练误差在一直减小,但测试误差却没怎么变,也就是说,
    44. # 当没有罚时,它甚至把噪声都拟合得很好了,因此会导致测试误差居高不下。
    45. #使用权重衰减
    46. train(lambd=3)#当有罚时,范数是0.380
    47. plt.show()#从它生成的图中,我们可以看到,训练误差在一直减小,测试误差也进行了相应的减小,也就是说,
    48. # 当有罚时,权重的可选范围变小了,因此会过滤掉多余的噪声,这就导致测试误差会有相应的减小。
    49. # 但当lambd参数调的过大的话,权重的可选范围会少之又少,导致本该用到的权值参数都被过滤了,这会导致欠拟合。

    w的L2范数是: 14.286995887756348
    w的L2范数是: 0.07397375255823135

     

     可以看出,对于没有罚的情况来讲,L1,L2的效果是一样的;

    对于有罚的情况来讲,L1的效果好一些。

  • 相关阅读:
    状态(State)模式
    nodejs+vue+elementui+express酒店管理系统
    GitHub爆火Java核心知识笔记,入门进阶涨薪如探囊取物
    国产软件迅速崛起,这应该是最适合国内程序员的API管理神器!
    git 配置公钥
    计算机网络课程设计——中小型网络工程设计
    Unity has changed its pricing model
    实操Hadoop大数据高可用集群搭建(hadoop3.1.3+zookeeper3.5.7+hbase3.1.3+kafka2.12)
    Linux学习6—文件的查找与压缩
    常见的SQL语句及函数
  • 原文地址:https://blog.csdn.net/weixin_48304306/article/details/127843397