秃姐学AI系列之:GoogLeNet + 代码实现

2024-08-26 22:36

本文主要是介绍秃姐学AI系列之:GoogLeNet + 代码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

GoogLeNet —— 含并行连结的网络

卷积层超参数

Inception块:小学生才做选择,我全要!

为什么要用Inception块?

GoogLeNet架构

详细展开每个Stage

Stage 1 & 2

Stage 3

Stage 4 & 5

Inception 有各种后续变种

总结

 代码实现

Inception Block

逐步实现每个Stage


GoogLeNet —— 含并行连结的网络

L大写用来致敬LeNet,但其实它跟LeNet什么关系都没有哈哈哈。

Just公司名字放上去然后玩个梗致敬以下。

我们上篇提到的NiN严重影响了这个网络的设计,所以还没看上篇博客的可以先去学习一下NiN的思想。指路:秃姐学AI系列之:NiN + 代码实现-CSDN博客 

卷积层超参数

这篇论文的一个重点是解决了什么样大小的卷积核最合适的问题。 毕竟,以前流行的网络使用小到1×1,大到11×11的卷积核。

卷积层:1x1?3x3?5x5?

MaxPooling?MeanPooling?

MLP:1x1?

本文的一个观点是,有时使用不同大小的卷积核组合是有利的。

本篇博客介绍的是一个稍微简化的GoogLeNet版本:省略了一些为稳定训练而添加的特殊特性,现在有了更好的训练方法,这些特性不是必要的。

Inception块:小学生才做选择,我全要!

这很可能得名于电影《盗梦空间》(Inception),因为电影中的一句话“我们需要走得更深”(“We need to go deeper”)

四个路径(Copy)从不同层面抽取信息,然后在输出通道维合并

四条路分别使用不同窗口大小的卷积层或池化层来抽取特征

每个层都会通过padding来保持输入输出同高宽

最后在输出的时候,沿着通道数的方向将四个通道的所有特征叠(concat)在一起。

  • 即高宽不变,变得只有通道数

中间的两条路径在输入上执行1×1卷积,为了减少通道数,从而降低模型的复杂性。 

我们以输入数据:192 * 28 * 28 来观察在模型中通道数是怎么变化的

5x5 卷积更贵,所以我们可以发现 5x5 之前那个卷积给参数压缩到一个更小的状态

我们可以发现:上图中,白色的Conv基本是用来改变通道数的,蓝色的层用来抽取特征信息(1x1用来抽取通道信息,3x3、5x5、最大池化用用来抽取空间信息) 

可能有同学会有疑问,这个数值是哪里来的?或者是怎么确定的?其实作者也没说怎么来的,但是我们通道数相加可以发现,最后输出通道数为256。

或者可以理解成,我们需要把更多数的通道数留给效果更好的路:

  • 比如上面的 3x3 一条路拥有一半的通道数,计算量也不大,效果也好;
  • 剩下的一般通道里面,又分了一半给 1x1 的卷积,不看空间卷积,只看通道卷积;
  • 剩下的1/4就两条通道对半分了

这个思路可以借鉴,但是其实具体是用什么数值这个东西确实是调出来的hahahaha

为什么要用Inception块?

跟单个 3x3 或 5x5 卷积层比,Inception块有更少的参数个数和计算复杂度

所以通过上表我们可以看出。Inception块不但可以让我们在更多维度抽取特征,还让我们计算量减小了很多

GoogLeNet架构

5段,9个Inception块

  • Stage1、2都没有用到Inception块,我们可以看到Stage 2添加的是我们刚刚上面分析的,作者他们可能认为最重要的中间那一条路,也就是 3x3 那条卷积。

这里的Stage定义是:只要把 高宽减半、通道数两倍 就合起来称为一个stage  

  • Stage3 用 2 个Inception块(不改变高宽,只改变通道数)+ 3x3的最大池化(stride = 2)来降低高宽。
  • Stage4 更厉害了,用了 5 个Inception块 + MaxPooling。
  • Stage5 使用 2 个Inception块 + 全局的平均池化
  • 最后通过一个全连接层来映射到你需要的分类数。

所以我们开头为什么说 GoogLeNet 借鉴了 NiN 的思路?

我们可以看到 GoogLeNet 用了大量的 1x1 Conv,其实也是拿它当MLP在使用

详细展开每个Stage

Stage 1 & 2

更小的宽口,更多的通道

  • 从AlexNet的第一层 11x11Conv变成 7x7
  • Stage2也从 5x5Conv 变成 3x3
  • AlexNet 高宽 降得是比较狠的,GoogLeNet没有降得这么狠,还是用padding缓冲了以下,缓慢下降

