深度卷积神经网络

在LeNet提出后,卷积神经网络在计算机视觉和机器学习领域中很有名气。但卷积神经网络并没有主导这些领域。这是因为虽然LeNet在小数据集上取得了很好的效果,但是在更大、更真实的数据集上训练卷积神经网络的性能和可行性还有待研究。事实上,在上世纪90年代初到2012年之间的大部分时间里,神经网络往往被其他机器学习方法超越,如支持向量机(support vector machines)。

在计算机视觉中,直接将神经网络与其他机器学习方法进行比较也许不公平。这是因为,卷积神经网络的输入是由原始像素值或是经过简单预处理(例如居中、缩放)的像素值组成的。但在使用传统机器学习方法时,从业者永远不会将原始像素作为输入。在传统机器学习方法中,计算机视觉流水线是由经过人的手工精心设计的特征流水线组成的。对于这些传统方法,大部分的进展都来自于对特征有了更聪明的想法,并且学习到的算法往往归于事后的解释。

虽然上世纪90年代就有了一些神经网络加速卡,但仅靠它们还不足以开发出有大量参数的深层多通道多层卷积神经网络。此外,当时的数据集仍然相对较小。除了这些障碍,训练神经网络的一些关键技巧仍然缺失,包括启发式参数初始化、随机梯度下降的变体、非挤压激活函数和有效的正则化技术。

因此,与训练端到端(从像素到分类结果)系统不同,经典机器学习的流水线看起来更像下面这样:

  • 获取一个有趣的数据集。在早期,收集这些数据集需要昂贵的传感器(在当时最先进的图像也就100万像素)。
  • 根据光学、几何学、其他知识以及偶然的发现,手工对特征数据集进行预处理。
  • 通过标准的特征提取算法,如SIFT(尺度不变特征变换) (Lowe, 2004)和SURF(加速鲁棒特征) (Bay et al., 2006)或其他手动调整的流水线来输入数据。
  • 将提取的特征送入最喜欢的分类器中(例如线性模型或其它核方法),以训练分类器。

当人们和机器学习研究人员交谈时,会发现机器学习研究人员相信机器学习既重要又美丽:优雅的理论去证明各种模型的性质。机器学习是一个正在蓬勃发展、严谨且非常有用的领域。然而,当人们和计算机视觉研究人员交谈,会听到一个完全不同的故事。计算机视觉研究人员会告诉一个诡异事实————推动领域进步的是数据特征,而不是学习算法。计算机视觉研究人员相信,从对最终模型精度的影响来说,更大或更干净的数据集、或是稍微改进的特征提取,比任何学习算法带来的进步要大得多。

学习表征

在2012年之前,图像特征的提取主要是通过手工设计特征函数来完成的,这些特征函数包括SIFT、SURF、HOG以及bags of visual words等,它们在当时占据了主导地位。然而,有一组研究人员,包括Yann LeCun、Geoff Hinton等,提出了不同的观点,他们认为特征应该通过学习得到,并且特征的提取应该由多个神经网络层共同完成,每一层都有可学习的参数。

其中的代表就是AlexNet,这是一种卷积神经网络的变体,由Alex Krizhevsky、Ilya Sutskever和Geoff Hinton提出。AlexNet在2012年的ImageNet挑战赛中取得了显著的成绩,这一成就标志着深度学习在计算机视觉领域的重大突破。AlexNet的命名来源于论文的第一作者Alex Krizhevsky。

在AlexNet的网络结构中,底层学习到的特征类似于传统的滤波器,能够检测到边缘、颜色和纹理等基本特征。随着网络层次的增加,更高层能够表示更复杂的特征,如眼睛、鼻子等,而最高层则能够检测到整个物体,如人、飞机等。最终,网络能够学习到图像的综合表示,使得不同类别的数据易于区分。

深度卷积神经网络在2012年取得突破的两个关键因素:大量的有标签数据和强大的计算硬件。大量的数据使得深度模型能够学习到复杂的特征,而GPU等硬件的发展则为训练这些模型提供了必要的计算能力。

缺少的成分:数据

包含许多特征的深度模型需要大量的有标签数据,才能显著优于基于凸优化的传统方法(如线性方法和核方法)。 然而,限于早期计算机有限的存储和90年代有限的研究预算,大部分研究只基于小的公开数据集。例如,不少研究论文基于加州大学欧文分校(UCI)提供的若干个公开数据集,其中许多数据集只有几百至几千张在非自然环境下以低分辨率拍摄的图像。这一状况在2010年前后兴起的大数据浪潮中得到改善。2009年,ImageNet数据集发布,并发起ImageNet挑战赛:要求研究人员从100万个样本中训练模型,以区分1000个不同类别的对象。ImageNet数据集由斯坦福教授李飞飞小组的研究人员开发,利用谷歌图像搜索(Google Image Search)对每一类图像进行预筛选,并利用亚马逊众包(Amazon Mechanical Turk)来标注每张图片的相关类别。这种规模是前所未有的。这项被称为ImageNet的挑战赛推动了计算机视觉和机器学习研究的发展,挑战研究人员确定哪些模型能够在更大的数据规模下表现最好。

