定义与初始化模型

softmax回归的输出层是一个全连接层,所以我们用一个线性模块就可以了。因为前面我们数据返回的每个batch样本x的形状为(batch_size, 1, 28, 28), 所以我们要先用view()x的形状转换成(batch_size, 784)才送入全连接层。

由于我们输入的张量为12828维的图像,必须转成n*1维,否则模型无法读取。因此我们定义一个net用来对输入数据做预处理。以后我们也可以通过这一层网络来实现输入张量的形状转换。
顺便提一点,在用轮子造模型时,不用手动调用forward()函数,系统会自动帮我们调用向前传播函数

  1. class Module(nn.Module):
  2. def __init__(self):
  3. super(Module, self).__init__()
  4. # ......
  5. def forward(self, x):
  6. # ......
  7. return x
  8. data = ..... #输入数据
  9. # 实例化一个对象
  10. module = Module()
  11. # 前向传播
  12. module(data)
  13. # 而不是使用下面的
  14. # module.forward(data)
  15. # 事实上module(data)等价于module.forward(data)
  1. class FlattenLayer(nn.Module):
  2. def __init__(self):
  3. super(FlattenLayer, self).__init__()
  4. # 模型训练时,不需要使用forward,只要在实例化一个对象中传入对应的参数就可以自动调用 forward 函数
  5. def forward(self, x):
  6. return x.view(x.shape[0], -1)

再提有关Sequential搭建网络的一点。Sequential是一个Sequential容器,模块将按照构造函数中传递的顺序添加到模块中。另外,也可以传入一个有序模块。
有序字典的好处在于元素会严格按照定义时的顺序排列,而不会像普通字典一样打乱。
因此,Sequential通过构造函数快速构建神经网络既可以直接调用其他神经网络的构造函数,也可以使用OrderedDict有序字典来搭建,区别在于后者可以为神经网络的隐藏层进行命名,而前者只能通过数组下标来访问。

  1. net = nn.Sequential(
  2. # 像这样搭建网络也可,但无法通过net.flatten这样调用
  3. # FlattenLayer(),
  4. # nn.Linear(num_input, num_outputs)
  5. OrderedDict([
  6. ('flatten', FlattenLayer()),
  7. ('linear', nn.Linear(num_input, num_outputs))
  8. ])
  9. )
  10. print(net)
  11. print(net[0])
  12. print(net.flatten)
  13. 结果:
  14. Sequential(
  15. (flatten): FlattenLayer()
  16. (linear): Linear(in_features=784, out_features=10, bias=True)
  17. )
  18. FlattenLayer()
  19. FlattenLayer()

可以调用nn包中的init函数库进行参数初始化,normal表标准差,constant表初始化指定的值,其实等同于之前的torch.normal以及torch.zeros初始化方法
详细参见:init参数初始化方法

  1. init.normal_(net.linear.weight, mean=0, std=0.01)
  2. init.constant_(net.linear.bias, val=0)

损失函数及优化算法

如果做了上一节的练习,那么你可能意识到了分开定义softmax运算和交叉熵损失函数可能会造成数值不稳定。因此,PyTorch提供了一个包括softmax运算和交叉熵损失计算的函数。它的数值稳定性更好。

我没明白

  1. loss = nn.CrossEntropyLoss()
  2. optimizer = torch.optim.SGD(net.parameters(), lr=0.1)

训练及完整代码

使用的训练函数与上一讲相同

  1. import torch
  2. import d2lzh
  3. from torch import nn
  4. from torch.nn import init
  5. from collections import OrderedDict
  6. batch_size = 256
  7. train_iter, test_iter = d2lzh.load_data_fashion_mnist(batch_size)
  8. num_input = 784
  9. num_outputs = 10
  10. # 由于我们输入的张量为1*28*28维的图像,必须转成n*1维,否则模型无法读取。因此我们定义一个net用来对输入数据做预处理
  11. class FlattenLayer(nn.Module):
  12. def __init__(self):
  13. super(FlattenLayer, self).__init__()
  14. # 模型训练时,不需要使用forward,只要在实例化一个对象中传入对应的参数就可以自动调用 forward 函数
  15. def forward(self, x):
  16. return x.view(x.shape[0], -1)
  17. # Sequential通过构造函数快速构建神经网络既可以直接调用其他神经网络的构造函数,也可以使用OrderedDict有序字典来搭建,区别在于后者可以为神经网络的隐藏层进行命名,而前者只能通过数组下标来访问
  18. net = nn.Sequential(
  19. # FlattenLayer(),
  20. # nn.Linear(num_input, num_outputs)
  21. # 有序字典的好处在于元素会严格按照定义时的顺序排列,而不会像普通字典一样打乱
  22. OrderedDict([
  23. ('flatten', FlattenLayer()),
  24. ('linear', nn.Linear(num_input, num_outputs))
  25. ])
  26. )
  27. if __name__ == "__main__":
  28. # 可以调用nn包中的init函数库进行参数初始化,normal表标准差,constant表初始化指定的值,其实等同于之前的torch.normal以及torch.zeros初始化方法
  29. init.normal_(net.linear.weight, mean=0, std=0.01)
  30. init.constant_(net.linear.bias, val=0)
  31. loss = nn.CrossEntropyLoss()
  32. optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
  33. num_epochs = 5
  34. d2lzh.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)