这一节中我们将用到更多 PyTorch 中的功能来更高效地实现线性回归。
本节内容可以参看莫烦教程的部分,重复的内容就不再本节赘述,害,刚入门学习的顺序有点乱。
3.3.1 生成数据集
这一部分与之前大同小异。torch 很大程度上可以替代 NumPy , 所以我把 NumPy 的一些方法也用 torch 来实现了。
# 生成数据集
# 统一类型
elem_type = torch.float32
# 特征个数
feature_size = 2
# 样本数量
example_size = 1000
# 真实的模型参数 (w, b)
true_w = torch.tensor([[2, -3.4]]).t()
true_b = torch.tensor(4.2)
# 输入特征
features = torch.normal(0, 1, (example_size, feature_size), dtype=elem_type)
# 标签, 需要加上正态分布的噪点
labels = torch.mm(features, true_w) + true_b
labels += torch.normal(0, 0.01, size=labels.size(), dtype=elem_type)
3.3.2 读取数据
torch_data
表示 torch.utils.data
这个模块, 这是 torch 中用于处理训练集的模块,读取数据的 data_iter
方法直接从中选取即可。
# 读取数据
# 批次大小
batch_size = 10
# 将特征和标签组合成训练集
data_set = torch_data.TensorDataset(features, labels)
# 随机读取小批次样本
data_iter = torch_data.DataLoader(data_set, batch_size, shuffle=True)
for x, y in data_iter:
print(x, '\n', y)
break
运行结果
tensor([[ 0.3235, -0.6944],
[-0.1412, 0.6338],
[ 1.1980, -0.2269],
[ 0.3041, -1.1575],
[ 0.3344, 0.1352],
[-0.4676, -0.3926],
[-0.3720, 0.7803],
[-0.0704, 0.8043],
[ 0.3639, 0.2389],
[-0.7440, -1.2381]])
tensor([[7.2142],
[1.7614],
[7.3694],
[8.7468],
[4.4089],
[4.6051],
[0.8034],
[1.3365],
[4.1287],
[6.9136]])
3.3.3 定义模型
这一次我们采用神经网络的方法来描述模型。
# 定义模型
class LinearRegressionNet(torch.nn.Module):
"""
线性回归的神经网络模型, 只有输入层和输出层两层, 无隐藏层
Attributes:
output: 输出层
"""
def __init__(self, feature_size):
"""
定义神经网络的架构
Args:
feature_size: 输入层的特征数量
Returns:
无
Raises:
无
"""
super(LinearRegressionNet, self).__init__()
self.output = torch.nn.Linear(feature_size, 1)
def forward(self, x):
"""
定义前向传播的计算过程
Args:
x: 输入特征
Returns:
神经网络的输出层
Raises:
无
"""
return self.output(x)
net = LinearRegressionNet(feature_size)
print(net)
# 查看参数
for param in net.parameters():
print(param)
运行结果
LinearRegressionNet(
(output): Linear(in_features=2, out_features=1, bias=True)
)
Parameter containing:
tensor([[0.5551, 0.5430]], requires_grad=True)
Parameter containing:
tensor([-0.1354], requires_grad=True)
我们可以看到网络的结构和参数。
以下提供几种快速搭建法,可以更加简洁地定义网络的架构。Sequential
是一个有序的容器,网络层将按照在传入Sequential
的顺序依次被添加到计算图中。
# 快递搭建网络
# 方法 1
net = torch.nn.Sequential(
torch.nn.Linear(feature_size, 1)
# 此处还可以写其他层
)
# 方法 2
net = torch.nn.Sequential()
net.add_module("output", torch.nn.Linear(feature_size, 1))
# net.add_moudle() ......
# 方法 3
from collections import OrderedDict
net = torch.nn.Sequential(OrderedDict([
("output", torch.nn.Linear(feature_size, 1))
# ......
]))
注意:
torch.nn
仅支持输入一个batch的样本不支持单个样本输入,如果只有单个样本,可使用input.unsqueeze(0)
来添加一维。
3.3.4 初始化模型参数
torch 自动对参数进行了初始化,当然我们也可以用torch.nn.init
自己设定初始值。torch.nn.init
模块中不带_
的方法已经弃用,使用带 _
的。
# 初始化模型参数
from torch.nn import init # 参数初始化模块
init.normal_(net.output.weight, mean=0, std=0.01)
init.constant_(net.output.bias, val=0)
3.3.5 选择损失函数
这次我们不用再自己定义损失函数了,从 torch 的库中挑选即可,我们依旧选择平方误差函数。
# 选择损失函数
loss_func = torch.nn.MSELoss()
3.3.6 选择优化算法
# 选择随机梯度下减法
optimizer = torch.optim.SGD(net.parameters(), lr=0.03)
print(optimizer)
运行结果
SGD (
Parameter Group 0
dampening: 0
lr: 0.03
momentum: 0
nesterov: False
weight_decay: 0
)
以下是对于学习率的一些补充,当然在这个例子中还用不上。
当我们需要更加精确的学习率时,我们可以为不同的子网络单独设置学习率
# 为不同的子网络设置不同的学习率
optimizer = torch.optim.SGD([
# 如果对某个参数不指定学习率,就使用最外层的默认学习率
{'params': net.subnet1.parameters()}, # lr=0.03
{'params': net.subnet2.parameters(), 'lr': 0.01}
], lr=0.03)
有时候我们甚至需要学习率是动态变化的而非一个固定的常数,我们当然可以重新配置一个优化器(这对于使用动量的优化算法比如 Adam 存在一定风险)。也可以通过修改 param_groups
属性来实现。
# 调整学习率
for param_group in optimizer.param_groups:
# 学习率调整为之前的 0.1
param_group["lr"] *= 0.1
3.3.7 训练模型
这一步变化不大
# 训练模型
# 迭代次数
iterate_num = 3
for i in range(iterate_num + 1):
for x, y in data_iter:
output = net(x)
lose = loss_func(output, y.view(output.size()))
optimizer.zero_grad()
lose.backward()
optimizer.step()
print("第{0}次迭代后的损失为{1:.7f}".format(i + 1, lose.item()))
运行结果
第1次迭代后的损失为0.0001321
第2次迭代后的损失为0.0001269
第3次迭代后的损失为0.0000623
第4次迭代后的损失为0.0001002
最后
打印结果
print(true_w, true_b)
print(net.output.weight, net.output.bias)
运行结果
tensor([[ 2.0000],
[-3.4000]]) tensor(4.2000)
Parameter containing:
tensor([[ 1.9993, -3.3998]], requires_grad=True) Parameter containing:
tensor([4.1995], requires_grad=True)
小结
整体流程是一致的,PyTorch 提供了相当多的组件,我们在搭建和训练模型时,更多得是需要选择适合问题的功能来使用。以下四个模块是常用的,注意一下它们的功能。torch.utils.data
: 提供了有关数据处理的工具torch.nn
: 定义了大量神经网络的层,误差函数torch.nn.init
: 定义了各种初始化方法torch.optim
: 提供了很多常用的优化算法。