现代卷积神经网络(GoogleNet),并使用GoogleNet进行实战CIFAR10分类

news2024/11/25 13:32:12

专栏:神经网络复现目录


本章介绍的是现代神经网络的结构和复现,包括深度卷积神经网络(AlexNet),VGG,NiN,GoogleNet,残差网络(ResNet),稠密连接网络(DenseNet)。
文章部分文字和代码来自《动手学深度学习》

文章目录

  • 含并行连结的网络(GoogLeNet)
  • Inception块
    • 定义和结构
    • 实现
  • GoogLeNet模型
    • 结构
    • 实现
  • 实战
    • 导包
    • 数据集
    • 优化器,损失函数
    • 训练和评估


含并行连结的网络(GoogLeNet)

GoogleNet,也叫Inception v1,是Google在2014年提出的深度卷积神经网络,它的主要特点是使用了Inception模块来替代原来的单一卷积层,以更好地提取特征。GoogleNet是在ImageNet数据集上训练出来的,它在当年的ImageNet比赛中获得了最好的结果。

GoogleNet的整体架构相对于之前的模型更为复杂,具有22层。其中,它的最大特点是使用了Inception模块,使得模型的参数量比之前的模型更小,但是精度更高。

Inception模块主要是将不同卷积核大小的卷积层和最大池化层进行组合,以获得不同大小的感受野,从而更好地提取特征。同时,Inception模块使用了1x1的卷积层进行通道数的调整,进一步减少了参数量。

除了Inception模块之外,GoogleNet还采用了全局平均池化层来代替全连接层,这也是后续很多模型都采用的做法,可以减少过拟合,同时减少参数量。

Inception块

定义和结构

Inception模块是GoogleNet中的一个核心组成部分,用于提取图像特征。该模块采用并行的多个卷积层和池化层来提取不同尺度的特征,然后将它们在通道维度上进行拼接,得到更丰富的特征表达。

一个基本的Inception模块包含了四个分支(Branch),每个分支都有不同的卷积核或者池化核,如下图所示:
在这里插入图片描述
在这个模块中,我们可以看到分支1、2、3都是卷积层,分支4则是最大池化层,其目的是提取图像中不同大小的特征。通过这些分支的组合,Inception模块可以有效地提取图像的多尺度特征,且计算代价相对较小。

具体来说,假设输入的特征图的大小是 h × w × c h\times w\times c h×w×c,Inception模块将其输入到四个分支中,这四个分支分别是:

分支1:1×1卷积层,用于对通道数进行降维,可以看做是对输入的特征进行了线性变换,从而提取空间信息;

分支2:1×1卷积层后接3×3卷积层,先通过1×1卷积层将通道数降维,然后再通过3×3卷积层提取特征。在这个分支中,1×1卷积层可以用来降低计算量,3×3卷积层用于提取空间信息,组合起来可以在计算效率和特征表达能力之间取得平衡;

分支3:1×1卷积层后接5×5卷积层,和分支2类似,该分支也是先通过1×1卷积层将通道数降维,然后再通过5×5卷积层提取特征。与3×3卷积核相比,5×5卷积核可以捕捉更大的空间范围,从而更好地提取空间信息;

分支4:3×3最大池化层后接1×1卷积层,通过池化层可以提取图像中不同大小的特征,然后使用1×1卷积层进行通道数降维。

最后,将四个分支的输出在通道维度上进行拼接,得到的结果就是Inception模块的输出。

实现

import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l


class Inception(nn.Module):
    # c1--c4是每条路径的输出通道数
    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)

GoogLeNet模型

GoogLeNet一共使用9个Inception块和全局平均汇聚层的堆叠来生成其估计值。Inception块之间的最大汇聚层可降低维度。 第一个模块类似于AlexNet和LeNet,Inception块的组合从VGG继承,全局平均汇聚层避免了在最后使用全连接层。
在这里插入图片描述

结构

  1. 卷积层,输入为 224 × 224 224\times224 224×224的彩色图像,使用 7 × 7 7\times7 7×7的卷积核,步幅为2,输出通道数为64,偏置和ReLU激活函数。
  2. 最大池化层,使用 3 × 3 3\times3 3×3的卷积核,步幅为2,输出尺寸为 112 × 112 112\times112 112×112
  3. 卷积层,输入通道数为64,使用 1 × 1 1\times1 1×1的卷积核,输出通道数为64,偏置和ReLU激活函数。
  4. 卷积层,输入通道数为64,使用 3 × 3 3\times3 3×3的卷积核,输出通道数为192,步幅为1,偏置和ReLU激活函数。
  5. 最大池化层,使用 3 × 3 3\times3 3×3的卷积核,步幅为2,输出尺寸为 56 × 56 56\times56 56×56
  6. 使用两个Inception模块,输出通道数为256和480,分别将它们堆叠在一起。
  7. 最大池化层,使用 3 × 3 3\times3 3×3的卷积核,步幅为2,输出尺寸为 28 × 28 28\times28 28×28
  8. 使用五个Inception模块,输出通道数分别为 512 , 512 , 512 , 528 512, 512, 512, 528 512,512,512,528 832 832 832,分别将它们堆叠在一起。
  9. 最大池化层,使用 3 × 3 3\times3 3×3的卷积核,步幅为2,输出尺寸为 14 × 14 14\times14 14×14
  10. 使用两个Inception模块,输出通道数分别为 832 832 832 1024 1024 1024,分别将它们堆叠在一起。
  11. 全局平均池化层,使用一个 7 × 7 7\times7 7×7的池化窗口,输出大小为 1 × 1 1\times1 1×1
  12. Dropout层,随机将一定比例的元素归零,防止过拟合。
  13. 全连接层,输出为10,对应10个类别。

