本文正在参加arxiv.org/abs/1409.48…
摘要
提出了一种代号为“Inception”的深度卷积神经网络架构,在2014年ImageNet大规模视觉识别挑战(ILSVRC14)中分类和检测任务中的达到了最好的sota。该体系结构的主要特点是提高了网络内部计算资源的利用率。这是通过一个精心设计的设计来实现的,它允许增加网络的深度和宽度,同时保持计算预算不变。为了优化质量(quality),架构决策基于Hebbian原则和多尺度处理。在我们提交ILSVRC14时使用的一个名为GoogLeNet的网络,是一个22层的深度网络,其性能是在分类和检测的背景下进行评估的。
1 引言
在过去的三年里,主要由于深度学习的进步,更具体地说是卷积网络[10],图像识别和目标检测的性能一直在以惊人的速度进步。一个令人鼓舞的消息是,这些进展不仅仅是更强大的硬件、更大的数据集和更大的模型的结果,而且主要是新想法、算法和改进的网络架构的结果。例如,除了ILSVRC2014竞赛的分类数据集外,没有使用新的数据源。本文提交给ILSVRC2014的GoogleNet网络实际上比Krizhevsky等人[9]两年前的最优架构少了12×倍的参数,同时也更准确得多。目标检测的最大收益并不是仅仅来自使用深度网络或更大的模型,而是来自深度架构和经典计算机视觉的协同作用,比如Girshick等人[6]的R-CNN算法。
另一个值得注意的因素是,随着移动计算和嵌入式计算的持续发展,算法的效率——特别是它们的性能和内存使用——变得越来越重要。值得注意的是,导致本文中提出的深层架构设计的考虑了这个因素,而不是完全只取决于精度上。对于大多数的实验,模型的设计是为了保持15亿的计算预算-在推理时增加,所以这不仅仅是一个纯粹的学术好奇心,是可以在现实世界中使用,即使在大型数据集上也是在一个合理的成本范围内。
2 相关工作
从LeNet-5[10]开始,卷积神经网络(CNN)通常有一个标准的设计结构——堆叠卷积层(可以是对比归一化和最大池),然后是一个或多个全连接层。这种标准设计的变体在图像分类任务中普遍存在,并在MNIST、CIFAR和最显著的ImageNet分类挑战[9,21]上取得了一些好的成绩。对于更大的数据集,如Imagenet,最近的趋势是增加层数[12]和层大小[21,14],并同时使用dropout[7]来解决过拟合的问题。
尽管担心最大池化层会导致准确空间信息的丢失,但与[9]相同的卷积网络架构也已成功地用于定位[9,14]、目标检测[6,14,18,5]和人体姿态估计[19]等任务。受灵长类动物视觉皮层的神经科学模型的启发,Serre等人[15]使用一系列不同大小的固定Gabor过滤器来处理多个尺度,类似于Inception模型。然而,与固定的固定2层深层模型[15]相反,Inception模型中的所有滤波器都是学习而来。此外,Inception层重复多次,在GoogLeNet模型中形成了22层深度模型。
Network in Network是Lin等人[12]提出的一种提高神经网络表征能力的方法。当应用于卷积层时,该方法可以看作是额外的1×1个卷积层,然后通常是校正的线性激活[9]。这使得它能够很容易地集成到当前的CNN管道中。本文在体系结构中大量使用这种方法。然而,在设置中,1×1卷积有双重作用:最重要的是,它们主要被用作降维模块,以消除计算瓶颈,否则这将限制网络的大小。这不仅允许增加深度,还允许增加网络的宽度,而不会造成显著的性能损失。
目前领先的目标检测方法是由Girshick等人[6]提出的具有卷积神经网络的区域(R-CNN)。R-CNN将整个检测问题分解为两个子问题:首先以一种与类别无关的方式对潜在的对象使用诸如颜色和超像素一致性之类的低级线索,然后使用CNN分类器来识别这些位置上的对象类别。这种两阶段的方法利用了具有低层特征分割边界框的准确性,以及最先进的cnn的高度强大的分类能力。我们在检测提交中采用了类似的管道,但在这两个阶段都探索了增强,如用于更高对象边界框召回的多盒[5]预测,以及用于更好地对边界框建议进行分类的集成方法。
3 动机和高层考虑
提高深层神经网络性能的最直接的方法是增加它规模。这包括增加网络的深度-层的数量和宽度-每层的单元数。这是一种训练更高质量模型的简单和安全的方法,特别是考虑到大量标记训练数据的可用性。然而,这个简单的解决方案也有两个主要的缺点。
- **更大的尺寸通常意味着大量的参数,这使得扩大后的网络更容易发生过拟合,特别是在训练集中标记的例子数量有限的情况下。**这可能是一个主要的瓶颈,因为创建高质量的训练集可能是很棘手和昂贵的。特别是如果需要专家来区分细粒度的视觉类别时,如ImageNet(即使是在1000类ILSVRC子集中),如图1所示,存在一些特别相像的图片,但类名不同。
- 网络规模的另一个缺点是计算资源的使用显著增加。例如,在深度视觉网络中,如果两个卷积层相连,它们的滤波器数量的任何均匀增加都会导致计算量的二次方增加。如果增加的容量使用效率较低(例如,如果大多数权重最终接近于零),那么就会浪费大量的计算。由于在实践中,计算预算总是有限的,因此计算资源的有效分配是首选的,即使其主要目标是增加结果的质量。
解决这两个问题的基本方法将是最终从完全连接转换为稀疏连接的架构,甚至在卷积内部。除了模拟生物系统外,由于Arora等人[2]的开创性工作,这也将具有更坚实的理论基础的优势。其主要结果表明,如果数据集的概率分布是由一个大的、非常稀疏的深度神经网络,那么最优网络拓扑可以构造一层通过分析的相关统计的激活最后一层和聚类神经元与高度相关的输出。虽然严格的数学证明需要非常强的条件,但这一说法与众所周知的Hebbian原理产生共鸣——神经元一起激发,一起连接——表明,在实践中,即使在不那么严格的条件下,基本思想也适用。
缺点是,当碰到在非均匀的稀疏数据结构上进行数值计算时,现在的计算架构效率非常低下。即使算术运算的数量减少了100×倍,查找和缓存丢失的开销是仍占据主导地位,以至于切换到稀疏矩阵也不会得到比较好的回报。随着稳定提升和高度调整的数值库的应用,差距仍在进一步扩大,这些数值库允许极其快速的密集矩阵乘法,利用底层CPU或GPU硬件[16,9]的细微细节,进一步扩大了差距。此外,非均匀稀疏模型需要更复杂的工程和计算基础设施。目前大多数面向视觉的机器学习系统只是利用卷积来利用空间领域的稀疏性。然而,卷积被实现为对上一层块的密集连接的集合。自论文[11]以来,ConvNets在特征维度上使用随机的稀疏连接表,为了打破对称性和提高学习能力,为了更好地优化并行计算,趋势重新转向与[9]的全连接。 结构的均匀性和大量的过滤器和更大的批量允许使用高效的密集计算。
这就提出了一个问题:是否有希望实现下一个中间步骤,一种利用额外稀疏性的体系结构,即使是滤波器级,但正如理论所建议的那样,能通过利用密集矩阵上的计算来利用当前的硬件。关于稀疏矩阵计算的大量文献(例如[3])表明,将稀疏矩阵聚类成相对稠密的子矩阵,往往会给稀疏矩阵乘法提供最先进的实际性能。似乎不难想象,在不久的将来,类似的方法将被用于非均匀的深度学习体系结构的自动化构建。
Inception架构最初是作为案例研究,用于评估一个复杂的网络拓扑构造算法的假设输出,该算法试图近似[2]视觉网络隐含的稀疏结构,并通过密集的、可用的组件覆盖假设的结果。尽管这是一项高度推测性的工作,但只有在对拓扑的精确选择进行了两次迭代之后,就已经可以看到对基于[12]的参考体系结构的适度收益。在进一步调整了学习速率、超参数和改进的训练方法后,确定了生成的Inception空间架构作为[6]和[5]的基础网络,在定位和目标检测中特别有用。有趣的是,虽然大多数Inception的架构选择都受到了彻底的质疑和测试,但它们至少在局部是最优的。
但仍需要谨慎:尽管所提议的架构已经在计算机视觉领域取得了成功,但它的质量是否可以归因于导致其构建的指导原则仍然值得怀疑。要确保这一点需要更彻底的分析和验证:例如,如果基于下面描述的原则的自动化工具能够为视觉网络找到类似但更好的拓扑结构。最令人信服的证明是,如果一个自动化系统能够创建网络拓扑,使用相同的算法在其他领域产生类似的收益。至少,Inception架构的最初成功为这个方向的未来工作带来了坚实的动力。
4 架构细节
Inception架构的主要想法是考虑怎样近似卷积视觉网络的最优稀疏结构并用容易获得的密集组件进行覆盖。注意,假设平移不变性意味着网络将由卷积构建块构建。所需要的就是找到最优的局部构造,并在空间上重复它。Arora等人[2]提出了一种逐层的构造,其中应该分析最后一层的相关性统计数据,并将其聚类为具有高相关性的单元组。这些集群构成了下一层的单元,并连接到前一层中的单元。假设前一层的每个单元对应于输入图像的某个区域,这些单元被分组为滤波器组。在较低的层(靠近输入的层)中,相关的单元将集中在局部区域。这意味着,最终会有许多集群集中在一个区域,可以在下一层被1×1卷积覆盖,正如[12]所建议的那样。然而也可以预期,将会有更少的空间上更分散的集群,可以在更大的补丁上被卷积覆盖,而在越来越大的区域上,补丁的数量将会变少。为了避免补丁对齐问题,当前版本的初始架构限制滤波器的尺寸大小为1×1,3×3和5×5,然而这个决定更多的是基于方便简单而不是必要的。这也意味着,提出的架构是所有这些层的组合,它们的输出滤波器组连接成一个单一的输出向量,形成下一阶段的输入。此外,由于池化操作对于当前卷积网络的成功至关重要,因此它建议在每个这样的阶段添加一个替代的并行池化路径也应该产生额外的有益效果(见图2(a))。
由于这些Inception模块在彼此的顶部堆叠,其输出相关统计必然有变化:由于较高层会捕获较高的抽象特征,其空间集中度预计会减少。这表明随着转移到更高层,3×3和5×5卷积的比例应该会增加。
上述模块的一个大问题是在具有大量滤波器的卷积层之上,在这初始版本中有这个问题(图2(a)),即使适量的5×5卷积计算也可能是非常昂贵的。一旦池化单元添加到混合中,这个问题甚至会变得更明显:输出滤波器的数量等于前一阶段滤波器的数量。池化层输出和卷积层输出的合并会导致这一阶段到下一阶段输出数量不可避免的增加。虽然这种架构可能会覆盖最优稀疏结构,但它会非常低效,导致在几个阶段内计算量爆炸。
这导致了Inception架构的第二个想法:在计算要求会增加太多的地方,明智地减少维度和映射。这是基于embeding的成功:即使是低维嵌入也可能包含大量关于相对较大的图像修补程序的信息。然而,embeding以密集、压缩的形式表示信息,压缩后的信息更难建模。而我们希望在大多数地方保持表示稀疏(根据[2]的要求),并且只有当信号必须聚集在一起时才对它们进行压缩。也就是说,在昂贵的3×3和5×5卷积之前,使用1×1卷积来进行计算降维。除了用来减少(参数)之外,它还包括使用线性修正单元激活,使得它们具有双重用途。最后的结果如图2(b)所示。
一般来说,Inception网络是由上述类型的模块堆叠组成的网络,偶尔有最大池层(步幅为2),使网格分辨率减半。由于技术原因(训练期间的内存效率),只在较高的层开始使用Inception空间模块,而以传统的卷积方式保持较低层的使用是有益的,这并不是绝对必要的,只是反映了我们目前实现中的一些基础结构效率低下。
这种体系结构的一个主要好处方面是,它允许在每个阶段显著增加单元的数量,而不会导致计算复杂性的不受控制的爆炸。这是在尺寸较大的块进行昂贵的卷积之前通过普遍使用降维实现的。这种设计的另一个实际有用的方面是,它与直觉相一致,即视觉息应该在不同的尺度上进行处理,然后进行聚合,以便下一阶段可以同时从不同的尺度上提取特征。
计算资源的使用改善允许增加每个阶段的宽度和阶段的数量,而不会陷入计算困境。可以利用Inception架构创建略差一些但计算成本更低的版本。我们发现,所有可用的控制允许计算资源的受控平衡,导致网络比没有Inception结构的类似执行网络快3—10倍,但是需要仔细的手动设计。
5 GoogLeNet
我们在ILSVRC2014竞赛中选择GoogLeNet作为团队名称,这个名字是对Yann LeCun开拓性的 LeNet5网络[10]的致敬。我们使用GoogleNet来作为提交的竞赛中所使用的Inception架构的特例。还使用了一个更深更广的初始网络,其质量稍差,但将其添加到集合中似乎可以稍微提高效果。**我们忽略了网络的细节,因为实验表明,精确的架构参数的影响相对较小。**在这里,为了演示目的,表1描述了最成功的特定实例(名为GoogLeNet)。在集成的7种模型中,有6种采用了完全相同的拓扑结构(但采用不同的采样方法训练)。
所有的卷积都使用了修正线性激活,包括Inception模块内部的卷积。在网络中感受野是在均值为0的RGB颜色空间中,大小是224×224。“#3×3 reduce”和“#5×5 reduce”表示在3×3和5×5卷积之前,降维层使用的1×1滤波器的数量。在pool proj
列可以看到内置的最大池化之后,投影层中1×1滤波器的数量。所有的这些降维/投影层也都使用了线性修正激活。
该网络的设计考虑到了计算效率和实用性,因此推理可以在单个设备上运行,甚至包括那些计算资源有限的设备,特别是低内存占用的设备。当只计算带有参数的层时,该网络有22层深(如果计算池化层的话,则是27层)。用于建设网络的层(独立构件)约为100。然而,这个数字取决于所使用的机器学习基础设施系统。在分类器之前使用平均池化是基于[12]的,尽管我们的实现的不同之处在于使用了一个额外的线性层。这使得网络可以很容易地调整和微调,但它主要是方便的,我们不期望它有一个重大的影响。我们发现,从全连接层变为平均池化,提高了大约top-1 0.6%的准确率,然而在移除了全连接层之后,Dropout的使用还是必不可少的。
考虑到网络的相对较大的深度,以有效的方式将梯度传播回所有层的能力是一个值得关注的问题。一个有趣的观点是,相对较浅的网络在这项任务上的强大性能表明,网络中间层产生的特性应该是非常有区别的。通过增加与这些中间层相连接的辅助分类器,期望在分类器的较低阶段增强识别,增加传播回来的梯度信号,并提供额外的正则化。这些分类器采用设置在初始(4a)和(4d)模块的输出之上的较小卷积网络的形式。
在训练过程中,它们的损失以折扣权重加到网络的总损失中(辅助分类器的损失加权0.3)。在推理时,这些辅助网络被丢弃。
包括辅助分类器在内的附加网络的具体结构如下:
- 一个滤波器大小5×5,步长为3的平均池化层,导致(4a)阶段的输出为4×4×512,(4d)的输出为4×4×528
- 具有128个滤波器的1×1卷积,用于降维和修正线性激活
- 一个全连接层,具有1024个单元和修正线性激活
- 丢弃70%输出的dropout层
- 使用带有softmax损失的线性层作为分类器(作为主分类器预测同样的1000类,但在推断时移除)
结果网络的示意图如下图所示。
6 训练方法
GoogLeNet网络使用DistBelief[4]分布式机器学习系统进行训练,该系统使用适量的模型和数据并行。尽管仅使用一个基于CPU的实现,但粗略的估计表明GoogLeNet网络可以用更少的高端GPU在一周之内训练到收敛,主要的限制是内存使用。**训练使用异步随机梯度下降,动量参数为0.9[**17],固定的学习率计划(每8个epoch下降4%的学习率)。Polyak平均[13]在推断时用来创建最终的模型。
图像采样方法在过去几个月的竞赛中发生了重大变化,并且已收敛的模型在其他选项上进行了训练,有时还结合着超参数的改变,例如dropout和学习率。因此,很难对训练这些网络的最有效的单一方式给出明确指导。更复杂的是,受[8]的启发,一些模型主要是在相对较小的裁剪图像进行训练,其它模型主要是在相对较大的裁剪图像上进行训练。然而,一个经过验证的方案在竞赛后工作地很好,包括各种尺寸的图像块的采样,它的尺寸均匀分布在图像区域的8%~100%之间,并在3/4和4/3之间随机选择其长宽比的各种大小的图像块进行采样。另外,发现Andrew Howard[8]的光度畸变对于克服训练数据成像条件的过拟合是有用的。此外,还开始使用随机插值方法(双线性、面积、最近邻和立方,概率相等)来调整大小,并结合其他超参数变化**,因此无法确定最终结果是否受到其使用的积极影响**。
7 ILSVRC2014分类挑战设置和结果
ILSVRC2014年的分类挑战涉及到将图像分类为图像集层次结构中的1000个叶节点类别中的一个。大约有120万张图像用于训练,5万张用于验证,10万张图像用于测试。每个图像与一个真实类别相关联,性能测量基于最高得分分类器预测。通常用两个数字表示:top-1准确率,比较第一个预测类与标签是否相似,和top-55错误率,比较前5个预测类中是否存在标签:如果标签在前5类中,那么该图像被认为是正确分类,不管其排名。该挑战使用了top-5的错误率来进行排名。
我们参与了这个挑战,没有使用任何外部数据进行训练。除了本文中提到的训练技术外,在测试过程中还采用了一组技术来获得更高的性能,将在下面详细阐述。
-
独立训练了基于同一个GoogLeNet模型的7个版本(包括一个更广泛的版本),并使用它们进行了集成预测。这些模型使用相同的初始化(即使使用相同的初始权重)和学习率策略进行训练,它们只在采样方法和输入图像的随机顺序上有所不同。
-
在测试过程中,采用了比Krizhevsky等人[9]更激进的裁剪方法。具体来说,将图像的大小调整为4个尺度,其中较短的维度(高度或宽度)分别为256、288、320和352,取这些调整大小的图像的左、中和右的正方形(在肖像图像的情况下,采用顶部,中心和底部方块)。对于每个方块,将采用4个角以及中心224×224裁剪图像以及方块尺寸归一化为224×224,以及它们的镜像版本。这样操作会导致每张图像裁剪出4x3x6x2=1444x3x6x2=144张,Andrew Howard[8]在前一年的记录中使用了类似的方法,经过我们实证验证,其方法略差于我们提出的方案。我们注意到,在实际应用中,这种积极裁剪可能是不必要的,因为存在合理数量的裁剪图像后,更多裁剪图像的带来的收益会变得很微小(正如我们后面展示的那样)。
-
softmax概率在多个裁剪图像上和所有单个分类器上进行平均获得最终预测。在实验中,分析了验证数据的替代方法,例如裁剪图像上的最大池化和分类器的平均,但是它们比简单平均的性能略逊。
在本文的其余部分中,分析了有助于最终提交的整体性能的多个因素。
我们在挑战中的最终提交在验证和测试数据上都获得了6.67%的top-5的错误率,排名第一。与2012年的SuperVision相比降低了56.5%,与前一年的最佳方法(Clarifai)相比相对减少了约40%,这两种方法都使用外部数据来训练分类器。表2显示了一些性能最好的方法的统计数据。
此外,还分析了多个测试选择的性能,通过改变模型的数量和裁剪图像的数量来预测一个图像。结果见表3。当使用一个模型时,选择了验证数据上top-1错误率最低的一个模型。所有数字都是在验证数据集上获得的,以避免与测试数据统计数据过拟合。
8 ILSVRC2014检测挑战设置和结果
ILSVRC检测任务是为了在200个可能的类别中生成图像中目标的边界框。如果检测到的对象匹配的它们实际类别并且边界框重叠至少50%(使用Jaccard索引),则将检测到的对象记为正确。无关的检测记为假阳性且被惩罚。**与分类任务相反,每张图像可能包含多个对象或没有对象,并且它们的尺度可能是变化的。**报告的结果使用平均精度均值(mAP)。
GoogLeNet所采用的检测方法类似于[6]的R-CNN,但用Inception模块作为区域分类器进行了增强。此外,通过结合选择性搜索[20]方法与多盒[5]预测,以提高更高的目标边界框召回率,改进了区域建议步骤。为了减少假阳性的数量,超分辨率的尺寸增加了2倍,这将选择搜索算法的区域生成减少了一半。我们添加了200个来自多盒[5]的区域候选。总的来说,大约60%的区域生成用于[6],同时将覆盖率从92%提高到93%。减少区域生成的数量,增加覆盖率的整体影响是对于单个模型的情况平均精度均值增加了1%。最后,在对每个区域进行分类时,使用了6个ConvNets的集合,将结果的准确率从40%提高到43.9%。注意,与R-CNN相反,由于缺乏时间,没有使用边界框回归。
我们获得了当前最好的检测结果,并显示自第一版检测任务以来的进展。与2013年的结果相比,准确率几乎翻了一番,所有表现好的团队都使用了卷积网络。在表4中报告了官方分数和每个团队的常见策略:外部数据的使用、集成模型或上下文模型。外部数据通常是ILSVRC12分类数据,用于预训练一个模型,然后根据检测数据进行改善。一些团队还提到使用了定位数据,由于大部分定位任务边界框不包含在检测数据集中,因此可以用这些数据预训练通用的边界框回归器,分类方法与预训练相同。GoogLeNet没有使用定位数据进行预训练。
在表5中,仅比较了单个模型的结果。最好性能模型是Deep Insight的,令人惊讶的是3个模型的集合仅提高了0.3个点,而GoogLeNet在模型集成时明显获得了更好的结果。
9 结论
本文的实验结果产生了一个坚实的证据,即通过现成的密集构建块来近似预期的最优稀疏结构是改进计算机视觉神经网络的可行方法。该方法的主要优点是,相比于较浅且较窄的架构,这个方法的主要优势是在计算需求适度增加的情况下有显著的质量收益。
还要注意到,本文提出的网络在检测任务上也具有竞争力,尽管该方法没有利用上下文,也没有执行边界框回归,这进一步显示了Inception架构优势的证据。
对于分类和检测任务,可以预期通过更昂贵的类似深度和宽度的非Inception网络可以实现相似的质量的结果,但本文提出的方法证明了稀疏架构是可行和有用的想法。这表明未来将在[2]的基础上以自动化的方式创建更稀疏和更精细的结构,以及将Inception架构的思考应用到其他领域。
总结
设计思想是用密集模块来近似出局部最优稀疏结构;越靠近前面的层越提取局部信息,越靠近后面越提取大范围信息,所以嵌入到两层之间的Inception模块大的小的感受野都需要有;
使用1×1卷积的理由:受到embedding用低维dense向量代替高维稀疏向量启发,同时希望降维后的低维向量不过于dense,便于计算处理,采用1×1卷积,既可以降维又可以减少计算,且可以提供非线性变换;
同时处理不同尺度的信息,再融合起来;
GAP代替全连接层好处一,便于fine-tune迁移学习,好处二,提升了0.6%的Top-1准确度;浅层特征其实也有了一定的区分度,所以在4a和4b后面添加辅助分类器,计算两个辅助Loss,测试阶段去除辅助分类器。
L=L最后+0.3L辅1+0.3L辅2 L=L_{最后} + 0.3 L_{辅1} + 0.3 L_{辅2}
参考
[1] Know your meme: We need to go deeper. knowyourmeme.com/memes/we-ne…. Accessed: 2014-09-15.
[2] S. Arora, A. Bhaskara, R. Ge, and T. Ma. Provable bounds for learning some deep representations. CoRR, abs/1310.6343, 2013.
[3] U. V. C ̧atalyu ̈rek, C. Aykanat, and B. Uc ̧ar. On two-dimensional sparse matrix partitioning: Models, methods, and a recipe. SIAM J. Sci. Comput., 32(2):656–683, Feb. 2010.
[4] J. Dean, G. Corrado, R. Monga, K. Chen, M. Devin, M. Mao, M. Ranzato, A. Senior, P. Tucker, K. Yang, Q. V. Le, and A. Y. Ng. Large scale distributed deep networks. In P. Bartlett, F. Pereira, C. Burges, L. Bottou, and K. Weinberger, editors, NIPS, pages 1232–1240. 2012.
[5] D. Erhan, C. Szegedy, A. Toshev, and D. Anguelov. Scalable object detection using deep neural networks. In CVPR, 2014.
[6] R. B. Girshick, J. Donahue, T. Darrell, and J. Malik. Rich feature hierarchies for accurate object detection and semantic segmentation. In Computer Vision and Pattern Recognition, 2014. CVPR 2014. IEEE Conference on, 2014.
[7] G. E. Hinton, N. Srivastava, A. Krizhevsky, I. Sutskever, and R. Salakhutdinov. Improving neural networks by preventing co-adaptation of feature detectors. CoRR, abs/1207.0580, 2012.
[8] A. G. Howard. Some improvements on deep convolutional neural network based image classification. CoRR, abs/1312.5402, 2013.
[9] A. Krizhevsky, I. Sutskever, and G. Hinton. Imagenet classification with deep convolutional neural networks. In Advances in Neural Information Processing Systems 25, pages 1106–1114, 2012.
[10] Y. LeCun, B. Boser, J. S. Denker, D. Henderson, R. E. Howard, W. Hubbard, and L. D. Jackel. Backpropagation applied to handwritten zip code recognition. Neural Comput., 1(4):541–551, Dec. 1989.
[11] Y. LeCun, L. Bottou, Y. Bengio, and P. Haffner. Gradient-based learning applied to document recognition. Proceedings of the IEEE, 86(11):2278–2324, 1998.
[12] M. Lin, Q. Chen, and S. Yan. Network in network. CoRR, abs/1312.4400, 2013.
[13] B. T. Polyak and A. B. Juditsky. Acceleration of stochastic approximation by averaging. SIAM J. Control Optim., 30(4):838–855, July 1992.
[14] P. Sermanet, D. Eigen, X. Zhang, M. Mathieu, R. Fergus, and Y. LeCun. Overfeat: Integrated recognition, localization and detection using convolutional networks. CoRR, abs/1312.6229, 2013.
[15] T. Serre, L. Wolf, S. M. Bileschi, M. Riesenhuber, and T. Poggio. Robust object recognition with cortex-like mechanisms. IEEE Trans. Pattern Anal. Mach. Intell., 29(3):411–426, 2007.
[16] F. Song and J. Dongarra. Scaling up matrix computations on shared-memory manycore systems with 1000 cpu cores. In Proceedings of the 28th ACM Interna- tional Conference on Supercomputing, ICS ’14, pages 333–342, New York, NY, USA, 2014. ACM.
[17] I. Sutskever, J. Martens, G. E. Dahl, and G. E. Hinton. On the importance of initialization and momentum in deep learning. In ICML, volume 28 of JMLR Proceed- ings, pages 1139–1147. JMLR.org, 2013.
[18] C.Szegedy,A.Toshev,andD.Erhan.Deep neural networks for object detection. In C. J. C. Burges, L. Bottou, Z. Ghahramani, and K. Q. Weinberger, editors, NIPS, pages 2553–2561, 2013.
[19] A. Toshev and C. Szegedy. Deeppose: Human pose estimation via deep neural networks. CoRR, abs/1312.4659, 2013.
[20] K. E. A. van de Sande, J. R. R. Uijlings, T. Gevers, and A. W. M. Smeulders. Segmentation as selective search for object recognition. In Proceedings of the 2011 International Conference on Computer Vision, ICCV ’11, pages 1879–1886, Washington, DC, USA, 2011. IEEE Computer Society.
[21] M. D. Zeiler and R. Fergus. Visualizing and understanding convolutional networks. In D. J. Fleet, T. Pajdla, B. Schiele, and T. Tuytelaars, editors, ECCV, volume 8689 of Lecture Notes in Computer Science, pages 818–833. Springer, 2014.
实践
基于pytorch1.14 实现Inception, 数据集是cifar-10,Inception的结构参见下表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U5dqHELg-1654169655753)(C:UsersDELL-3020AppDataRoamingTyporatypora-user-imagesimage-20220602193152902.png)]
import os
import sys
import time
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as f
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
# 配置GPU环境
os.environ['CUDA_VISIABLE_DIVICES'] = '0'
# 配置其他超参数
batch_size = 32
num_works = 4
lr = 1e-4
epochs = 20
from torchvision import transforms
image_size = 224
data_transform = transforms.Compose(
[# transforms.ToPILImage(), # 这一步取决于后续的数据读取方式,如果使用内置数据集则不需要
transforms.Resize(image_size), transforms.ToTensor()]
)
from torchvision import datasets
train_data = datasets.CIFAR10(root='./', train=True, download=True, transform=data_transform)
test_data = datasets.CIFAR10(root='./', train=False, download=True, transform=data_transform)
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=num_works, drop_last=True)
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=num_works)
image, label = next(iter(train_loader))
print(image.shape, label.shape)
plt.imshow(image[0][0], cmap='gray')
def ConvBNRelu(in_channels,out_channels,kernel_size):
return nn.Sequential(
nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=kernel_size,
stride=1,padding=kernel_size//2),
nn.BatchNorm2d(out_channels),
nn.ReLU6(inplace=True)
)
# inception 模块单元
class InceptionV1Module(nn.Module):
def __init__(self,in_channels,out_channels1,out_channels2reduce,out_channels2,out_channels3reduce,out_channels3,out_channels4):
super(InceptionV1Module,self).__init__()
self.branch1_conv = ConvBNRelu(in_channels=in_channels,out_channels=out_channels1,kernel_size=1)
self.branch2_conv1 = ConvBNRelu(in_channels=in_channels,out_channels=out_channels2reduce,kernel_size=1)
self.branch2_conv2 = ConvBNRelu(in_channels=out_channels2reduce,out_channels=out_channels2,kernel_size=3)
self.branch3_conv1 = ConvBNRelu(in_channels=in_channels,out_channels=out_channels3reduce,kernel_size=1)
self.branch3_conv2 = ConvBNRelu(in_channels=out_channels3reduce,out_channels=out_channels3,kernel_size=5)
self.branch4_pool = nn.MaxPool2d(kernel_size=3,stride=1,padding=1)
self.branch4_conv1 = ConvBNRelu(in_channels=in_channels,out_channels=out_channels4,kernel_size=1)
def forward(self,x):
out1 = self.branch1_conv(x)
out2 = self.branch2_conv2(self.branch2_conv1(x))
out3 = self.branch3_conv2(self.branch3_conv1(x))
out4 = self.branch4_conv1(self.branch4_pool(x))
# 将tensor拼接在一起,增加深度
out = torch.cat([out1,out2,out3,out4],dim=1)
return out
# 辅助的两条通道
class InceptionAux(nn.Module):
def __init__(self,in_channels,out_channels):
super(InceptionAux,self).__init__()
self.auxiliary_avgpool = nn.AvgPool2d(kernel_size=5,stride=3)
self.auxiliary_conv1 = ConvBNRelu(in_channels=in_channels,out_channels=128,kernel_size=1)
self.auxiliary_linear1 = nn.Linear(in_features=128*4*4,out_features=1024)
self.auxiliary_relu = nn.ReLU6(inplace=True)
self.auxiliary_dropout = nn.Dropout(p=0.7)
self.auxiliary_linear2 = nn.Linear(in_features=1024,out_features=out_channels)
def forward(self,x):
x = self.auxiliary_conv1(self.auxiliary_avgpool(x))
x = x.view(x.size(0),-1)
x = self.auxiliary_relu(self.auxiliary_linear1(x))
out = self.auxiliary_linear2(self.auxiliary_dropout(x))
return out
class InceptionV1(nn.Module):
def __init__(self,num_classes=1000,stage='train'):
super(InceptionV1,self).__init__()
self.stage = stage
self.block1 = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,stride=2,padding=3),
nn.BatchNorm2d(64),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1),
nn.Conv2d(in_channels=64,out_channels=64,kernel_size=1,stride=1),
nn.BatchNorm2d(64)
)
self.block2 = nn.Sequential(
nn.Conv2d(in_channels=64,out_channels=192,kernel_size=3,padding=1,stride=1),
nn.BatchNorm2d(192),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
)
self.block3 = nn.Sequential(
InceptionV1Module(in_channels=192,out_channels1=64,out_channels2reduce=96,out_channels2=128,
out_channels3reduce=16,out_channels3=32,out_channels4=32),
InceptionV1Module(in_channels=256,out_channels1=128,out_channels2reduce=128,out_channels2=192,
out_channels3reduce=32,out_channels3=96,out_channels4=64),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
)
self.block4_1 = InceptionV1Module(in_channels=480,out_channels1=192,out_channels2reduce=96,out_channels2=208,
out_channels3reduce=16,out_channels3=48,out_channels4=64)
if self.stage == 'train':
self.aux_logits1 = InceptionAux(in_channels=512,out_channels=num_classes)
self.block4_2 = nn.Sequential(
InceptionV1Module(in_channels=512,out_channels1=160,out_channels2reduce=112,out_channels2=224,
out_channels3reduce=24,out_channels3=64,out_channels4=64),
InceptionV1Module(in_channels=512,out_channels1=128,out_channels2reduce=128,out_channels2=256,
out_channels3reduce=24,out_channels3=64,out_channels4=64),
InceptionV1Module(in_channels=512,out_channels1=112,out_channels2reduce=144,out_channels2=288,
out_channels3reduce=32,out_channels3=64,out_channels4=64)
)
if self.stage == 'train':
self.aux_logits2 = InceptionAux(in_channels=528,out_channels=num_classes)
self.block4_3 = nn.Sequential(
InceptionV1Module(in_channels=528,out_channels1=256,out_channels2reduce=160,out_channels2=320,
out_channels3reduce=32,out_channels3=128,out_channels4=128),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
)
self.block5 = nn.Sequential(
InceptionV1Module(in_channels=832,out_channels1=256,out_channels2reduce=160,out_channels2=320,
out_channels3reduce=32,out_channels3=128,out_channels4=128),
InceptionV1Module(in_channels=832,out_channels1=384,out_channels2reduce=192,out_channels2=384,
out_channels3reduce=48,out_channels3=128,out_channels4=128)
)
self.avgpool = nn.AvgPool2d(kernel_size=7,stride=1)
self.dropout = nn.Dropout(p=0.4)
self.linear = nn.Linear(in_features=1024,out_features=num_classes)
def forward(self,x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
aux1 = self.block4_1(x)
aux2 = self.block4_2(aux1)
x = self.block4_3(aux2)
out = self.block5(x)
out = self.avgpool(out)
out = self.dropout(out)
out = out.view(out.size(0),-1)
out = self.linear(out)
if self.stage == 'train':
aux1 = self.aux_logits1(aux1)
aux2 = self.aux_logits2(aux2)
return aux1,aux2,out
else:
return out
model = InceptionV1(10)
model = model
print(model)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=lr)
from torch.utils.tensorboard import SummaryWriter
writer1 = SummaryWriter('./runs/loss')
writer2 = SummaryWriter('./runs/acc')
def train(epoch):
model.train()
train_loss = 0
for data, label in train_loader:
# data, label = data.cuda(), label.cuda()
optimizer.zero_grad()
aux1, aux2, out = model(data)
loss_aux1 = criterion(aux1, label)
loss_aux2 = criterion(aux2, label)
loss_out = criterion(out, label)
loss = loss_out + 0.3 * loss_aux1 + 0.3 * loss_aux2
loss.backward()
optimizer.step()
train_loss += loss.item() * data.size(0)
train_loss = train_loss / len(train_loader.dataset)
writer1.add_scalar('loss', train_loss, epoch)
print('Epoch: {} tTraining Loss: {:.6f}'.format(epoch, train_loss))
def val(epoch):
# 设置评估状态
model.eval()
val_loss = 0
gt_labels = []
pred_labels = []
# 不设置梯度
with torch.no_grad():
for data, label in test_loader:
# data, label = data.cuda, label.cuda()
_, _, output = model(data)
preds = torch.argmax(output, 1)
gt_labels.append(label.cpu().data.numpy())
pred_labels.append(preds.cpu().data.numpy())
loss = criterion(output, label)
val_loss += loss.item()*data.size(0)
# 计算验证集的平均损失
val_loss = val_loss /len(test_loader.dataset)
writer1.add_scalar('loss', val_loss, epoch)
gt_labels, pred_labels = np.concatenate(gt_labels), np.concatenate(pred_labels)
# 计算准确率
acc = np.sum(gt_labels ==pred_labels)/len(pred_labels)
writer2.add_scalar('acc', acc, epoch)
print('Epoch: {} tValidation Loss: {:.6f}, Accuracy: {:6f}'.format(epoch, val_loss, acc))
for epoch in range(1, 10):
train(epoch)
val(epoch)
writer1.close()
writer2.close()