经典神经网络 | GoogleNet 论文解析及代码实现

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 经典神经网络 | GoogleNet 论文解析及代码实现

157c14dd29db3565e580133a18c84549.png

论文研究目标


利用赫布理论和多尺度处理直觉设计一种增加深度和宽度的提高内部计算资源利用率的(同时保持了计算预算不变)网络。GoogleNet在ImageNet分类比赛的Top-5错误率降到了6.7%。

创新点


  • 提出Inception模块
  • 使用辅助Loss
  • 全连接层使用简单的平均池化代替

网络主要结构


138a3b3c4fcbfd7718c3122530ac66cd.jpg

图一  网络总体架构

上图为主要包含Inception块+辅助分类器的GoogLeNet结构示意图。

Inception模块


07a80b0b161ebd3a157dad1d465ccf98.jpg

上图为Inception块示意图 (a)为普通的Inception块;(b)为带有1×1卷积的,可以对输入通道降维的Inception块

Inception模块特点


  1. 由Inception基础块组成。
  2. Inception块相当于⼀个有4条线路的⼦⽹络。它通过不同窗口形状的卷积层和最⼤池化层来并⾏抽取信息,并使⽤1×1卷积层减少通道数从而降低模型复杂度。
  3. 可以⾃定义的超参数是每个层的输出通道数,我们以此来控制模型复杂度。

371ac25cc9e41aee3cfd4276d9d52e1c.png

针对同一个输入层,在Inception块中有四条并行的线路,其中前1~3个是1×1卷积层,第4个是一个MaxPooling池化层,这四条线路最后的输出拥有相同的shape和不同的channel通道数。于是,这些输出最后可在channel维度进行合并。例如:28×28×64,28×28×128,28×28×32,28×28×32。通道合并层的shape:28×28×256(64+128+32+32)。

举例分析加入1*1卷积核设计的好处:

假设输入时256个feature map进来,256个feature map输出,假设Inception层只执行3x3的卷积,那么这就需要执行 (256x256) x (3x3) 次乘法(大约589,000次计算操作)。现在Bottleneck layer的思想是先来减少特征的数量,我们首先执行256 -> 64 的1×1卷积,然后在所有Bottleneck layer的分支上对64大小的feature map进行卷积,最后再64 -> 256 1x1卷积。

操作量是:

256×64 × 1×1 = 16,384      64x1x1卷积核对上一层输出卷积计算

64×256 × 3×3= 147456      256x3x3卷积核对1x1卷积输出进行卷积计算

总共约163840,而我们以前有近600,000。减少3倍多的操作。

layer设计


GoogLeNet是作者团队在参加2014大规模视觉挑战赛时送去参加的几种Inception结构的模型之一。该网络设计时考虑了计算效率和实用性,故可以在单个设备上运行推理,对低内存设备比较友好。整个网络使用了9个Inception块,结构排布如表格中所示:

e75f0d3ac974f778069cca2f9569683a.jpg

训练方法


模型训练采用了DistBelief分布式机器学习系统对GoogleNet进行了训练(CPU)。论文表示使用高端GPU,可以在1周内完成模型的训练。训练采用了0.9动量的异步随机梯度下降,固定学习率(每8个迭代学习率降低4%),另外使用各个各个尺寸的图片(数据增强)对于降低过拟合很有用。

总结&实验结果


作者在论文中表示,用现有的dense结构来组合构建出最佳的稀疏结构,是改善计算机视觉神经网络的可行方法。与较浅和较窄的网络结构相比,该方法的优点在于计算量适度增加的情况下显著提高网络效果。在目标检测领域,尽管没有利用上下文和bounding box回归,我们的效果还是很好,进一步表面Inception结构的优越性,未来将在此基础上继续研究更加精细和自动化地方式来创造稀疏结构用以促进各领域的工作。

e1852f18c81e074feff7c9e5e41bb999.png

代码实现如下图的GoogLenet网络


a13c2d65ade5c42e7342d1e598ef0b70.png

构建Inception基本模块

class Inception(nn.Module):
    # c1 - c4为每条线路里的层的输出通道数
    def __init__(self, in_c, c1, c2, c3, c4):
        super(Inception, self).__init__()
        # 线路1,单1 x 1卷积层
        self.p1_1 = nn.Conv2d(in_c, c1, kernel_size=1)
        # 线路2,1 x 1卷积层后接3 x 3卷积层
        self.p2_1 = nn.Conv2d(in_c, c2[0], kernel_size=1)
        self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)
        # 线路3,1 x 1卷积层后接5 x 5卷积层
        self.p3_1 = nn.Conv2d(in_c, c3[0], kernel_size=1)
        self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)
        # 线路4,3 x 3最大池化层后接1 x 1卷积层
        self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
        self.p4_2 = nn.Conv2d(in_c, c4, kernel_size=1)
    def forward(self, x):
        p1 = F.relu(self.p1_1(x))
        p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
        p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
        p4 = F.relu(self.p4_2(self.p4_1(x)))
        return torch.cat((p1, p2, p3, p4), dim=1)  # 在通道维上连结输出