实现

class GoogLeNet(nn.Module):
    def __init__(self, in_channels=3, num_classes=1000):
        super(GoogLeNet, self).__init__()

        # 第一阶段
        self.stage1 = nn.Sequential(
            nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )

        # 第二阶段
        self.stage2 = nn.Sequential(
            nn.Conv2d(64, 64, kernel_size=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 192, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )

        # 第三阶段
        self.stage3 = nn.Sequential(
            Inception(in_channels=192, c1=64, c2=(96, 128), c3=(16, 32), c4=32),#64+128+32+32=256
            Inception(256, 128, (128, 192), (32, 96), 64),#128+192+96+64=480
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )

        # 第四阶段
        self.stage4 = 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)
        )

        # 第五阶段
        self.stage5 = nn.Sequential(
            Inception(832, 256, (160, 320), (32, 128), 128),
            Inception(832, 384, (192, 384), (48, 128), 128),
            nn.AdaptiveAvgPool2d((1, 1)),
            nn.Flatten()
        )

        # 全连接层
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        x = self.stage1(x)
        x = self.stage2(x)
        x = self.stage3(x)
        x = self.stage4(x)
        x = self.stage5(x)
        x = self.fc(x)
        return x

实战

导包

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
torch.manual_seed(1234)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

数据集

transform_train = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

transform_test = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=32,
                                         shuffle=False, num_workers=2)

优化器,损失函数

net = GoogLeNet(num_classes=10).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

训练和评估

import time
def evaluate_accuracy(data_iter, net, device):
    net.eval()  # 评估模式
    acc_sum, n = 0.0, 0
    with torch.no_grad():
        for X, y in data_iter:
            X, y = X.to(device), y.to(device)
            acc_sum += (net(X).argmax(dim=1) == y).float().sum().cpu().item()
            n += y.shape[0]
    net.train()  # 改回训练模式
    return acc_sum / n

def train(net, train_iter, test_iter, loss, optimizer, device, epochs):
    net = net.to(device)
    print("training on ", device)
    batch_count = 0
    for epoch in range(epochs):
        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        for X, y in train_iter:
            X, y = X.to(device), y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        test_acc = evaluate_accuracy(test_iter, net, device)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec' % (
            epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))
train(net,trainloader,testloader,criterion,optimizer,device,10)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/393796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Hello Linux】进程控制 (内含思维导图)

作者:小萌新 专栏:Linux 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:简单介绍下进程的控制 包括进程启动 进程终止 进程等待 进程替换等概念 进程控制介绍进程创建fork函数fork函数的返回值fork函数的使用…

利用Cookie劫持+HTML注入进行钓鱼攻击

目录 HTML注入和cookie劫持: 发现漏洞 实际利用 来源 HTML注入和cookie劫持: HTML注入漏洞一般是由于在用户能够控制的输入点上,由于缺乏安全过滤,导致攻击者能将任意HTML代码注入网页。此类漏洞可能会引起许多后续攻击&#…

高德地图绘制图层

效果图: //初始数据 data(){return{//地图map:{address:,map:null,//当前鼠标绘制mouseTool:null,//当前编辑polyEditor:null,//覆盖物的绘制点【用于编辑】mouseToolArr:[],//覆盖物的poly对象polyArr:[],//地图右侧功能按钮signNumber:0,//保存提交的覆盖物的点数…

win10添加右键菜单打开VSCode

当进入一个文件夹后,想右键直接打开我的工程,用发现没有vscode项。 我了方便,把 VSCode添加到右键菜单吧。 1. 在桌面新建一个txt文档,用文本编辑器打开 2. 查看vscode所在位置 在桌面找到vscode快捷键图标,右键--&g…

2023年考PMP真的有用吗?含备考资料分享~

对于项目管理者来说,是真的有用,前提是你真的学进去了,是为了学习而考,而不是为了考证而考,考试的作用不是为了让你得到证书,而是考校你的水平,知识是知识,经验是经验,缺…

【浅学Java】MySQL索引七连炮

MySQL索引面试七连炮0. 谈一下你对索引的理解1. MySQL索引原理和数据结构能介绍一下吗2. B树和B树的区别3. MySQL聚簇索引和非聚簇索引的区别4. 使用MySQL索引都有什么原则4.1 回表4.2 索引覆盖4.3 最左匹配4.4 索引下推5. 不同的存储引擎是如何进行数据的存储的6. MySQL组合索…

根据文件内容批量更改文件名称

