深度学习笔记010WeightDecay权重衰退

权重衰退——最常见的处理过拟合的方法
通过限制参数的选择范围来控制模型容量
对于权重W和偏移b,让||W||² <= θ
小的θ意味着更强的正则项
通常不限制b,限不限制都差不多

因为W越大,往往对噪声的放大就更大,所以我们需要适当限制W大小,达到控制噪音的目的,也就解决了过拟合问题。

 

但通常不直接用上面的式子,而是:

     

 

 加入的一项通常叫做罚,penalty

想要模型复杂度降低,直接增加拉马达即可。

 

正则化:能够减少泛化误差的方法都叫正则。

下图的前导知识如下:

理解正则化很棒的视频:https://www.bilibili.com/video/BV1Z44y147xA?spm_id_from=333.999.0.0

理解梯度:https://blog.csdn.net/keeppractice/article/details/105620966

 

 加入这个罚penalty,画出penalty的图像,可以看到我们将W的范围限制到了原点周围的圆里,这样就相当于把W往原点附近拉扯,W就会变小,模型容量就会降低,从而模型的泛化能力就会得到提升,从而模型的过拟合问题会得到缓解。

 

 为什么叫权重衰退?

因为我们每次更新梯度之前,会在1上减去一个ηλ,来缩小当前权重,也就是认为有一次衰退。

 

Q&A:

1、权重衰退作用很有限,不要过于依赖。

2、Weight_decay常取1e-2,1e-3,1e-4

3、噪音越大,W越大。所以限制W的大小就会限制噪音的污染。

 

 1 import torch
 2 from torch import nn
 3 from d2l import torch as d2l
 4 
 5 n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
 6 true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
 7 train_data = d2l.synthetic_data(true_w, true_b, n_train)
 8 train_iter = d2l.load_array(train_data, batch_size)
 9 test_data = d2l.synthetic_data(true_w, true_b, n_test)
10 test_iter = d2l.load_array(test_data, batch_size, is_train=False)
11 
12 def init_params():
13     w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)
14     b = torch.zeros(1, requires_grad=True)
15     return [w, b]
16 
17 def l2_penalty(w):
18     return torch.sum(w.pow(2))/2    #/2是为了求导之后W系数是1
19 
20 #尝试一下这里用L1范数
21 def l1_penalty(w):
22     return torch.sum(torch.abs(w))
23 
24 def train(lambd):
25     #初始化W和b
26     w, b = init_params()
27     # 定义了一个线性回归的net,loss选的是平方损失,lambda X是匿名函数
28     net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
29     num_epochs, lr = 100, 0.003
30     # 动画化,但是在pycharm里应该没什么价值
31     animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
32                             xlim=[5, num_epochs], legend=['train', 'test'])
33     for epoch in range(num_epochs):
34         for X, y in train_iter:
35             # 增加了L2范数惩罚项,
36             # 广播机制使l2_penalty(w)成为一个长度为batch_size的向量
37             #l = loss(net(X), y) + lambd * l2_penalty(w)
38             #尝试一下用L1
39             l = loss(net(X), y) + lambd * l2_penalty(w)
40             l.sum().backward()
41             d2l.sgd([w, b], lr, batch_size)
42         if (epoch + 1) % 5 == 0:
43             animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
44                                      d2l.evaluate_loss(net, test_iter, loss)))
45     print('w的L2范数是:', torch.norm(w).item())
46     d2l.plt.show()
47 
48 
49 train(lambd=6)
50 
51 
52 #简洁实现:
53 
54 
55 
56 def train_concise(wd):
57     net = nn.Sequential(nn.Linear(num_inputs, 1))
58     for param in net.parameters():
59         param.data.normal_()
60     loss = nn.MSELoss(reduction='none')
61     num_epochs, lr = 100, 0.003
62     # 偏置参数没有衰减
63     trainer = torch.optim.SGD([
64         {"params":net[0].weight,'weight_decay': wd},
65         {"params":net[0].bias}], lr=lr)
66     animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
67                             xlim=[5, num_epochs], legend=['train', 'test'])
68     for epoch in range(num_epochs):
69         for X, y in train_iter:
70             trainer.zero_grad()
71             l = loss(net(X), y)
72             l.mean().backward()
73             trainer.step()
74         if (epoch + 1) % 5 == 0:
75             animator.add(epoch + 1,
76                          (d2l.evaluate_loss(net, train_iter, loss),
77                           d2l.evaluate_loss(net, test_iter, loss)))
78     print('w的L2范数:', net[0].weight.norm().item())
79     d2l.plt.show()
80 
81 #train_concise(10)

结论:

首先我们将weight decay取为0,3,6,分别如下:

 

 

 

 

 

 W最优解为0.01,但是可以看到取0的时候,完全拟合了训练数据,而测试数据基本没有动静,这是最极端的过拟合;取3的时候对测试数据有了一些反应;取6的时候可以看到,测试数据收敛到了一个不错的值,所以这个过拟合能很不错地用权重衰退法解决。

好奇L1什么样子,于是将代码中加入了一个L1范式,取λ为6,结果如下:

 

 

好像差不多hhhh。

 

posted @ 2022-01-18 22:21  爱和九九  阅读(1104)  评论(0)    收藏  举报