针对于过拟合现象,有两种可以解决的办法:权重衰减、丢弃法。这篇文章先总结权重衰减。
一、权重衰减介绍及参数迭代方式推导
权重衰减是等价于范数正则化的。那么什么是正则化呢?
正则化是通过为模型损失函数添加惩罚项使得学习出的模型参数值较小的办法,是应对过拟合的常用手段。下面先描述范数正则化,再解释为何它叫权重衰减。
范数惩罚项是指:模型的权重参数每个元素的平方和 × 正常数。以线性回归
中的损失函数
为例。将权重参数用向量表示,带有范数惩罚项的新损失函数为
其中超参数,为样本个数
- 当权重参数为0时,惩罚项最小
- 当较大时,惩罚项的比重较大,这通常会使学到的权重参数元素接近于0
- 当时,惩罚项完全不起作用
加入该惩罚项之后,在小批量随机梯度下降中,将参数的迭代方式推导如下:
假设新的损失函数为,则
对于小批量来说,所以
于是,就有了下面新的迭代方式
可见,正则化令权重先乘上小于1的数,再减去不含惩罚项的梯度。因此范数也成为权重衰减。权重衰减通过惩罚绝对值较大的模型参数,为需要学习的模型增加了限制,这可能对过拟合有效。
二、解决过拟合现象实验
该部分,通过高维线性回归来引入一个过拟合问题,并使用权重衰减来试着应付过拟合。该高维线性回归函数如下:
其中
- 表示样本特征的维度,表示服从均值为0、标准差为0.01的正态分布的噪音项
- 假设
- 为了观察过拟合现象,将训练集样本数设为20,而测试集样本数为100
%matplotlib inline
import d2lzh as d2l
from mxnet import autograd, gluon, init, nd
from mxnet.gluon import data as gdata, loss as gloss, nn
## 生成训练集
n_train, n_test, num_inputs = 20, 100, 200
true_w, true_b = nd.ones((num_inputs, 1)) * 0.01, 0.05
features = nd.random.normal(shape=(n_train + n_test, num_inputs))
labels = nd.dot(features, true_w) + true_b
labels += nd.random.normal(scale=0.01, shape=labels.shape)
train_features, test_features = features[:n_train, :], features[n_train:, :]
train_labels, test_labels = labels[:n_train], labels[n_train:]
## 定义:初始化参数、L2范数惩罚项函数
def init_params():
w = nd.random.normal(scale=1, shape=(num_inputs, 1))
b = nd.zeros(shape=(1,))
w.attach_grad()
b.attach_grad() # 要附上梯度
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
train_iter = gdata.DataLoader(gdata.ArrayDataset(train_features, train_labels),
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:
with autograd.record():
# 添加了L2范数惩罚项
l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
l.backward()
d2l.sgd([w, b], lr, batch_size) # 参数:要优化的参数,学习率,batch_size
train_ls.append(loss(net(train_features, w, b),
train_labels).mean().asscalar())
test_ls.append(loss(net(test_features, w, b),
test_labels).mean().asscalar())
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().asscalar())
其中,d2l.semilogy()
的函数说明如下:
其中参数分别表示:第一个曲线的x、y
,x轴的名字
、y轴的名字
、第二个曲线的x、y
、legend:每个曲线对应的说明
、作图大小
控制fit_and_plot()
函数的参数lambd
,可以实现权重衰减的程度。
当lamda=0
时,没有实现权重衰减。由于模型的维度很高,比较复杂,所以很容易出现过拟合现象,并且此时权重参数的范数也比较大。
fit_and_plot(lambd=0)
结果如下:
当lambd=3
时,使用了权重衰减。
fit_and_plot(lambd=3)
结果如下:
可以看出,虽然训练误差有所提高,但测试集误差有所下降。所以过拟合现象得到一定程度的缓和。另外,此时权重参数范数也更小,并且比较接近0。
在利用mxnet框架的时候,通过指定gluon.Trainner
中的wd
(weight decay)参数,可以实现权重衰减。其中'wd': wd
中的wd表示权重衰减系数。如下: