LeNet|卷积神经网络|动手学深度学习

106 阅读4分钟

1. 将平均汇聚层替换为最大汇聚层,会发生什么?

将下面的 LeNet 实现中的平均汇聚层替换为最大汇聚层,会发生什么?

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)
net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Flatten(),
    nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Linear(84, 10))
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
loss 0.462, train acc 0.827, test acc 0.816
266892.4 examples/sec on cuda:0

image.png

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Flatten(),
    nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Linear(84, 10))
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
loss 0.401, train acc 0.852, test acc 0.846
259683.9 examples/sec on cuda:0

image.png

在LeNet架构中,将平均汇聚层(nn.AvgPool2d)替换为最大汇聚层(nn.MaxPool2d)会改变网络在处理输入数据时的聚合方式。平均汇聚层(Average Pooling)和最大汇聚层(Max Pooling)的主要区别在于它们如何对输入特征图(feature maps)进行降采样。

平均汇聚层通过计算每个窗口区域内的像素值的平均数来生成输出特征图的像素值。而最大汇聚层则选择每个窗口区域内的最大像素值作为输出特征图的像素值。

以下是将平均汇聚层替换为最大汇聚层后可能发生的一些变化:

  1. 不变性增强:最大汇聚层可以增强网络对输入图像中特定特征的位置变化的不变性。这意味着即使特征在图像中发生小的位移,网络仍然能够通过最大汇聚层识别出这些特征。

  2. 特征选择:最大汇聚层倾向于保留每个窗口中最强的激活信号,这可能有助于网络在特征图中选择更加显著的特征。

  3. 计算效率:在某些情况下,最大汇聚层可能比平均汇聚层有更高的计算效率,因为它只需要找到最大值而不是计算所有值的平均。

  4. 梯度消失问题:最大汇聚层可能会加剧梯度消失问题,因为它在反向传播过程中可能会产生更稀疏的梯度。

  5. 网络性能:最大汇聚层可能会影响网络的收敛速度和最终性能。在某些情况下,它可能会提高性能,因为它有助于保留重要的特征信息;而在其他情况下,它可能会导致过拟合,特别是如果网络没有足够的正则化。

  6. 对噪声的敏感性:最大汇聚层相对于平均汇聚层可能对噪声更敏感,因为它只考虑了每个窗口中的最大值,而忽略了其他像素的信息。

  7. 模型大小:由于最大汇聚层和平均汇聚层在PyTorch中的实现通常非常相似,所以替换它们不会直接影响模型的大小。

  8. 调试和理解:最大汇聚层可能会改变特征图的分布,这可能需要重新评估网络的调试和理解。

请注意,实际效果需要通过实验来验证,因为不同数据集和任务可能对这种改变的响应不同。

2. 尝试构建一个基于LeNet的更复杂的网络,以提高其准确性。

  1. 调整卷积窗口大小。
  2. 调整输出通道的数量。
  3. 调整激活函数(如ReLU)。
  4. 调整卷积层的数量。
  5. 调整全连接层的数量。
  6. 调整学习率和其他训练细节(例如,初始化和轮数)。
net = nn.Sequential(
    nn.Conv2d(1, 32, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Flatten(),
    nn.Linear(128 * 3 * 3, 256), nn.Sigmoid(),
    nn.Linear(256, 128), nn.Sigmoid(),
    nn.Linear(128, 10))

3. 在MNIST数据集上尝试以上改进的网络。

lr, num_epochs = 0.5, 20
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
loss 0.193, train acc 0.928, test acc 0.887
149628.3 examples/sec on cuda:0

image.png

4. 显示不同输入(例如毛衣和外套)时,LeNet第一层和第二层的激活值。

print(*[(name, param.shape) for name, param in net.named_parameters()])
('0.weight', torch.Size([32, 1, 3, 3])) ('0.bias', torch.Size([32])) ('3.weight', torch.Size([64, 32, 3, 3])) ('3.bias', torch.Size([64])) ('6.weight', torch.Size([128, 64, 3, 3])) ('6.bias', torch.Size([128])) ('10.weight', torch.Size([256, 1152])) ('10.bias', torch.Size([256])) ('12.weight', torch.Size([128, 256])) ('12.bias', torch.Size([128])) ('14.weight', torch.Size([10, 128])) ('14.bias', torch.Size([10]))