"凸优化"是一种数学优化方法,它在机器学习和人工智能领域中非常重要。凸优化问题是指在一定条件下,目标函数和约束条件都是凸的,即函数在其定义域内任何两点间的线段上,函数值都不小于这两点的函数值。换句话说,凸函数的图形在任何地方都不会凹陷进去,而是向外凸出。
在机器学习中,凸优化常用于寻找最优解,例如线性回归、支持向量机(SVM)等模型的训练过程。由于凸问题具有许多优良的性质,比如局部最优解就是全局最优解,因此它们相对容易求解,并且求解过程稳定。
然而,凸优化也有其局限性。当问题是非凸的,即目标函数或约束条件不是凸的,那么找到全局最优解就变得非常困难。在深度学习领域,许多模型的优化问题是非凸的,这就需要使用更复杂的优化算法来求解。
深度模型在大量有标签数据的情况下,可以显著优于基于凸优化的传统方法。这表明,尽管凸优化在某些情况下非常有效,但深度学习模型通过处理大规模数据集,能够实现更复杂的模式识别和学习任务。

缺少的成分:硬件

  1. 深度学习对计算资源的要求:深度学习模型的训练过程需要进行大量的迭代,每次迭代都需要处理大量的线性代数运算,这使得对计算资源的需求非常高。

  2. 20世纪90年代至21世纪初的算法选择:由于计算资源的限制,研究人员倾向于使用优化凸目标的简单算法。

  3. GPU的引入:图形处理器(GPU)最初设计用于加速图形处理,但后来发现它们在执行矩阵和向量乘法等操作时非常高效,这些操作与深度学习中的卷积层计算非常相似。因此,GPU开始被用于深度学习的训练,极大地提高了计算效率。

  4. GPU与CPU的比较

    • CPU:中央处理器(CPU)的每个核心具有高时钟频率和大缓存,适合执行各种指令,但制造成本高,且在单一任务上性能有限。
    • GPU:图形处理器由许多小的处理单元组成,虽然每个核心性能较弱,但核心数量庞大,使得GPU在处理并行任务时性能远超CPU。
  5. GPU的性能优势

    • GPU的功耗相对较低,因为它们的内核简单且运行频率较低。
    • GPU具有更高的内存带宽,这对于深度学习中的数据传输至关重要。
  6. 深度学习的重大突破:2012年,Alex Krizhevsky和Ilya Sutskever实现了可以在GPU上运行的深度卷积神经网络,这一创新使得卷积神经网络的计算瓶颈得以解决,推动了深度学习的发展。

  7. cuda-convnet:这是Krizhevsky和Sutskever开发的一个深度学习框架,它利用GPU的并行计算能力,提高了卷积神经网络的训练速度,成为行业标准。

AlexNet

2012年,AlexNet横空出世。它首次证明了学习到的特征可以超越手工设计的特征。它一举打破了计算机视觉研究的现状。 AlexNet使用了8层卷积神经网络,并以很大的优势赢得了2012年ImageNet图像识别挑战赛。

AlexNet和LeNet的架构非常相似,如下图所示。注意,这里提供的是一个稍微精简版本的AlexNet,去除了当年需要两个小型GPU同时运算的设计特点。

从LeNet(左)到AlexNet(右)

AlexNet和LeNet的设计理念非常相似,但也存在显著差异。

  1. AlexNet比相对较小的LeNet5要深得多。AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。

  2. AlexNet使用ReLU而不是sigmoid作为其激活函数。

下面的内容将深入研究AlexNet的细节。

模型设计

在AlexNet的第一层,卷积窗口的形状为\(11\times11\)。由于ImageNet中大多数图像的高和宽比MNIST图像的大10倍以上,因此,需要一个更大的卷积窗口来捕获目标。第二层的卷积窗口形状被缩减为\(5\times5\),然后是\(3\times3\)。此外,在第一层、第二层和第五层卷积层之后,加入窗口形状为\(3\times3\)、步幅为2的最大汇聚层。而且,AlexNet的卷积通道为LeNet的10倍。

在最后一个卷积层后有两个全连接层,分别有4096个输出。 这两个巨大的全连接层拥有将近1GB的模型参数。 由于早期GPU显存有限,原版的AlexNet采用了双数据流设计,使得每个GPU只负责存储和计算模型的一半参数。 幸运的是,现在GPU显存相对充裕,所以现在很少需要跨GPU分解模型。

激活函数

