深度学习-权重衰退
使用均方范数作为硬性限制
通过参数限制值的选择性范围来控制模型容量
min ℓ(w,b)\ell(w,b)ℓ(w,b) subject to ∣∣w∣∣2≤θ||w||^2 \le \theta∣∣w∣∣2≤θ
- 通常不限制偏移b(限制和限制都差不多)
- 小的θ\thetaθ意味着更强的正则项
使用均方范数作为柔性限制
- 对每个θ\thetaθ都可以找到λ\lambdaλ使得之前的目标函数等价于下面
minℓ(w,b)+λ2∣∣w∣∣2\ell(w, b) + \frac{\lambda}{2}||w||^2ℓ(w,b)+2λ∣∣w∣∣2- 可以通过拉格朗日乘子来证明
- 超参数λ\lambdaλ控制了正则项的重要程度
- λ=0\lambda = 0λ=0: 无作用
- λ→∞,w∗→0\lambda \rightarrow \infty, w^* \rightarrow 0λ→∞,w∗→0
其中minℓ(w,b)\ell(w, b)ℓ(w,b)代表了之前的损失函数, λ2∣∣w∣∣2\frac{\lambda}{2}||w||^22λ∣∣w∣∣2控制值不会特别大, 其中λ\lambdaλ是超参数, 如果不希望模型太复杂可以通过增加λ\lambdaλ来解决
演示最优解的影响
w∗=argminℓ(w,b)+λ2∣∣w∣∣2w˘∗=argminℓ(e,b)
w^* = argmin \ell(w, b) + \frac{\lambda}{2}||w||^2 \\
\breve{w}^* = argmin\ell(e, b)
w∗=argminℓ(w,b)+2λ∣∣w∣∣2w˘∗=argminℓ(e,b)

- w˘∗\breve{w}^*w˘∗是之前的最优解
- ℓ\ellℓ(绿色的线)
- 黄色的线是损失线
- 其中优化过程w˘∗\breve{w}^*w˘∗会不断地往w∗w^*w∗靠近, 在w∗w^*w∗位置形成平衡点
- 优化过程使得最优解往原点走, 可以使得模型复杂度变低
其中, 我们希望优化ℓ\ellℓ(绿色的线)
参数更新法则
计算梯度
∂∂w(ℓ(w,b)+λ2∣∣w∣∣2)=∂ℓ(w,b)∂w+λw
\frac{\partial}{\partial{w}}(\ell(w,b) +\frac{\lambda}{2}||w||^2)=\frac{\partial{\ell(w,b)}}{\partial{w}}+\lambda{w}
∂w∂(ℓ(w,b)+2λ∣∣w∣∣2)=∂w∂ℓ(w,b)+λw
时间t更新参数
wt+1=(1−ηλ)wt−η∂ℓ(wt,bt)∂wt
w_{t+1} = (1 - \eta\lambda)w_t - \eta\frac{\partial\ell(w_t,b_t)}{\partial{w_t}}
wt+1=(1−ηλ)wt−η∂wt∂ℓ(wt,bt)
通常ηλ<1\eta\lambda \lt1ηλ<1, 在深度学习中通常叫做权重衰退
代码实现
import torch
from torch import nn
from d2l import torch as d2l
# y = 0.05 + sum^d_{i=1}0.01x_i+ where(0, 0.01)
# 20个训练样本, 方便实验过拟合, 0.05是偏移量, x_i是输入, 均值为0, 方差为1
n_train, n_test, num_inputs, batch_Size = 20, 100, 200, 5
true_new, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
train_data = d2l.synthetic_data(true_w, true_b, n_train)
train_iter = d2l.load_array(train_data, batch_size)
test_data = d2l.synthetic_data(true_w, true_b, n_test)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)
# 模型参数初始化
def init_params():
w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
return w, b
# 定义L2范数惩罚
def l2_penalty(w):
return torch.sum(w.pow(2)) / 2
def train(lambd):
w, b = init_params()
net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
num_epochs, lr = 100, 0.003
animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='loss', xlim=[5, num_epochs], legend=['train', 'test'])
for epoch in range(num_epochs):
for X, y in train_iter:
with troch.enable_grad():
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, d2l.evaluate_loss(net, test_iter))))
print("w的L2范数是", torch.norm(w).item())
train(lambd=0) # w的l2范数12.56
train(lambd=3) # w的l2范数0.35
简洁实现
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
trainer = torch.optim.SGD([
{
"params": net[0].weight,
"weight_decay": wd # 在算法内部实现lambd的计算
}, {
"param": net[0].bias
}
}], lr=lr)
animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='loss', xlim=[5, num_epochs], legend=['train', 'test'])
for epoch in range(num_epochs):
for X, y in train_iter:
with torch.enable_grad():
trainer.zero_grad()
l = loss(net(X), Y)
L.backward()
trainer.step()
更多推荐



所有评论(0)