总体实现
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1),
                   nn.Conv2d(64, 192, kernel_size=3, padding=1),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b3 = nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32),
                   Inception(256, 128, (128, 192), (32, 96), 64),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b4 = nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64),
                   Inception(512, 160, (112, 224), (24, 64), 64),
                   Inception(512, 128, (128, 256), (24, 64), 64),
                   Inception(512, 112, (144, 288), (32, 64), 64),
                   Inception(528, 256, (160, 320), (32, 128), 128),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),
                   Inception(832, 384, (192, 384), (48, 128), 128),
                   d2l.GlobalAvgPool2d())
net = nn.Sequential(b1, b2, b3, b4, b5, 
                    d2l.FlattenLayer(), nn.Linear(1024, 10))
net = nn.Sequential(b1, b2, b3, b4, b5, d2l.FlattenLayer(), nn.Linear(1024, 10))
X = torch.rand(1, 1, 96, 96)
for blk in net.children(): 
    X = blk(X)
    print('output shape: ', X.shape)
#batchsize=128
batch_size = 16
# 如出现“out of memory”的报错信息,可减小batch_size或resize
#train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device
相关文章
|
18天前
|
安全 虚拟化
在数字化时代,网络项目的重要性日益凸显。本文从前期准备、方案内容和注意事项三个方面,详细解析了如何撰写一个优质高效的网络项目实施方案,帮助企业和用户实现更好的体验和竞争力
在数字化时代,网络项目的重要性日益凸显。本文从前期准备、方案内容和注意事项三个方面,详细解析了如何撰写一个优质高效的网络项目实施方案,帮助企业和用户实现更好的体验和竞争力。通过具体案例,展示了方案的制定和实施过程,强调了目标明确、技术先进、计划周密、风险可控和预算合理的重要性。
40 5
|
19天前
|
SQL 安全 网络安全
网络安全的护城河:漏洞防御与加密技术的深度解析
【10月更文挑战第37天】在数字时代的浪潮中,网络安全成为守护个人隐私与企业资产的坚固堡垒。本文将深入探讨网络安全的两大核心要素——安全漏洞和加密技术,以及如何通过提升安全意识来强化这道防线。文章旨在揭示网络攻防战的复杂性,并引导读者构建更为稳固的安全体系。
31 1
|
28天前
|
SQL 安全 测试技术
网络安全的盾牌与剑——漏洞防御与加密技术解析
【10月更文挑战第28天】 在数字时代的浪潮中,网络空间安全成为我们不可忽视的战场。本文将深入探讨网络安全的核心问题,包括常见的网络安全漏洞、先进的加密技术以及提升个人和组织的安全意识。通过实际案例分析和代码示例,我们将揭示黑客如何利用漏洞进行攻击,展示如何使用加密技术保护数据,并强调培养网络安全意识的重要性。让我们一同揭开网络安全的神秘面纱,为打造更加坚固的数字防线做好准备。
41 3
|
17天前
RS-485网络中的标准端接与交流电端接应用解析
RS-485,作为一种广泛应用的差分信号传输标准,因其传输距离远、抗干扰能力强、支持多点通讯等优点,在工业自动化、智能建筑、交通运输等领域得到了广泛应用。在构建RS-485网络时,端接技术扮演着至关重要的角色,它直接影响到网络的信号完整性、稳定性和通信质量。
|
2月前
|
存储 安全 网络安全
网络安全的屏障与钥匙:漏洞防御与加密技术深度解析
【10月更文挑战第20天】在数字世界的迷宫中,网络安全是守护我们数据宝藏的坚固盾牌和锋利钥匙。本篇文章将带您穿梭于网络的缝隙之间,揭示那些潜藏的脆弱点—网络安全漏洞,同时探索如何通过现代加密技术加固我们的数字堡垒。从基本概念到实战策略,我们将一同揭开网络安全的神秘面纱,提升您的安全意识,保护个人信息不受侵犯。
51 25
|
1月前
|
边缘计算 自动驾驶 5G
|
25天前
|
SQL 安全 算法
网络安全的屏障与钥匙:漏洞防护与加密技术解析
【10月更文挑战第31天】在数字世界的海洋中,网络安全是航船的坚固屏障,而信息安全则是守护宝藏的金钥匙。本文将深入探讨网络安全的薄弱环节——漏洞,以及如何通过加密技术加固这道屏障。从常见网络漏洞的类型到最新的加密算法,我们不仅提供理论知识,还将分享实用的安全实践技巧,帮助读者构建起一道更加坚不可摧的防线。
28 1
|
1月前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
93 10
|
1月前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
36 1
|
2月前
|
数据中心

推荐镜像

更多