这一节中我们将用到更多 PyTorch 中的功能来更高效地实现线性回归。
本节内容可以参看莫烦教程的部分,重复的内容就不再本节赘述,害,刚入门学习的顺序有点乱。

3.3.1 生成数据集

这一部分与之前大同小异。torch 很大程度上可以替代 NumPy , 所以我把 NumPy 的一些方法也用 torch 来实现了。

  1. # 生成数据集
  2. # 统一类型
  3. elem_type = torch.float32
  4. # 特征个数
  5. feature_size = 2
  6. # 样本数量
  7. example_size = 1000
  8. # 真实的模型参数 (w, b)
  9. true_w = torch.tensor([[2, -3.4]]).t()
  10. true_b = torch.tensor(4.2)
  11. # 输入特征
  12. features = torch.normal(0, 1, (example_size, feature_size), dtype=elem_type)
  13. # 标签, 需要加上正态分布的噪点
  14. labels = torch.mm(features, true_w) + true_b
  15. labels += torch.normal(0, 0.01, size=labels.size(), dtype=elem_type)

3.3.2 读取数据

torch_data 表示 torch.utils.data 这个模块, 这是 torch 中用于处理训练集的模块,读取数据的 data_iter 方法直接从中选取即可。

  1. # 读取数据
  2. # 批次大小
  3. batch_size = 10
  4. # 将特征和标签组合成训练集
  5. data_set = torch_data.TensorDataset(features, labels)
  6. # 随机读取小批次样本
  7. data_iter = torch_data.DataLoader(data_set, batch_size, shuffle=True)
  8. for x, y in data_iter:
  9. print(x, '\n', y)
  10. break

运行结果

  1. tensor([[ 0.3235, -0.6944],
  2. [-0.1412, 0.6338],
  3. [ 1.1980, -0.2269],
  4. [ 0.3041, -1.1575],
  5. [ 0.3344, 0.1352],
  6. [-0.4676, -0.3926],
  7. [-0.3720, 0.7803],
  8. [-0.0704, 0.8043],
  9. [ 0.3639, 0.2389],
  10. [-0.7440, -1.2381]])
  11. tensor([[7.2142],
  12. [1.7614],
  13. [7.3694],
  14. [8.7468],
  15. [4.4089],
  16. [4.6051],
  17. [0.8034],
  18. [1.3365],
  19. [4.1287],
  20. [6.9136]])

3.3.3 定义模型

这一次我们采用神经网络的方法来描述模型。

  1. # 定义模型
  2. class LinearRegressionNet(torch.nn.Module):
  3. """
  4. 线性回归的神经网络模型, 只有输入层和输出层两层, 无隐藏层
  5. Attributes:
  6. output: 输出层
  7. """
  8. def __init__(self, feature_size):
  9. """
  10. 定义神经网络的架构
  11. Args:
  12. feature_size: 输入层的特征数量
  13. Returns:
  14. Raises:
  15. """
  16. super(LinearRegressionNet, self).__init__()
  17. self.output = torch.nn.Linear(feature_size, 1)
  18. def forward(self, x):
  19. """
  20. 定义前向传播的计算过程
  21. Args:
  22. x: 输入特征
  23. Returns:
  24. 神经网络的输出层
  25. Raises:
  26. """
  27. return self.output(x)
  28. net = LinearRegressionNet(feature_size)
  29. print(net)
  30. # 查看参数
  31. for param in net.parameters():
  32. print(param)

运行结果

  1. LinearRegressionNet(
  2. (output): Linear(in_features=2, out_features=1, bias=True)
  3. )
  4. Parameter containing:
  5. tensor([[0.5551, 0.5430]], requires_grad=True)
  6. Parameter containing:
  7. tensor([-0.1354], requires_grad=True)