此外,AlexNet将sigmoid激活函数改为更简单的ReLU激活函数。 一方面,ReLU激活函数的计算更简单,它不需要如sigmoid激活函数那般复杂的求幂运算。 另一方面,当使用不同的参数初始化方法时,ReLU激活函数使训练模型更加容易。 当sigmoid激活函数的输出非常接近于0或1时,这些区域的梯度几乎为0,因此反向传播无法继续更新一些模型参数。 相反,ReLU激活函数在正区间的梯度总是1。 因此,如果模型参数没有正确初始化,sigmoid函数可能在正区间内得到几乎为0的梯度,从而使模型无法得到有效的训练。

容量控制和预处理

AlexNet通过暂退法(dropout)控制全连接层的模型复杂度,而LeNet只使用了权重衰减。 为了进一步扩充数据,AlexNet在训练时增加了大量的图像增强数据,如翻转、裁切和变色。 这使得模型更健壮,更大的样本量有效地减少了过拟合。 以后会更详细地讨论数据扩增。

import torch
from torch import nn
from d2l import torch as d2l

net = nn.Sequential(
    # 这里使用一个11*11的更大的窗口来捕获对象
    # input:[batch_size, 3, 224, 224]
    nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
    # input:[batch_size, 96, 54, 54]
    nn.MaxPool2d(kernel_size=3, stride=2),
    # input:[batch_size, 96, 26, 26]
    nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
    # input:[batch_size, 256, 26, 26]
    nn.MaxPool2d(kernel_size=3, stride=2),
    # input:[batch_size, 256, 12, 12]
    nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
    # input:[batch_size, 384, 12, 12]
    nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
    # input:[batch_size, 384, 12, 12]
    nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
    # input:[batch_size, 256, 12, 12]
    nn.MaxPool2d(kernel_size=3, stride=2),
    # input:[batch_size, 256, 5, 5]
    nn.Flatten(),
    # input:[batch_size, 256*5*5=6400]
    nn.Linear(6400, 4096), nn.ReLU(),
    # input:[batch_size, 4096]
    nn.Linear(4096, 4096), nn.ReLU(),
    # input:[batch_size, 4096]
    nn.Dropout(p=0.5),
    # input:[batch_size, 4096]
    nn.Linear(4096, 10)
    # output:[batch_size, 10]
)

我们构造一个高度和宽度都为224的单通道数据,来观察每一层输出的形状。 它上图中的AlexNet架构相匹配。

X = torch.rand(1, 1, 224, 223)
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__,'output shape:\t',X.shape)
Conv2d output shape:	 torch.Size([1, 96, 54, 54])
ReLU output shape:	 torch.Size([1, 96, 54, 54])
MaxPool2d output shape:	 torch.Size([1, 96, 26, 26])
Conv2d output shape:	 torch.Size([1, 256, 26, 26])
ReLU output shape:	 torch.Size([1, 256, 26, 26])
MaxPool2d output shape:	 torch.Size([1, 256, 12, 12])
Conv2d output shape:	 torch.Size([1, 384, 12, 12])
ReLU output shape:	 torch.Size([1, 384, 12, 12])
Conv2d output shape:	 torch.Size([1, 384, 12, 12])
ReLU output shape:	 torch.Size([1, 384, 12, 12])
Conv2d output shape:	 torch.Size([1, 256, 12, 12])
ReLU output shape:	 torch.Size([1, 256, 12, 12])
MaxPool2d output shape:	 torch.Size([1, 256, 5, 5])
Flatten output shape:	 torch.Size([1, 6400])
Linear output shape:	 torch.Size([1, 4096])
ReLU output shape:	 torch.Size([1, 4096])
Linear output shape:	 torch.Size([1, 4096])
ReLU output shape:	 torch.Size([1, 4096])
Dropout output shape:	 torch.Size([1, 4096])
Linear output shape:	 torch.Size([1, 10])

读取数据集

尽管原文中AlexNet是在ImageNet上进行训练的,但本书在这里使用的是Fashion-MNIST数据集。因为即使在现代GPU上,训练ImageNet模型,同时使其收敛可能需要数小时或数天的时间。 将AlexNet直接应用于Fashion-MNIST的一个问题是,Fashion-MNIST图像的分辨率(\(28\times28\)像素)低于ImageNet图像。 为了解决这个问题,我们将它们增加到\(224\times224\)像素(通常来讲这不是一个明智的做法,但在这里这样做是为了有效使用AlexNet架构)。 这里需要使用d2l.load_data_fashion_mnist函数中的resize参数执行此调整。

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

训练AlexNet

现在AlexNet可以开始被训练了。与上一篇的LeNet相比,这里的主要变化是使用更小的学习速率训练,这是因为网络更深更广、图像分辨率更高,训练卷积神经网络就更昂贵。

lr, num_epochs = 0.01, 10
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
loss 0.293, train acc 0.893, test acc 0.872
949.8 examples/sec on cuda:0

posted @ 2024-08-03 14:42  codersgl  阅读(5)  评论(0编辑  收藏  举报