
权重衰减
用于对付过拟合问题
权重衰减等价于 L2 范数正则化(regularization)
我们知道线性回归损失函数例如:
将权重参数用向量 w=[w1,w2] 表示,带有 L2 范数惩罚项的新损失函数为:
其中超参数 λ>0 。当权重参数均为0时,惩罚项最小。当 λ 较大时,惩罚项在损失函数中的比重较大,这通常会使学到的权重参数的元素较接近0。当 λ 设为0时,惩罚项完全不起作用。
上式中 L2 范数平方 展开后得到
。有了 L2 范数惩罚项后,在小批量随机梯度下降中,权重 w1 和 w2 的迭代方式更改为:
高维线性回归实验
我们以高维线性回归为例来引入一个过拟合问题,并使用权重衰减来应对过拟合。设数据样本特征的维度为p。对于训练数据集和测试数据集中特征为x1,x2,…,xp的任一样本,我们使用如下的线性函数来生成该样本的标签:
其中噪声项 ϵ 服从均值为0、标准差为0.01的正态分布。为了较容易地观察过拟合,我们考虑高维线性回归问题,如设维度 p=200 ;同时,我们特意把训练数据集的样本数设低,如20。
数据导入
#首先导入必要的包
%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)
#前面0到20行是训练特征,后面的是测试特征
train_features, test_features = features[:n_train, :], features[n_train:, :]
#前面20个是训练标签,后面的是测试标签
train_labels, test_labels = labels[:n_train], labels[n_train:]
从零开始实现
初始化参数模型
#初始化参数并且附上梯度
def init_params():
w = nd.random.normal(scale=0.01,shape=(num_inputs,1))
b = nd.zeros(shape=(1,))
w.attach_grad()
b.attach_grad()
return [w,b]
定义L2范数惩罚项
#运用此惩罚项求取关于w的梯度,从而在优化函数中进行优化
def l2_penalty(w):
return (w**2).sum()/2
定义训练和测试
#设置小批量大小,周期以及学习率
batch_size ,num_epochs,lr = 1,100,0.003
#定义模型和损失函数
net = d2l.linreg
loss = d2l.squared_loss
#训练模型
train_iter = gdata.DataLoader(gdata.ArrayDataset(train_features,train_labels),batch_size,shuffle=True)
def fit_and_plot(lamd):
w,b = init_params()
train_ls,test_ls = [],[]
for epoch in range(num_epochs):
for X,y in train_iter:
with autograd.record():
l = loss(net(X,w,b),y) + lamd *l2_penalty(w)
l.backward()
d2l.sgd([w,b],lr,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,'epchs','loss',range(1,num_epochs+1),test_ls,['train','test']) #画图
print("L2 norm of w:",w.norm().asscalar())
观察过拟合
fit_and_plot(lamd=0)

L2 norm of w: 0.14012791
可以看到,过拟合中几个周期后,训练误差急速下降,导致训练误差比测试误差小很多
使用权重衰减
fit_and_plot(lamd=3)

L2 norm of w: 0.034974933
可以看到,使用权重衰减之后,训练误差
简洁实现
from mxnet.gluon import nn
def fit_and_plot_gluon(wd):
net = nn.Sequential()
net.add(nn.Dense(1))
net.initialize(init.Normal(sigma=1))
trainer_w = gluon.Trainer(net.collect_params('.*weight'),'sgd',{'learning_rate':lr,'wd':wd})
trainer_b = gluon.Trainer(net.collect_params('.*bias'),'sgd',{'learning_rate':lr})
train_ls,test_ls = [],[]
for epoch in range(num_epochs):
for X,y in train_iter:
with autograd.record():
l = loss(net(X),y)
l.backward()
trainer_w.step(batch_size)
trainer_b.step(batch_size)
train_ls.append(loss(net(train_features),train_labels).mean().asscalar())
test_ls.append(loss(net(test_features),test_labels).mean().asscalar())
d2l.semilogy(range(1,num_epochs+1),train_ls,'epoch','loss',range(1,num_epochs+1),test_ls,['train','test'])
print('L2 norm of w:',net[0].weight.data().norm().asscalar())
观察过拟合
fit_and_plot_gluon(0)

L2 norm of w: 13.975993
使用权重衰减
fit_and_plot_gluon(3)

L2 norm of w: 0.034945916
网友评论