Going Deeper with Convolutions
文章目录
0 摘要
- 提出了Inception(深度卷积神经网络架构),是ILSVRC2014分类和检测的冠军
- 特点:在网络内部提高了计算资源的利用率。
- 通过精细的设计,在保证计算量不变的情况下,增加深度和宽度
- 为了优化质量,架构基于Hebbian原则(生物神经方面)和多尺度处理的直觉
- GoogLeNet,一个22层的深度网络,分类和检测
1 引言
背景
由于深度学习和卷积网络发现,目标分类和检测能力显著提高。而且,不是仅由于更强大的硬件、大数据集和大模型,二十算法、网络架构提高。
没有用到新数据集。
GoogLeNet是AlexNet参数的1/12,精度提高。
- 目标检测领域,最大的收获不是来自越来越深的网络的简单堆叠,而是深度架构和经典计算机视觉的协同作用。
- 移动、嵌入式计算的发展,算法效率尤其是功率和内存使用格外重要。本文的实验可以在现实世界中使用。
本文
关注计算机视觉中的高效深度神经网络架构,“Inception”。
深的含义:
- Inception模块的形式引入新的组织层次
- 网络深度的增加
2 相关工作
- 自LeNet开始,CNN标准结构:堆叠卷积层(正则、池化)—若干全连接层。
- 一系列大小不同的固定Gabor滤波器处理多尺度。本文使用类似策略,重复多次Inception层,直至22层深度模型GoogLeNet。
- Network-in-Network,在网络中添加1x1卷积层增加深度。本文使用1x1卷积目标,降低维度消除计算瓶颈。
- 目标检测中的R-CNN分解成两个子问题:根据颜色纹理等低级信息生成对象位置建议+CNN分类识别。这种两阶段方法利用了具有低级提示的边界框分割的准确性以及最优CNN模型的强大分类能力。 本文在目标检测上采用了类似的流程,但是在两个阶段都进行了改进。
3 动机和高级考虑
提高深度网络性能最直接的方法:增加网络尺寸(深度和宽度)。缺点:
- 大尺寸意味着大量参数,容易过拟合,尤其是标记样例不足时。主要瓶颈
- 计算量增加,可能浪费计算资源
解决方法:引入稀疏性,用稀疏连接代替全连接层,甚至一般的卷积。
如果数据集的概率分布可以由大型的,非常稀疏的深度神经网络表示,则可以通过分析前一层激活的相关统计量并以高度相关的输出对神经元进行聚类,逐层构建最佳网络拓扑。Hebbian准则:fire together, wire together。
但是目前的计算基础架构在对非均匀稀疏数据结构进行数值计算时效率很低。通过使用稳定改进和经过高度调整的数值库,利用底层CPU或GPU硬件的微小细节,可以实现极快的密集矩阵乘法,从而进一步扩大了差距。非均匀稀疏模型需要更精细的引擎和计算架构。
当前大多数面向视觉的机器学习系统仅通过使用卷积就在空间域中利用稀疏性。 但是,卷积被实现为到较早层中补丁的密集连接的集合。卷积网络传统上一直在特征维度上使用随机和稀疏连接表,以打破对称性并改善学习效果,然而趋势回到完全连接,以进一步优化并行计算。 当前用于计算机视觉的最新架构具有统一的结构。 大量的过滤器和更大的批处理大小允许高效使用密集计算。
这就提出了下一个中间步骤是否有希望的问题:如理论所建议的,利用滤波器级稀疏性的体系结构,但通过利用密集矩阵的计算来利用我们当前的硬件。 关于稀疏矩阵计算的大量文献表明,将稀疏矩阵聚类为相对密集的子矩阵往往会为稀疏矩阵乘法提供竞争性能。 认为在不久的将来将类似的方法用于非统一深度学习架构的自动化构建似乎并不为过。
这一部分完全不知道在说些什么,稀疏、密集指的是什么??用密集矩阵的方式实现稀疏连接?
Inception体系结构最初是作为一个案例研究来评估复杂网络拓扑构造算法的假设输出的,该算法试图逼近隐式的视觉网络的稀疏结构,并通过密集的、易获得的组件覆盖假设的结果。谨慎:尽管Inception体系结构已成为计算机视觉的成功之举,但能否将其归因于导致其构建的指导原则仍然值得怀疑。
*4 架构细节
Inception架构的主要思想:如何容易地估算出卷积视觉网络地最佳局部稀疏结构,并用容易获得的组件覆盖它。平移不变性意味着网络由卷积构建块构建。只需要找到最优局部结构并在空间上重复它。
较浅的层小卷积应该更多特征比较分散,较深的层特征比较集中,应该用大卷积。本文使用1x1,3x3和5x5卷积核(非必须,只是为了对齐容易)。这也意味着建议的体系结构是所有这些层的组合,它们的输出拼接乘一个张量,形成下一级的输入。 由于池化操作对于当前卷积网络的成功至关重要,因此建议添加池化路径,如下:
但是存在问题是:尽管此体系结构可能涵盖了最佳稀疏结构,但效率很低,导致在几个阶段内出现了计算爆炸。
第二种架构:降低维度。即使是低维的嵌入也可能包含许多有关较大图像补丁的信息。在的3×3和5×5卷积之前,使用1×1卷积来计算减少量,还有增强非线性能力的作用,如下:
通常,Inception网络是由彼此堆叠的上述类型的模块组成的网络,偶尔具有步长为2的最大池化层,使分辨率减半。仅在较高的层开始使用Inception模块,同时以传统的卷积方式保留较低的层似乎是有益的。
优点:允许显着增加每个阶段的单元数量,而不会在随后的阶段引起失控的计算复杂性膨胀。 这是通过在昂贵的卷积和较大的补丁尺寸之前普遍使用降维来实现的。 此外,该设计遵循实际的直觉,即视觉信息应按不同的比例进行处理,然后进行汇总,以便下一阶段可以同时从不同的比例中提取特征。
计算资源的改进允许在不引起计算困难的情况下,增加阶段的深度与宽度。
5 GoogLeNet
GoogLeNet指的是ICLSVRC2014提交的模型。集成学习的思想,用了7个模型。
- 所有卷积使用ReLU作为激活函数,降维/投影等也是用ReLU做激活
- 输入224x2242RGB颜色通道
- 考虑了计算效率和实用性
- 网络共22层
- 附加线性层(主要是适应其他数据集,微调用)、平均池化层
- dropout
- 添加 辅助分类器 帮助反向传播,解决梯度消失。这些分类器采用较小的卷积网络的形式。 在训练过程中,损失以权重0.3的方式加入到总损失中。 测试时,辅助网络没有用。
- 其他参数:
- 平均池化:步长为3大小5x5。
- 128卷积核的1x1卷积用于降维,后跟ReLU
- 全连接层1024个神经元,ReLU
- dropout0.7概率
- 线性层后1000路softmax
6 训练策略
- 使用分布式机器学习系统使用适度的模型和数据并行性对GoogLeNet网络进行训练。
- 使用基于CPU的实现,但粗略估计表明,可在一周内使用很少的高端GPU进行融合,主要限制是内存使用率。
- 0.9momentum的异步随机梯度下降,固定的学习率(每8个周期降低4%)。
- Polyak平均最终输出
7 ILSVRC2014
分类
使用top-5进行排名。
其他提升性能的技术:
- 7个版本整体预测。 模型仅在抽样策略上不一样
- 裁剪策略。图片分别缩放到四种尺寸:256、288、320、352,取左、中、右(或上、中、下)正方形,再取四角和中心以及缩放到224x224正方形与镜像。共4x3x6x2个裁剪区。(4种尺寸、3个范围、6=(5+1)、镜像翻倍)。再多将不会有帮助。
- 将softmax概率在多个区域和所有单个分类器上取平均,以获得最终预测。
最终第一名
检测
ILSVRC检测任务是在200种可能的类别中的图像中的对象周围生成边界框。 如果检测到的对象与真实类别匹配并且其边界框重叠至少50%,则视为正确。使用mAP报告结果。
8 结论
- 通过随时可用的密集构造块来近似预期的最佳稀疏结构,是改善计算机视觉神经网络的可行方法。 与较浅和较窄的体系结构相比,此方法的主要优点是在计算需求适度增加的情况下可显着提高质量。
- 向稀疏架构过渡通常是可行且有用的想法。
- 未来的工作:以自动方式创建稀疏和更精细的结构,以及将Inception体系结构的见解应用于其他领域。
9 关键代码
class GoogLeNet(nn.Module):
def __init__(self, num_classes=1000, aux_logits=True):
"""
:param num_classes: 类别数目
:param aux_logits: 是否添加辅助分类器
:param transform_input: 是否对数据做变换
:param init_weights: 是否初始化权重
"""
super(GoogLeNet, self).__init__()
self.aux_logits = aux_logits
self.conv1 = BasicConv2d(3, 64, kernel_size=7, stride=2, padding=3)
self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.conv2 = BasicConv2d(64, 64, kernel_size=1)
self.conv3 = BasicConv2d(64, 192, kernel_size=3, padding=1)
self.maxpool2 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception3a = Inception(192, 64, 96, 128, 16, 32, 32)
self.inception3b = Inception(256, 128, 128, 192, 32, 96, 64)
self.maxpool3 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception4a = Inception(480, 192, 96, 208, 16, 48, 64)
self.inception4b = Inception(512, 160, 112, 224, 24, 64, 64)
self.inception4c = Inception(512, 128, 128, 256, 24, 64, 64)
self.inception4d = Inception(512, 112, 144, 288, 32, 64, 64)
self.inception4e = Inception(528, 256, 160, 320, 32, 128, 128)
self.maxpool4 = nn.MaxPool2d(2, stride=2, ceil_mode=True)
self.inception5a = Inception(832, 256, 160, 320, 32, 128, 128)
self.inception5b = Inception(832, 384, 192, 384, 48, 128, 128)
if aux_logits: # 添加辅助分支部分
self.aux1 = InceptionAux(512, num_classes) # 4a(12x12x512)后面
self.aux2 = InceptionAux(528, num_classes) # 4d(12x12x528)后面
# 最后分类部分
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
# 1x1x1024
self.dropout = nn.Dropout(0.2)
self.fc = nn.Linear(1024, num_classes)
def forward(self, x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.maxpool2(x)
x = self.inception3a(x)
x = self.inception3b(x)
x = self.maxpool3(x)
x = self.inception4a(x)
if self.training and self.aux_logits: # 这个self.training属于父类nn.module中定义的属性,默认True,可以不管
aux1 = self.aux1(x)
x = self.inception4b(x)
x = self.inception4c(x)
x = self.inception4d(x)
if self.training and self.aux_logits:
aux2 = self.aux2(x)
x = self.inception4e(x)
x = self.maxpool4(x)
x = self.inception5a(x)
x = self.inception5b(x)
x = self.avgpool(x)
x = x.view(x.size(0), -1)
x = self.dropout(x)
x = self.fc(x)
if self.training and self.aux_logits: # 若有辅助分类器,返回三元组
return _GoogLeNetOuputs(x, aux2, aux1)
return x
class Inception(nn.Module):
def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):
"""
特征映射大小不变
:param in_channels: 输入通道数
:param ch1x1: 1x1分支最终输出通道数
:param ch3x3red: 3x3分支中间通道数
:param ch3x3: 3x3分支最终输出通道数
:param ch5x5red: 5x5分支中间通道数
:param ch5x5: 5x5分支最终输出通道数
:param pool_proj: 池化分支最终通道数
"""
super(Inception, self).__init__()
self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
self.branch2 = nn.Sequential(
BasicConv2d(in_channels, ch3x3red, kernel_size=1),
BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)
)
self.branch3 = nn.Sequential(
BasicConv2d(in_channels, ch5x5red, kernel_size=1),
BasicConv2d(ch5x5red, ch5x5, kernel_size=3, padding=1) # 注意这里和原文有差异,使用的仍然是3x3卷积核
)
self.branch4 = nn.Sequential(
nn.MaxPool2d(kernel_size=3, stride=1, padding=1, ceil_mode=True),
BasicConv2d(in_channels, pool_proj, kernel_size=1)
)
def forward(self, x):
branch1 = self.branch1(x)
branch2 = self.branch2(x)
branch3 = self.branch3(x)
branch4 = self.branch4(x)
outputs = [branch1, branch2, branch3, branch4]
return torch.cat(outputs, 1)
class InceptionAux(nn.Module):
def __init__(self, in_channels, num_classes):
"""
辅助分类器
:param in_channels: 输入通道数
:param num_classes: 类别数目
"""
super(InceptionAux, self).__init__()
self.conv = BasicConv2d(in_channels, 128, kernel_size=1) # 调节通道数
self.fc1 = nn.Linear(2048, 1024)
self.fc2 = nn.Linear(1024, num_classes)
def forward(self, x):
x = F.adaptive_avg_pool2d(x, (4, 4))
x = self.conv(x)
x = x.view(x.size(0), -1)
x = F.relu(self.fc1(x), inplace=True)
x = F.dropout(x, 0.7, training=self.training)
x = self.fc2(x)
return x
class BasicConv2d(nn.Module):
def __init__(self, in_channels, out_channels, **kwargs):
super(BasicConv2d, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)
self.bn = nn.BatchNorm2d(out_channels, eps=0.001)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
return F.relu(x, inplace=True)
10 发展(其他论文)
GoogLeNet Inception V2
《Batch Normalization:Accelerating Deep Network Training by Reducing Internal Covariate Shift》
Inception V2即在InceptionV1的基础上加上了BN层并且将 5 ∗ 5 5*5 5∗5卷积用两个 3 ∗ 3 3*3 3∗3卷积代替。
Inception V3
《Rethinking theInception Architecture for Computer Vision》
Inception V4
《Inception-v4,Inception-ResNet and the Impact of Residual Connections on Learning》
inception v4实际上是把原来的inception加上了resnet的方法,从一个节点能够跳过一些节点直接连入之后的一些节点,并且残差也跟着过去一个。另外就是V4把一个先1x1再3x3那步换成了先3x3再1x1。