深度卷积神经⽹络(AlexNet)
神经⽹络可以直接基于图像的原始像素进⾏分类。这种称为端到端(end-to�end)的⽅法节省了很多中间步骤。然而,在很⻓⼀段时间⾥更流⾏的是研究者通过勤劳与智慧
所设计并⽣成的⼿⼯特征。这类图像分类研究的主要流程是:
1. 获取图像数据集;
2. 使⽤已有的特征提取函数⽣成图像的特征;
3. 使⽤机器学习模型对图像的特征分类。
当时认为的机器学习部分仅限最后这⼀步。如果那时候跟机器学习研究者交谈,他们会认为机器学习既重要⼜优美。优雅的定理证明了许多分类器的性质。机器学习领域⽣机勃勃、严谨而且极其有⽤。然而,如果跟计算机视觉研究者交谈,则是另外⼀幅景象。他们会告诉你图像识别⾥“不可告⼈”的现实是:计算机视觉流程中真正重要的是数据和特征。也就是说,使⽤较⼲净的数据集和较有效的特征甚⾄⽐机器学习模型的选择对图像分类结果的影响更⼤。
学习特征表⽰
既然特征如此重要,它该如何表⽰呢?
我们已经提到,在相当⻓的时间⾥,特征都是基于各式各样⼿⼯设计的函数从数据中提取的。事实上,不少研究者通过提出新的特征提取函数不断改进图像分类结果。这⼀度为计算机视觉的发展做出了重要贡献。
然而,另⼀些研究者则持异议。他们认为特征本⾝也应该由学习得来。他们还相信,为了表征⾜够复杂的输⼊,特征本⾝应该分级表⽰。持这⼀想法的研究者相信,多层神经⽹络可能可以学得数据的多级表征,并逐级表⽰越来越抽象的概念或模式。以图像分类为例,并回忆“⼆维卷积层” ⼀节中物体边缘检测的例⼦。在多层神经⽹络中,图像的第⼀级的表⽰可以是在特定的位置和⻆度是否出现边缘;而第⼆级的表⽰说不定能够将这些边缘组合出有趣的模式,如花纹;在第三级的表⽰中,也许上⼀级的花纹能进⼀步汇合成对应物体特定部位的模式。这样逐级表⽰下去,最终,模型能够较容易根据最后⼀级的表⽰完成分类任务。需要强调的是,输⼊的逐级表⽰由多层模型中的参数决定,而这些参数都是学出来的。
缺失要素⼀:数据
包含许多特征的深度模型需要⼤量的有标签的数据才能表现得⽐其他经典⽅法更好。限于早期计算机有限的存储和90年代有限的研究预算,⼤部分研究只基于小的公开数据集。例如,不少研究论⽂基于加州⼤学欧⽂分校(UCI)提供的若⼲个公开数据集,其中许多数据集只有⼏百⾄⼏千张图像。这⼀状况在2010年前后兴起的⼤数据浪潮中得到改善。特别是,2009年诞⽣的ImageNet数据集包含了1,000⼤类物体,每类有多达数千张不同的图像。这⼀规模是当时其他公开数据集⽆法与之相提并论的。ImageNet数据集同时推动计算机视觉和机器学习研究进⼊新的阶段,使此前的传统⽅法不再有优势。
缺失要素⼆:硬件
深度学习对计算资源要求很⾼。早期的硬件计算能⼒有限,这使训练较复杂的神经⽹络变得很困难。然而,通⽤GPU的到来改变了这⼀格局。很久以来,GPU都是为图像处理和计算机游戏设计的,尤其是针对⼤吞吐量的矩阵和向量乘法从而服务于基本的图形变换。值得庆幸的是,这其中的数学表达与深度⽹络中的卷积层的表达类似。通⽤GPU这个概念在2001年开始兴起,涌现出诸如OpenCL和CUDA之类的编程框架。
AlexNet
2012年,AlexNet横空出世。这个模型的名字来源于论⽂第⼀作者的姓名Alex Krizhevsky [1]。 AlexNet使⽤了8层卷积神经⽹络,并以很⼤的优势赢得了ImageNet 2012图像识别挑战赛。它⾸次证明了学习到的特征可以超越⼿⼯设计的特征,从而⼀举打破计算机视觉研究的前状。
AlexNet与LeNet的设计理念⾮常相似,但也有显著的区别。
第⼀,与相对较小的LeNet相⽐,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。下⾯我们来详细描述这些层的设计。AlexNet第⼀层中的卷积窗口形状是11× 11。因为ImageNet中绝⼤多数图像的⾼和宽均⽐MNIST图像的⾼和宽⼤10倍以上,ImageNet图像的物体占⽤更多的像素,所以需要更⼤的卷积窗口来捕获物体。第⼆层中的卷积窗口形状减小到5 × 5,之后全采⽤3 × 3。此外,第⼀、第⼆和第五个卷积层之后都使⽤了窗口形状为3 × 3、步幅为2的最⼤池化层。而且,AlexNet使⽤的卷积通道数也⼤于LeNet中的卷积通道数数⼗倍。紧接着最后⼀个卷积层的是两个输出个数为4096的全连接层。这两个巨⼤的全连接层带来将近1GB的模型参数。由于早期显存的限制,最早的AlexNet使⽤双数据流的设计使⼀个GPU只需要处理⼀半模型。幸运的是,显存在过去⼏年得到了⻓⾜的发展,因此通常我们不再需要这样的特别设计了。
第⼆,AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。⼀⽅⾯,ReLU激活函数的计算更简单,例如它并没有sigmoid激活函数中的求幂运算。另⼀⽅⾯,ReLU激活函数在不同的参数初始化⽅法下使模型更容易训练。这是由于当sigmoid激活函数输出极接近0或1时,这些区域的梯度⼏乎为0,从而造成反向传播⽆法继续更新部分模型参数;而ReLU激活函数在正区间的梯度恒为1。因此,若模型参数初始化不当,sigmoid函数可能在正区间得到⼏乎为0的梯度,从而令模型⽆法得到有效训练。
第三,AlexNet通过丢弃法(参⻅“丢弃法”⼀节)来控制全连接层的模型复杂度。而LeNet并没有使⽤丢弃法。
第四,AlexNet引⼊了⼤量的图像增⼴,如翻转、裁剪和颜⾊变化,从而进⼀步扩⼤数据集来缓解过拟合。我们将在后⾯的“图像增⼴”⼀节详细介绍这种⽅法。
下⾯我们实现稍微简化过的AlexNet
import d2lzh as d2l
from mxnet import gluon, init, nd
from mxnet.gluon import data as gdata, nn
import os
import sys
net = nn.Sequential()
# 使⽤较⼤的11 x 11窗⼝来捕获物体。同时使⽤步幅4来较⼤幅度减⼩输出⾼和宽。这⾥使⽤的输出通
# 道数⽐LeNet中的也要⼤很多
net.add(nn.Conv2D(96, kernel_size=11, strides=4, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2),
# 减⼩卷积窗⼝,使⽤填充为2来使得输⼊与输出的⾼和宽⼀致,且增⼤输出通道数
nn.Conv2D(256, kernel_size=5, padding=2, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2),
# 连续3个卷积层,且使⽤更⼩的卷积窗⼝。除了最后的卷积层外,进⼀步增⼤了输出通道数。
# 前两个卷积层后不使⽤池化层来减⼩输⼊的⾼和宽
nn.Conv2D(384, kernel_size=3, padding=1, activation='relu'),
nn.Conv2D(384, kernel_size=3, padding=1, activation='relu'),
nn.Conv2D(256, kernel_size=3, padding=1, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2),
# 这⾥全连接层的输出个数⽐LeNet中的⼤数倍。使⽤丢弃层来缓解过拟合
nn.Dense(4096, activation="relu"), nn.Dropout(0.5),
nn.Dense(4096, activation="relu"), nn.Dropout(0.5),
# 输出层。由于这⾥使⽤Fashion-MNIST,所以⽤类别数为10,⽽⾮论⽂中的1000
nn.Dense(10))
X = nd.random.uniform(shape=(1, 1, 224, 224))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
conv0 output shape:
(1, 96, 54, 54)
pool0 output shape:
(1, 96, 26, 26)
conv1 output shape:
(1, 256, 26, 26)
pool1 output shape:
(1, 256, 12, 12)
conv2 output shape:
(1, 384, 12, 12)
conv3 output shape:
(1, 384, 12, 12)
conv4 output shape:
(1, 256, 12, 12)
pool2 output shape:
(1, 256, 5, 5)
dense0 output shape:
(1, 4096)
dropout0 output shape: (1, 4096)
dense1 output shape:
(1, 4096)
dropout1 output shape: (1, 4096)
dense2 output shape:
(1, 10)