4-5权重衰减
4-5权重衰减
1.高维线性回归
%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l
代码解释
n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5- 定义了一些超参数:
n_train:训练数据集的样本数量,这里为 20。n_test:测试数据集的样本数量,这里为 100。num_inputs:输入特征的数量,这里为 200。batch_size:每个小批量的样本数量,这里为 5。
- 定义了一些超参数:
true_w, true_b = torch.ones((num_inputs, 1)) \* 0.01, 0.05- 定义了线性模型的真实权重和偏置:
true_w:真实权重是一个形状为(num_inputs, 1)的张量,所有元素初始化为 0.01。true_b:真实偏置是一个标量,值为 0.05。
- 定义了线性模型的真实权重和偏置:
train_data = d2l.synthetic_data(true_w, true_b, n_train)- 使用
d2l.synthetic_data函数生成训练数据集。true_w和true_b是线性模型的真实参数。n_train是训练数据集的样本数量。- 该函数会生成一个包含输入特征和对应目标值的数据集。
- 使用
train_iter = d2l.load_array(train_data, batch_size)- 使用
d2l.load_array函数将训练数据集转换为一个数据迭代器。train_data是训练数据集。batch_size是每个小批量的样本数量。- 数据迭代器会在每次迭代时返回一个包含
batch_size个样本的小数据批量。
- 使用
test_data = d2l.synthetic_data(true_w, true_b, n_test)- 使用
d2l.synthetic_data函数生成测试数据集。true_w和true_b是线性模型的真实参数。n_test是测试数据集的样本数量。- 该函数会生成一个包含输入特征和对应目标值的数据集。
- 使用
test_iter = d2l.load_array(test_data, batch_size, is_train=False)
- 使用
d2l.load_array函数将测试数据集转换为一个数据迭代器。test_data是测试数据集。batch_size是每个小批量的样本数量。is_train=False表示这是一个测试数据迭代器,通常用于评估模型性能。
# 定义超参数
# 训练样本数测试、样本数、输入特征数、小批量样本数
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)
# 创建训练数据迭代器
train_iter = d2l.load_array(train_data, batch_size)
# 生成测试数据集
test_data = d2l.synthetic_data(true_w, true_b, n_test)
# 创建测试数据迭代器
test_iter = d2l.load_array(test_data, batch_size, is_train=False)
2.从0开始实现
2.1 初始化模型参数
代码解释
def init_params():- 定义了一个函数
init_params(),该函数用于初始化模型的参数。
- 定义了一个函数
w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)- 使用
torch.normal函数初始化权重参数w。0和1分别是均值和标准差,表示权重参数从均值为 0、标准差为 1 的正态分布中随机采样。size=(num_inputs, 1)指定了权重参数的形状,即输入特征数为num_inputs,输出为 1。requires_grad=True表示这个张量需要计算梯度,用于后续的反向传播。
- 使用
b = torch.zeros(1, requires_grad = True)- 使用
torch.zeros函数初始化偏置参数b。1指定了偏置参数的形状,即一个标量。requires_grad=True表示这个张量需要计算梯度,用于后续的反向传播。
- 使用
return [w, b]- 返回初始化后的权重参数
w和偏置参数b,以列表的形式返回。
- 返回初始化后的权重参数
def init_params():
# 初始化权重参数,从均值为0、标准差为1的正态分布中随机采样,形状为(num_inputs, 1),需要计算梯度
w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)
# 初始化偏置参数,为0,形状为1,需要计算梯度
b = torch.zeros(1, requires_grad = True)
# 返回初始化后的权重和偏置参数
return [w, b]
2.2 定义 L2 范数惩罚
def l2_penalty(w):
return torch.sum(w.pow(2)) / 2
2.3 定义训练代码实现
代码解释
def train(lambd):- 定义了一个函数
train(lambd),其中lambd是 L2 正则化的参数,用于控制正则项的强度。
- 定义了一个函数
w, b = init_params()- 调用
init_params()函数初始化模型的权重w和偏置b。
- 调用
net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss- 定义了模型
net和损失函数loss。net是一个 lambda 函数,表示线性回归模型,输入X,输出d2l.linreg(X, w, b)。loss是平方损失函数,用于计算预测值和真实值之间的差异。
- 定义了模型
num_epochs, lr = 100, 0.003- 定义了训练的超参数:
num_epochs:训练的总轮数,这里为 100。lr:学习率,这里为 0.003。
- 定义了训练的超参数:
animator = d2l.Animator(xlabel = 'epochs', ylabel = 'loss', yscale = 'log', xlim = [5, num_epochs], legend = ['train', 'test'])- 创建了一个
d2l.Animator对象,用于动态绘制训练过程中的损失曲线。xlabel和ylabel分别设置了 x 轴和 y 轴的标签。yscale='log'表示 y 轴使用对数刻度。xlim=[5, num_epochs]设置了 x 轴的范围。legend=['train', 'test']设置了图例,分别表示训练集和测试集的损失。
- 创建了一个
for epoch in range(num_epochs):- 外层循环,表示训练的总轮数。
for x, y in train_iter:- 内层循环,遍历训练数据迭代器
train_iter,每次获取一个小批量的数据x和对应的标签y。
- 内层循环,遍历训练数据迭代器
l = loss(net(x), y) + lambd \* l2_penalty(w)- 计算损失值
l,包括平方损失和 L2 正则化项。loss(net(x), y)是平方损失。lambd * l2_penalty(w)是 L2 正则化项,l2_penalty(w)是权重参数w的 L2 范数。
- 计算损失值
l.sum().backward()- 对损失值
l求和后进行反向传播,计算梯度。
- 对损失值
d2l.sgd([w, b], lr, batch_size)- 使用随机梯度下降(SGD)更新权重
w和偏置b。
- 使用随机梯度下降(SGD)更新权重
if (epoch + 1) % 5 == 0:- 每隔 5 轮训练,记录一次训练集和测试集的损失,并绘制损失曲线。
animator.add(epoch+1, (d2l.evaluate_loss(net, train_iter, loss), d2l.evaluate_loss(net, test_iter, loss)))- 使用
d2l.evaluate_loss函数计算训练集和测试集的损失,并将结果添加到animator中,用于绘制损失曲线。
- 使用
print('w的L2范数是:',torch.norm(w).item())- 打印训练结束后权重参数
w的 L2 范数。
- 打印训练结束后权重参数
def train(lambd):
# 初始化模型参数
w, b = init_params()
# 定义模型和损失函数
net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
# 定义训练的超参数
num_epochs, lr = 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:
# 增加了L2范数惩罚项
# 广播机制使 l2_penalty(w)成为一个长度为batch_size的向量
# 计算损失值,包括平方损失和L2正则化项
l = loss(net(x), y) + lambd * l2_penalty(w)
# 反向传播计算梯度
l.sum().backward()
# 使用随机梯度下降更新参数
d2l.sgd([w, b], lr, batch_size)
# 每隔5轮训练,记录一次训练集和测试集的损失
if (epoch + 1) % 5 == 0:
animator.add(epoch+1, (d2l.evaluate_loss(net, train_iter, loss),
d2l.evaluate_loss(net, test_iter, loss)))
# 打印训练结束后权重参数的L2范数
print('w的L2范数是:',torch.norm(w).item())
2.4 忽视正则化直接训练
train(lambd = 0)
w的L2范数是: 13.750493049621582
2.5 使用权重衰减
train(lambd = 3)
w的L2范数是: 0.37755826115608215
3.简洁实现
代码解释
def train_concise(wd):- 定义了一个函数
train_concise(wd),其中wd是 L2 正则化的参数,用于控制正则项的强度。
- 定义了一个函数
net = nn.Sequential(nn.Linear(num_inputs, 1))- 使用
nn.Sequential定义了一个简单的线性回归模型。nn.Linear(num_inputs, 1)表示一个线性层,输入特征数为num_inputs,输出为 1。
- 使用
for param in net.parameters():- 遍历模型的所有参数。
param.data.normal_()- 使用
normal_()方法将每个参数初始化为均值为 0、标准差为 1 的正态分布。
- 使用
loss = nn.MSELoss(reduction = 'none')- 定义了均方误差损失函数
MSELoss,reduction='none'表示不对损失值进行任何聚合,返回每个样本的损失值。
- 定义了均方误差损失函数
num_epochs, lr = 100, 0.003- 定义了训练的超参数:
num_epochs:训练的总轮数,这里为 100。lr:学习率,这里为 0.003。
- 定义了训练的超参数:
trainer = torch.optim.SGD([{"params":net[0].weight, 'weight_decay':wd}, {"params":net[0].bias}], lr=lr)- 定义了优化器
SGD,用于更新模型的参数。{"params":net[0].weight, 'weight_decay':wd}:对权重参数net[0].weight应用 L2 正则化,正则化强度为wd。{"params":net[0].bias}:偏置参数net[0].bias不应用 L2 正则化。lr=lr:设置学习率为lr。
- 定义了优化器
animator = d2l.Animator(xlabel = 'epochs', ylabel = 'loss', yscale = 'log', xlim = [5, num_epochs], legend = ['train', 'test'])- 创建了一个
d2l.Animator对象,用于动态绘制训练过程中的损失曲线。xlabel和ylabel分别设置了 x 轴和 y 轴的标签。yscale='log'表示 y 轴使用对数刻度。xlim=[5, num_epochs]设置了 x 轴的范围。legend=['train', 'test']设置了图例,分别表示训练集和测试集的损失。
- 创建了一个
for epoch in range(num_epochs):- 外层循环,表示训练的总轮数。
for x, y in train_iter:- 内层循环,遍历训练数据迭代器
train_iter,每次获取一个小批量的数据x和对应的标签y。
- 内层循环,遍历训练数据迭代器
trainer.zero_grad()- 清除之前的梯度,避免梯度累积。
l = loss(net(x), y)- 计算损失值
l,这里使用了定义好的损失函数loss。
- 计算损失值
l.mean().backward()- 对损失值
l求均值后进行反向传播,计算梯度。
- 对损失值
trainer.step()- 使用优化器
trainer更新模型的参数。
- 使用优化器
if (epoch + 1) % 5 == 0:- 每隔 5 轮训练,记录一次训练集和测试集的损失,并绘制损失曲线。
animator.add(epoch+1, (d2l.evaluate_loss(net, train_iter, loss), d2l.evaluate_loss(net, test_iter, loss)))- 使用
d2l.evaluate_loss函数计算训练集和测试集的损失,并将结果添加到animator中,用于绘制损失曲线。
- 使用
print('w的L2范数:', net[0].weight.norm().item())- 打印训练结束后权重参数
net[0].weight的 L2 范数。
- 打印训练结束后权重参数
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
# 偏置参数没有衰减
# 定义优化器,对权重参数应用L2正则化,偏置参数不应用
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()
# 每隔5轮训练,记录一次训练集和测试集的损失
if (epoch + 1) % 5 == 0:
animator.add(epoch+1, (d2l.evaluate_loss(net, train_iter, loss),
d2l.evaluate_loss(net, test_iter, loss)))
# 打印训练结束后权重参数的L2范数
print('w的L2范数:', net[0].weight.norm().item())
train_concise(0)
w的L2范数: 13.302008628845215
train_concise(3)
w的L2范数: 0.43226614594459534

本章主要使用权重衰减的方法来训练模型
浙公网安备 33010602011771号