权重衰退
常用的处理过拟合的方法
使用均方范数作为硬性限制
通过限制参数值的选择范围来控制模型容量
通常不限制偏移b
小的θ意味着更强的正则项
使用均方范数作为柔性限制
总结
权重衰退通过L2正则项使得模型参数不会太大,从而控制模型复杂度
正则项权重是控制模型复杂度的超参数
代码实现
import torch
from torch import nn
from d2l import torch as d2l
生成人工数据集
n_train, n_test, num_inputs, batch_size=20,100,200,5 # 训练数据越小,越容易过拟合(此处是故意过拟合)
true_w, true_b=torch.ones((num_inputs, 1))*0.01,0.05
train_data=d2l.synthetic_data(true_w,true_b,n_train) # 具体来说,d2l.synthetic_data函数通常接受几个参数,如权重(weights)、偏置(bias)和样本数量,然后基于这些参数生成特征和标签的数据集。这些参数可以帮助你控制生成数据的分布和复杂性。
train_iter=d2l.load_array(train_data,batch_size) # 用于从文件中加载数据并将其转换为NumPy数组。它的功能是读取文件中的数据,并将其转换为指定的数据类型和形状。
test_data=d2l.synthetic_data(true_w,true_b,n_test)
test_iter=d2l.load_array(test_data,batch_size,is_train=False)
# 此处插入d2l.synthetic_data()函数的使用功能
import torch
from d2l import torch as d2l
# 定义权重和偏置
true_w1 = torch.tensor([2, -3.4])
true_b1 = 4.2
# 生成合成数据集
features1, labels1 = d2l.synthetic_data(true_w1, true_b1, 10)
features1,labels1
# 在这个例子中,true_w1和true_b1定义了生成数据的线性方程的参数
# 而10指定了要生成的样本数量。
# 函数返回两个张量:features和labels,它们分别包含生成的特征和对应的标签。
(tensor([[-0.7090, -2.2213],
[-0.2302, -0.2752],
[-0.2158, -0.1926],
[-0.3529, -0.1453],
[-0.9229, 0.2693],
[ 0.8251, 0.5114],
[ 0.4833, 0.9003],
[-1.1898, 2.1717],
[ 0.3343, 1.3417],
[ 0.1269, -1.2196]]),
tensor([[10.3366],
[ 4.6631],
[ 4.4256],
[ 4.0140],
[ 1.4457],
[ 4.1345],
[ 2.1194],
[-5.5593],
[ 0.3102],
[ 8.5957]]))
初始化模型参数
def init_params(): # w和b的初始化函数
w=torch.normal(0,1,size=(num_inputs,1),requires_grad=True) # w均值为0,方差为1
b=torch.zeros(1,requires_grad=True) # b则是全0标量
return [w,b]
定义L2范数惩罚
def l2_penalty(w):
return torch.sum(w.pow(2))/2
定义训练代码
def train(lambd): # lambd: 超参数
w,b=init_params() # 初始化权重wb
net, loss=lambda X:d2l.linreg(X,w,b), d2l.squared_loss # 线性回归,平方损失函数
num_epochs,lr=100,0.003 # 迭代100次,学习率0.003
animator=d2l.Animator(xlabel='epochs',ylabel='loss',yscale='log',
xlim=[5,num_epochs], legend=['train','test']) # 动画效果
for epoch in range(num_epochs): # 每次数据迭代
for X,y in train_iter: # 每个迭代器拿出X和y
# with torch.enable_grad():
l=loss(net(X),y)+lambd* l2_penalty(w) # 超参数是传进来的lambd
# 原始的神经网络损失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,loss),
d2l.evaluate_loss(net,test_iter,loss)))
print('w的L2范数是:',torch.norm(w).item())
忽略正则化直接训练
train(lambd=0)
w的L2范数是: 13.919422149658203
使用权重衰退
train(lambd=3) # 数值可自选调节
w的L2范数是: 0.35291415452957153
权重衰退简洁实现
def train_concise(wd):
net = nn.Sequential(nn.Linear(num_inputs, 1))
for param in net.parameters():
param.data.normal_()
loss = nn.MSELoss(reduction='none')
num_epochs, lr = 100, 0.003
# 偏置参数没有衰减
trainer = torch.optim.SGD([
{"params":net[0].weight,'weight_decay': wd},
{"params":net[0].bias}], lr=lr)
animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
xlim=[5, num_epochs], legend=['train', 'test'])
for epoch in range(num_epochs):
for X, y in train_iter:
trainer.zero_grad()
l = loss(net(X), y)
l.mean().backward()
trainer.step()
if (epoch + 1) % 5 == 0:
animator.add(epoch + 1,
(d2l.evaluate_loss(net, train_iter, loss),
d2l.evaluate_loss(net, test_iter, loss)))
print('w的L2范数:', net[0].weight.norm().item())
train_concise(0)
w的L2范数: 13.60023021697998
train_concise(3)
w的L2范数: 0.35634884238243103