[pytorch、学习] - 5.6 深度卷积神经网络(AlexNet)

参考

5.6 深度卷积神经网络(AlexNet)

在LeNet提出后的将近20年里,神经网络一度被其他机器学习方法超越,如支持向量机。虽然LeNet可以在早期的小数据集上取得好的成绩,但是在更大的真实数据集上的表现并不尽如人意。一方面,神经网络计算复杂。虽然20世纪90年代也有过一些针对神经网络的加速硬件,但并没有像之后GPU那样大量普及。因此,训练一个多通道、多层和有大量参数的卷积神经网络在当年很难完成。另一方面,当年研究者还没有大量深入研究参数初始化和非凸优化算法等诸多领域,导致复杂的神经网络的训练通常较困难。

在很长一段时间里更流行的是研究者通过勤劳与智慧所设计并生成的手工特征。这类图像分类研究的主要流程是:

  1. 获取图像数据集;
  2. 使用已有的特征提取函数生成图像的特征;
  3. 使用机器学习模型对图像的特征分类。

当时认为的机器学习部分仅限最后这一步。如果那时候跟机器学习研究者交谈,他们会认为机器学习既重要又优美。优雅的定理证明了许多分类器的性质。机器学习领域生机勃勃、严谨而且极其有用。然而,如果跟计算机视觉研究者交谈,则是另外一幅景象。他们会告诉你图像识别里“不可告人”的现实是:计算机视觉流程中真正重要的是数据和特征。也就是说,使用较干净的数据集和较有效的特征甚至比机器学习模型的选择对图像分类结果的影响更大。

5.6.1 学习特征表示

研究者相信,多层神经网络可能可以学得数据的多级表征,并逐级表示越来越抽象的概念。以图像分类为例:在多层神经网络中,图像的第一级的表示可以是在特定位置和角度是否出现边缘;而第二级的表示说不定能够将这些边缘组合出有趣的模式,如花纹;在第三级的表示中,也许上以及的花纹能进一步汇合成对应物体特定部位的模式。这样逐级表示下去,最终,模型能够较容易根据最后一级的表示完成分类任务。需要强调的是,输入的逐级表示由多层模型中的参数决定,而这些参数都是学习出来的。

5.6.1.1 缺失要素一: 数据

包含许多特征的深度模型需要大量的有标签的数据才能表现得比其他经典方法更好。限于早期计算机有限的存储和90年代有限的研究预算,大部分研究只基于小的公开数据集。例如,不少研究论文基于加州大学欧文分校(UCI)提供的若干个公开数据集,其中许多数据集只有几百至几千张图像。这一状况在2010年前后兴起的大数据浪潮中得到改善。特别是,2009年诞生的ImageNet数据集包含了1,000大类物体,每类有多达数千张不同的图像。这一规模是当时其他公开数据集无法与之相提并论的。ImageNet数据集同时推动计算机视觉和机器学习研究进入新的阶段,使此前的传统方法不再有优势。

5.6.1.2 缺失要素二: 硬件

深度学习对计算资源要求很高。早期的硬件计算能力有限,这使训练较复杂的神经网络变得很困难。然而,通用GPU的到来改变了这一格局。很久以来,GPU都是为图像处理和计算机游戏设计的,尤其是针对大吞吐量的矩阵和向量乘法从而服务于基本的图形变换。值得庆幸的是,这其中的数学表达与深度网络中的卷积层的表达类似。通用GPU这个概念在2001年开始兴起,涌现出诸如OpenCL和CUDA之类的编程框架。这使得GPU也在2010年前后开始被机器学习社区使用。

5.6.2 AlexNet

在这里插入图片描述

下面实现简化过的AlexNet

import time
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision

import sys
sys.path.append("..")
import d2lzh_pytorch as d2l


device = torch.device("cuda" if torch.cuda.is_available() else 'cpu')

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.conv = nn.Sequential(
            # N = (W - F + 2P)/S + 1,除不尽向下取整(记不清了,向上取整对不上,向下取整刚好对上...)
            nn.Conv2d(1, 96, 11, 4),   # in_channels, out_channels, kernel_size, stride, padding: (256, 1, 224, 224) -> (256, 96, 54,54)
            nn.ReLU(),
            nn.MaxPool2d(3, 2),   # kernel_size, stride: (256, 96, 54, 54) -> (256, 96, 26, 26)
            # 减少卷积窗口,使用填充为2来使得输入的高和宽一致,且增大输出通道数
            nn.Conv2d(96, 256, 5, 1, 2),  # (256, 96, 26, 26) -> (256, 256, 26, 26)
            nn.ReLU(),
            nn.MaxPool2d(3, 2),  # (256, 256, 26, 26) -> (256, 256, 12, 12)
            # 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,进一步增大了输出通道数。
            # 前两个卷积层不使用池化层来减小输入的高和宽
            nn.Conv2d(256, 384, 3, 1, 1),   # (256, 256, 12, 12) -> (256, 384, 12, 12)
            nn.ReLU(),
            nn.Conv2d(384, 384, 3, 1, 1),  # (256, 384, 12, 12) -> (256, 384, 12, 12)
            nn.ReLU(),
            nn.Conv2d(384, 256, 3, 1, 1),  # (256, 384, 12, 12) -> (256, 256, 12, 12)
            nn.ReLU(),
            nn.MaxPool2d(3,2)  # (256, 256, 12, 12) -> (256, 256, 5, 5)
        )
        # 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
        self.fc = nn.Sequential(
            nn.Linear(256*5*5, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            # 输出层。由于这里使用Fashion-MNIST,所以用类别数为10
            nn.Linear(4096, 10),
        )
    
    def forward(self, img):
        feature = self.conv(img)  # 256 * 1 * 224 * 224
        output = self.fc(feature.view(img.shape[0], -1))
        return output
net = AlexNet()
print(net)

在这里插入图片描述

5.6.3 读取数据

def load_data_fashion_mnist(batch_size, resize= None, root="~/Datasets/FashionMNIST"):
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size = resize))
    trans.append(torchvision.transforms.ToTensor())
    
    transform = torchvision.transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
    mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
    
    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size = batch_size, shuffle=True, num_workers=4)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size = batch_size, shuffle=False, num_workers=4)
    
    return train_iter, test_iter

batch_size = 128
train_iter, test_iter = load_data_fashion_mnist(batch_size, resize = 224)

5.6.4 训练

lr, num_epochs = 0.001, 5
optimizer = optim.Adam(net.parameters(), lr =lr)
d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/piano9425/article/details/107199826