线性回归的pytorch实现
实践中,我们通常可以用比上一节深度学习实战(基于pytroch)系列(四)更简洁的代码来实现同样的模型。在本节中,我们将介绍如何使用pytorch提供的接口更方便地实现线性回归的训练。
导包及设置随机种子
python
import torch
import torch.nn as nn
import torch.utils.data as data
import numpy as np
# 设置随机种子以确保结果可重现
torch.manual_seed(42)
生成数据
生成与上一节深度学习实战(基于pytroch)系列(四)中相同的数据集。其中features是训练数据特征,labels是标签。
python
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn(num_examples, num_inputs)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.randn(labels.shape) * 0.01
print("特征形状:", features.shape)
print("标签形状:", labels.shape)
读取数据
pytorch提供了torch.utils.data来读取数据,其中TensorDataset 是Dataset 的具体实现类用来处理张量数据。比如tensordataset[i] 返回了一个元组 (features[i], labels[i]) 。Dataset可以支持自定义实现来处理非张量数据,比如图像音频等数据,自定义的时候需要实现 len 和 getitem 方法。
其中DataLoader负责从 Dataset 中按指定规则抽取数据,并组合成批次供模型训练。它实现了:批处理、打乱、并行加载这三个核心需求。
python
batch_size = 10
#创建数据集和数据加载器
dataset = data.TensorDataset(features, labels)
data_iter = data.DataLoader(dataset, batch_size, shuffle=True)
#读取并打印第一个小批量数据样本
for X, y in data_iter:
print("第一个批次的特征:", X)
print("第一个批次的标签:", y)
break
定义模型
在上一节实现中,我们需要定义模型参数,并使用它们一步步描述模型是怎样计算的。当模型结构变得更复杂时,这些步骤将变得更繁琐。其实,pytroch提供了大量预定义的层,这使我们只需关注使用哪些层来构造模型。
首先,导入nn模块。实际上,"nn"是neural networks(神经网络)的缩写。顾名思义,该模块定义了大量神经网络的层。我们先定义一个模型变量net,它是一个Sequential实例。Sequential实例可以看作是一个串联各个层的容器。在构造模型时,我们在该容器中依次添加层。当给定输入数据时,容器中的每一层将依次计算并将输出作为下一层的输入。
python
#使用Sequential容器定义模型
net = nn.Sequential(
nn.Linear(num_inputs, 1) # 全连接层,对应Gluon的Dense层
)
print("\n模型结构:")
print(net)
初始化模型参数
在使用net前,我们需要初始化模型参数,如线性回归模型中的权重和偏差。我们从nn模块导入init模块。该模块提供了模型参数初始化的各种方法。这里的init是initializer的缩写形式。我们通过`init.Normal_(m.weight, mean=0, std=0.01) 指定权重参数每个元素将在初始化时随机采样于均值为0、标准差为0.01的正态分布。偏差参数默认会初始化为零。
python
#手动初始化权重和偏置
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, mean=0, std=0.01) # 权重初始化
nn.init.constant_(m.bias, 0) # 偏置初始化为0
net.apply(init_weights)
print("\n初始化后的权重:", net[0].weight.data)
print("初始化后的偏置:", net[0].bias.data)
apply() 方法是PyTorch中一个非常重要的函数,它会递归地对网络中的每个子模块(包括所有层)应用指定的函数。它会遍历 net 中的每一个层,对每个层调用 init_weights 函数
normal_ 后缀的下划线表示原地操作(in-place)
定义损失函数
nn模块定义了各种损失函数,可以直接调用nn模块里面的损失函数
python
loss = nn.MSELoss() # 平方损失
定义优化算法
同样,我们也无须实现小批量随机梯度下降。直接调用torch.optim.SGD可以实现小批量随机梯度下降,指定学习率为0.03。
python
learning_rate = 0.03
trainer = torch.optim.SGD(net.parameters(), lr=learning_rate) # 随机梯度下降
训练模型
python
num_epochs = 3
for epoch in range(1, num_epochs + 1):
total_loss = 0
batch_count = 0
for X, y in data_iter:
# 前向传播
y_hat = net(X)
l = loss(y_hat, y.view(-1, 1)) # 调整y的形状以匹配输出
# 反向传播
trainer.zero_grad() # 梯度清零
l.backward() # 反向传播计算梯度
trainer.step() # 更新参数
# 每个epoch结束后计算在整个数据集上的损失
with torch.no_grad():
entire_loss = loss(net(features), labels.view(-1, 1))
print(f'epoch {epoch}, loss: {entire_loss.mean().item():.6f}')
item():将张量转换为Python数值
比较学到的参数和真实参数
python
#获取学到的参数
dense = net[0] # 第一层(也是唯一的一层)
learned_w = dense.weight.data
learned_b = dense.bias.data
print("\n参数比较:")
print("真实权重:", true_w)
print("学到的权重:", learned_w.numpy()[0])
print("真实偏置:", true_b)
print("学到的偏置:", learned_b.item())
输出结果
参数比较: 真实权重: [2, -3.4]
学到的权重: [ 2.0007157 -3.4000518]
真实偏置: 4.2
学到的偏置: 4.199595928192139
可以看到学到的参数和真实的参数非常接近