虽然 高宽 降得没有AlexNet那么块,但是GoogLeNet也是通过前两个 Stage 很快的把高宽从 224x224 降到了 28x28,然后把通道数拉上去,为后面的计算提供可控性(减少计算量)

Stage 3

两个Inception Block

  • 整个数据在穿过两个Inception Block的特征通道数变化是:
    • 192 -> 256 -> 480
  • 第一个Inception Block我们上面已经讲过了,四条线的通道数分配分别是:
    • 1/4、1/2、1/8、1/8
  •  第二个Inception Block我们发现 虽然第二天线 3x3Conv的通道数增加了,但是其实比例没有拿到一半,反而 5x5Conv扩大到原来的近三倍的样子,1x1Conv 和 MaxPool 都是扩大到原来的两倍的样子

完全没有规律可言......同一条路没有,不同路之间也没太有....基本GOOGLE就是机器多,然后用机器做了个架构搜索hahaha 

所以导致 GoogLeNet 这个论文很难复现,有那么多超参数

Stage 4 & 5

 Stage 4 的 5 个 Inception Block 只有 开头 和 结尾的 Block 进行了通道数的增加

最后输出一个长为 1024 的向量

和 VGG 很像,只不过 Block 用的是 Inception,最后输出的 1024 维度也比 VGG的 512 大很多

Inception 有各种后续变种

我们介绍的是v1,v1这个版本几乎没被人使用过,现在使用较多的是v3

  • Inception-BN(v2)——使用 batch normalization
  • Inception-v3——修改了 Inception 块,尝试了很多不同的思想:
    • 替换 5x5 为多个 3x3 卷积层

    • 替换 5x5 为 1x7 和 7x1 卷积层
      • 先在行上面看看空间信息,而不看列
      • 再在列上面看看空间信息,而不看行

    • 替换 3x3 为 1x3 和 3x1 卷积层
      • 前面讲的都是串行的,这个常识直接改成了并行

    • 更深

虽然 v3 这个东西很诡异,但实际上效果还挺好的hahaha(当然是在当年)。但是v3比较耗内存!耗内存多,计算慢,但在当年还是精度挺好的一个网络

  • Inception-v4——使用残差连接

总结

  • Inception 块相当于一个有4条路径的子网络。它通过不同窗口形状的卷积层和最大汇聚层来并行抽取不同信息,并使用1×1卷积层减少每像素级别上的通道维数从而降低模型复杂度。

    • 主要优点:模型参数小、计算复杂度低

  • GoogLeNet使用了 9 个 Inception 块与其他层(卷积层、全连接层)串联起来。其中 Inception 块的通道数分配之比是在 ImageNet 数据集上通过大量的实验得来的。

    • 是第一个达到上百层的网络

当然 GoogLeNet 也不是纯欻以下升到 100 层,升一百直到 ResNet 出现才可以,在 GoogLeNet 还是要加并行那些层才可以达到上百层。

  • GoogLeNet和它的后继者们一度是ImageNet上最有效的模型之一:它以较低的计算复杂度提供了类似的测试精度。

但是GoogLeNet其实不咋受欢迎,因为网络太奇怪啦。乱七八糟的超参数也不知道怎么来的。

 代码实现

Inception Block

import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2lclass Inception(nn.Module):# c1--c4是每条路径的输出通道数# **kwargs:将除了前面显式列出的参数外的其他参数,以dict结构进行接受def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):super(Inception, self).__init__(**kwargs)# 线路1,单1x1卷积层self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)# 线路2,1x1卷积层后接3x3卷积层self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)# 线路3,1x1卷积层后接5x5卷积层self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)# 线路4,3x3最大汇聚层后接1x1卷积层self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)self.p4_2 = nn.Conv2d(in_channels, 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)

逐步实现每个Stage

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.ReLU(),nn.Conv2d(64, 192, kernel_size=3, padding=1),nn.ReLU(),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),nn.AdaptiveAvgPool2d((1,1)),nn.Flatten())net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))

对着网络架构图抄就行了 

有一个思路可以注意一下:虽然每个Stage里面都乱七八糟的,但是每个 Stage 都用一个 Sequential 包住,打印网络看起来其实是很清晰的:

这里数据维度就不展示了,因为为了适应我的数据集我把网络维度改了

这篇关于秃姐学AI系列之:GoogLeNet + 代码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1109874

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景