秃姐学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

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja