L2范数正则化
ZOU

方法

L2 范数是指向量对应元素平方和再开方,一般用来表示。

L2 范数正则化等价于权重衰减。正则化通过为模型损失添加惩罚项使学出的模型参数值较小,是应对过拟合的常用手段。

L2 范数正则化在模型原损失函数的基础上添加 L2 范数惩罚项,从而得到训练所需要的最小化的函数。惩罚项是指模型权重参数每个元素的平方和与一个正的常数的乘积。以一个普通的线性回归损失函数为例子:

其中 w 为权重,用向量表示。则带有 L2 惩罚项的新损失函数为:

其中超参数,数值越大时,比重越大,这会使学到的权重参数的元素接近 0。表示惩罚很大。

以权重 w1 为例,采用小批量随机梯度下降更新权重,权重更新公式由之前的:

更改为:

其中是小批量的样本数,是学习率。

可以看到 L2 范数正则化令权重 w1 先自乘小于 1 的数,再减去不含惩罚项的梯度。因此,其又被称为权重衰减。权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制

实现

首先生成数据,使用如下函数生成样本的标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
%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:]

从零实现,使用了 d2l库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 从零开始实现
def init_params():
w = torch.randn((num_inputs, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True) # 反向传播自动求导
return [w, b]

# 只定义惩罚模型的权重参数
def l2_penalty(w):
return (w**2).sum() / 2

# 训练集合测试集实验模型
batch_size, num_epochs, lr = 1, 100, 0.003
net, loss = d2l.linreg, d2l.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:
# 添加L2范数惩罚项
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()
d2l.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())
d2l.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())

展示一下使用和不使用权重衰减的效果:

1
2
# 可以观察到很明显的过拟合现象
fit_and_plot(lambd=0)

1
2
# 尝试使用权重衰减
fit_and_plot(lambd=3)

也可以借助 torch 库来快速实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 简洁实现
def fit_and_plot_pytorch(wd):
#对权重衰减,权重名称一般是以weight结尾
net = nn.Linear(num_inputs, 1)
nn.init.normal_(net.weight, mean=0, std=1)
nn.init.normal_(net.bias, mean=0, std=1)
optim_w = torch.optim.SGD(params=[net.weight], lr=lr,
weight_decay=wd) # 对权重参数衰减
optim_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()

optim_w.zero_grad()
optim_b.zero_grad()

l.backward()

# 分别更新权重和偏差
optim_w.step()
optim_b.step()

train_ls.append(loss(net(train_features),
train_labels).mean().item())
test_ls.append(loss(net(test_features),
test_labels).mean().item())
d2l.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())

换一个参数测试结果:

1
fit_and_plot_pytorch(5)

  • 本文标题:L2范数正则化
  • 本文作者:ZOU
  • 创建时间:2022-03-18 17:17:13
  • 本文链接:https://yipeng.xyz/2022/03/18/L2范数正则化/
  • 版权声明:可随意使用,但是转载请联系我!
 评论