我们可以看到网络的结构和参数。
以下提供几种快速搭建法,可以更加简洁地定义网络的架构。
Sequential是一个有序的容器,网络层将按照在传入Sequential的顺序依次被添加到计算图中。

  1. # 快递搭建网络
  2. # 方法 1
  3. net = torch.nn.Sequential(
  4. torch.nn.Linear(feature_size, 1)
  5. # 此处还可以写其他层
  6. )
  7. # 方法 2
  8. net = torch.nn.Sequential()
  9. net.add_module("output", torch.nn.Linear(feature_size, 1))
  10. # net.add_moudle() ......
  11. # 方法 3
  12. from collections import OrderedDict
  13. net = torch.nn.Sequential(OrderedDict([
  14. ("output", torch.nn.Linear(feature_size, 1))
  15. # ......
  16. ]))

注意:torch.nn仅支持输入一个batch的样本不支持单个样本输入,如果只有单个样本,可使用input.unsqueeze(0)来添加一维。

3.3.4 初始化模型参数

torch 自动对参数进行了初始化,当然我们也可以用torch.nn.init自己设定初始值。
torch.nn.init 模块中不带_的方法已经弃用,使用带 _ 的。

  1. # 初始化模型参数
  2. from torch.nn import init # 参数初始化模块
  3. init.normal_(net.output.weight, mean=0, std=0.01)
  4. init.constant_(net.output.bias, val=0)

3.3.5 选择损失函数

这次我们不用再自己定义损失函数了,从 torch 的库中挑选即可,我们依旧选择平方误差函数。

  1. # 选择损失函数
  2. loss_func = torch.nn.MSELoss()

3.3.6 选择优化算法

  1. # 选择随机梯度下减法
  2. optimizer = torch.optim.SGD(net.parameters(), lr=0.03)
  3. print(optimizer)

运行结果

  1. SGD (
  2. Parameter Group 0
  3. dampening: 0
  4. lr: 0.03
  5. momentum: 0
  6. nesterov: False
  7. weight_decay: 0
  8. )

以下是对于学习率的一些补充,当然在这个例子中还用不上。

当我们需要更加精确的学习率时,我们可以为不同的子网络单独设置学习率

  1. # 为不同的子网络设置不同的学习率
  2. optimizer = torch.optim.SGD([
  3. # 如果对某个参数不指定学习率,就使用最外层的默认学习率
  4. {'params': net.subnet1.parameters()}, # lr=0.03
  5. {'params': net.subnet2.parameters(), 'lr': 0.01}
  6. ], lr=0.03)

有时候我们甚至需要学习率是动态变化的而非一个固定的常数,我们当然可以重新配置一个优化器(这对于使用动量的优化算法比如 Adam 存在一定风险)。也可以通过修改 param_groups 属性来实现。

  1. # 调整学习率
  2. for param_group in optimizer.param_groups:
  3. # 学习率调整为之前的 0.1
  4. param_group["lr"] *= 0.1

3.3.7 训练模型

这一步变化不大

  1. # 训练模型
  2. # 迭代次数
  3. iterate_num = 3
  4. for i in range(iterate_num + 1):
  5. for x, y in data_iter:
  6. output = net(x)
  7. lose = loss_func(output, y.view(output.size()))
  8. optimizer.zero_grad()
  9. lose.backward()
  10. optimizer.step()
  11. print("第{0}次迭代后的损失为{1:.7f}".format(i + 1, lose.item()))

运行结果

  1. 1次迭代后的损失为0.0001321
  2. 2次迭代后的损失为0.0001269
  3. 3次迭代后的损失为0.0000623
  4. 4次迭代后的损失为0.0001002

最后

打印结果

  1. print(true_w, true_b)
  2. print(net.output.weight, net.output.bias)

运行结果

  1. tensor([[ 2.0000],
  2. [-3.4000]]) tensor(4.2000)
  3. Parameter containing:
  4. tensor([[ 1.9993, -3.3998]], requires_grad=True) Parameter containing:
  5. tensor([4.1995], requires_grad=True)

小结

整体流程是一致的,PyTorch 提供了相当多的组件,我们在搭建和训练模型时,更多得是需要选择适合问题的功能来使用。以下四个模块是常用的,注意一下它们的功能。
torch.utils.data: 提供了有关数据处理的工具
torch.nn: 定义了大量神经网络的层,误差函数
torch.nn.init: 定义了各种初始化方法
torch.optim: 提供了很多常用的优化算法。

3.3 线性回归的简洁实现.py