注意的问题: ★★★待处理的文件顺序要与excel中新的文件名称顺序一致,我直接复制文件地址到excel中顺序与原来顺序不一样,也不能通过排序得到原来的顺序,这里给出一种解决办法,具体步骤见2数据预处理阶段。 1. 这是我…

最新版本OpenAI怎么调试--用Postman调试最新版OpenAI-API

动动小手指,去到openai的API介绍页面。 https://platform.openai.com/docs/api-reference/models 通过官网的提示,可以copy对应的调试命令进行测试。 本文主要通过curl命令实现。 打开Postman,对,就是那个测试接口用的postman ​…

浙江首场千人大会现场爆满!实在智能九哥专题演讲:企业数字化转型,从实在RPA开始!

为帮助众多电商商家探索数字时代下新赛道、新趋势、新方向,制定有目标、有节奏的全年生意规划,“未来电商高峰论坛暨电商生态赋能大会”于3月4日在杭州正式拉开序幕。本次大会旨在向品牌电商企业主、运营操盘手分享数字电商时代的黄金趋势及运营策略&…

【C++】30h速成C++从入门到精通(继承)

继承的概念及定义继承的概念继承(inheritance)机制是面向对象程序设计使代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序…

数据结构——链表OJ题目讲解(1)

作者:几冬雪来 时间:2023年3月7日 内容:数据结构链表OJ题目讲解 题目来源:力扣和牛客 目录 前言: 刷题: 1.移出链表元素: 2.链表的中间结点: 3. 链表中倒数第k个结点&#xff1…

第六届省赛——8移动距离(总结规律)

题目:X星球居民小区的楼房全是一样的,并且按矩阵样式排列。其楼房的编号为1,2,3...当排满一行时,从下一行相邻的楼往反方向排号。比如:当小区排号宽度为6时,开始情形如下:1 2 3 4 5 612 11 10 9 8 713 14 1…

【论文简述】MVSTER: Epipolar Transformer for EfficientMulti-View Stereo(ECCV 2022)

一、论文简述 1. 第一作者:Xiaofeng Wang 2. 发表年份:2022 3. 发表期刊:ECCV 4. 关键词:MVS、3D重建、Transformer、极线几何 5. 探索动机:融合多视图代价体很关键,现有的方法效率低,引入…

【Git】P2 分支(创建分支,合并分支,分支冲突,分支分类)

分支分支的概念2077 与 分支git - 分支分支语句查看与创建分支切换与删除分支合并分支分支冲突分支分类分支的概念 什么是分支? 2077 与 分支 我最喜欢的游戏就是 赛博朋克2077,美国末日 和 GTA,下图是2077的存档。 存档非常多的原因是因为…

JavaScript 语句、注释和代码块实例集合

文章目录JavaScript 语句、注释和代码块实例集合JavaScript 语句JavaScript 代码块JavaScript 单行注释JavaScript 多行注释使用单行注释来防止执行使用多行注释来防止执行JavaScript 语句、注释和代码块实例集合 JavaScript 语句 源码 <!DOCTYPE html> <html> &…

Springboot 读取模板excel信息内容并发送邮件, 并不是你想想中的那么简单

Springboot 读取模板excel信息内容并发送邮件 背景技术选型搭建过程数据加密隐藏问题暴露背景追溯解决背景 在我们日常开发中, 会遇到这样一种场景, 就是读取表格中的数据, 并将数据以附件的形式通过邮箱发送到表格中的每个人 即: excel 读取 excel 写入 发送邮件(携带附件), 例…

Volsdf Sampling algorithm

l论文作者开发一个算法计算抽样S方程中使用 I(c,v)≈I^S(c,v)∑i1m−1τ^iLiI(\boldsymbol{c}, \boldsymbol{v}) \approx \hat{I}_{\mathcal{S}}(\boldsymbol{c}, \boldsymbol{v})\sum_{i1}^{m-1} \hat{\tau}_{i} L_{i} I(c,v)≈I^S​(c,v)i1∑m−1​τ^i​Li​ 首先是通过利用…

小区业主入户安检小程序开发

小区业主入户安检小程序开发 可针对不同行业自定义安检项目&#xff0c;线下安检&#xff0c;线上留存&#xff08;安检拍照/录像&#xff09;&#xff0c;提高安检人员安检效率 功能特性&#xff0c;为你介绍小区入户安检系统的功能特性。 小区管理;后台可添加需要安检的小区…

LeetCode-96. 不同的二叉搜索树

题目来源 96. 不同的二叉搜索树 递归 1.我们要知道二叉搜索树的性质&#xff0c;对于一个二叉搜索树&#xff0c;其 【左边的节点值 < 中间的节点值 < 右边的节点值】&#xff0c;也就是说&#xff0c;对于一个二叉搜索树&#xff0c;其中序遍历之后形成的数组应该是一…

分布式系统中的补偿机制设计问题

我们知道&#xff0c;应用系统在分布式的情况下&#xff0c;在通信时会有着一个显著的问题&#xff0c;即一个业务流程往往需要组合一组服务&#xff0c;且单单一次通信可能会经过 DNS 服务&#xff0c;网卡、交换机、路由器、负载均衡等设备&#xff0c;而这些服务于设备都不一…