点击【拇指笔记】,关注我的公众号。
1.通过权重衰减解决过拟合问题
1.1 权重衰减
为了减轻上一篇文章提到的过拟合现象,往往需要增大训练集,但增大训练集的代价往往是高昂的。
因此这里介绍一种常用的缓解过拟合问题的方法:权重衰减。
1.2 实现方法
权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制。权重衰减等价于范数正则化。正则化通过为模型损失函数添加惩罚项使学习得到的模型参数值较小。
范数正则化在模型原损失函数基础上添加范数惩罚项,范数惩罚项指的是模型权重参数()每个元素的平方和与一个正的常数的乘积。
以如下这个损失函数为例
对应的迭代方程为
它的带有范数惩罚项的新损失函数为
其中为超参数()。当较大时,惩罚项比重较大,这会使学到的权重参数较接近0。当为0时,惩罚项完全不起作用。
当优化算法为小批量随机梯度下降(SGD)时,的迭代方程b变为
由此可见,因为添加了范数正则化,迭代方程中的权重参数自乘了一个小于1的数()。因此范数正则化又叫做权重衰减。实际场景中,有时也需要在惩罚项中添加偏差元素的平方和。
1.3 引入过拟合问题
以高维线性回归为例,引入过拟合问题。
以下面这个维度为的线性函数为例,生成人工数据集。
噪声项服从均值为0,标准差为0.01的正态分布。假设,训练集样本数为20。
1.3.1 生成人工数据集
代码语言:javascript复制根据模型,随机生成特征值,计算得到标签。
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 = features[:n_train,:]
test_features = features[n_train:,:]
train_labels = labels[:n_train,:]
test_labels = labels[n_train:,:]
#分割测试集和训练集
1.3.2 定义和初始化模型
先将权重参数和偏差参数初始化,
代码语言:javascript复制def init():
w = torch.randn((num_inputs,1),requires_grad = True)
b = torch.zeros(1,requires_grad = True)
return [w,b]
代码语言:javascript复制def linear(x,w,b): return torch.mm(x,w) b
1.3.3 定义损失函数和优化函数
使用之前在线性回归中介绍的平方误差函数和小批量随机梯度下降算法。
代码语言:javascript复制#平方误差损失函数
def square_loss(y_hat,y):
return (y_hat-y.view(y_hat.size()))**2/2
#小批量随机梯度下降
def sgd(params,lr,batch_size):
for param in params:
param.data -= lr*param.grad/batch_size
1.3.4 定义范数惩罚项
这里只惩罚权重参数。
代码语言:javascript复制def l2(w): return (w**2).sum()/2
1.3.5 训练模型
代码语言:javascript复制batch_size,num_epochs,lr = 1,100,0.003
net,loss = linear,square_loss
dataset = torch.utils.data.TensorDataset(train_features,train_labels)
train_iter = torch.utils.data.DataLoader(dataset,batch_size,shuffle = True)
def train(lambda1):
#通过设置lambda=0,可以实现过拟合效果。设置lambda=3,实现权重衰减,减轻过拟合。
w,b = init()
train_ls,test_ls = [],[]
for epoch in range(num_epochs 1):
for x,y in train_iter:
l = loss(net(x,w,b),y) lambda1*l2(w)
l = l.sum()
if w.grad is not None:
#如果权重参数的梯度信息不是None,代表已经开始计算,需要进行梯度清零
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
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())
print('权重参数的L2范数',w.norm().item())
1.3.6 过拟合效果
代码语言:javascript复制#令lambda=0,L2范数为0。即不开启权重衰减
train(0)
过拟合情况下,对数化的训练误差和泛化误差随学习周期的变化如图
可以看出,出现了严重的过拟合。
1.3.7 使用权重衰减矫正过拟合
代码语言:javascript复制
#令lambda=3,开启权重衰减
train(3)
使用权重衰减后,对数化的训练误差和泛化误差随学习周期的变化如图
代码语言:javascript复制不难看出,使用权重衰减法后, 过拟合现象得到一定程度的缓解。