GoogLeNet(Inception v3)论文及代码复现笔记

参考文章:
Inception V3 – 《Rethinking the Inception Architecture for Computer Vision》论文笔记
深度学习第J9周:Inception v3算法实战与解析
经典卷积模型(四)GoogLeNet-Inception(V3)代码解析
PyTorch GitHub官网Inception源码地址:https://github.com/pytorch/vision/blob/main/torchvision/models/inception.py

一、论文阅读笔记

论文:Rethinking the Inception Architecture for Computer Vision

文章继承了BN-Inception(Inception v2)中将5 × 5 卷积分解为两个3 × 3 卷积的思路以降低计算成本(n个5 × 5 卷积比n个3 × 3 卷积计算成本高25 / 9 = 2.78 倍),同时还引入了空间分离卷积,也就是将n × n 卷积给分解成1 × n和n × 1两个卷积,可以大大减少计算量,特别是对于像5 × 5 和7 × 7这样的大卷积核。这两个思路对于轻量级网络的设计有一定的借鉴。

文章中提到了4个通用的网络设计原则:
1、不要在网络的一开始使用过大的filter size,这会导致图像信息的丢失;
2、高维数据的表示更容易在网络内进行局部处理,添加激活函数可以获得更多的分离特征;
3、空间聚合可以通过低维嵌入来完成,其表示能力没有太多或任何损失。(这里讲的就是网络中Inception模块的分成4个branch最后聚合在一起所使用的原则);
4、平衡网络的宽度和深度。

Inception v3的主要特点如下:

  1. 更深的网络结构:Inception v3比之前的Inception网络结构更深,包含了48层卷积层。这使得网络可以提取更多层次的特征,从而在图像识别任务上取得更好的效果。
  2. 使用Factorized Convolutions:Inception v3采用了Factorized Convolutions(分解卷积),将较大的卷积核分解为多个较小的卷积核(例如将5 × 5 卷积分解为两个3 × 3 卷积)。这种方法可以降低网络的参数数量,减少计算复杂度,同时保持良好的性能。
  3. 使用Batch Normalization:Inception v3在每个卷积层之后都添加了Batch Normalization(BN),这有助于网络的收敛和泛化能力。BN可以减少Internal Covariate Shift(内部协变量偏移)现象,加快训练速度,同时提高模型的鲁棒性。
  4. 辅助分类器:Inception v3引入了辅助分类器,可以在网络训练过程中提供额外的梯度信息,帮助网络更好地学习特征。辅助分类器位于网络的某个中间层,其输出会与主分类器的输出进行加权融合,从而得到最终的预测结果。
  5. 基于RMSProp的优化器:Inception v3使用了RMSProp优化器进行训练。相比于传统的随机梯度下降(SGD)方法,RMSProp可以自适应地调整学习率,使得训练过程更加稳定,收敛速度更快。

论文中共用到了三种Inception modules,即传统的Inception(图1),以及使用了非对称分解卷积核的Inception(图3),以及加入了filter expanded的Inception(图4),图中的pool都为 3x3 stride=1的avgpool。
原始的Inception模块,如图(图1):
原始的Inception模型
Inception模块,其中每个 5 × 5 卷积被两个 3 × 3 卷积替换,如图(图2):
在这里插入图片描述
作者将 n×n 的卷积核尺寸分解为 1×n 和 n×1 两个卷积。例如,一个 3×3 的卷积等价于首先执行一个 1×3 的卷积再执行一个 3×1 的卷积。他们还发现这种方法在成本上要比单个 3×3 的卷积降低 33%,这一结构如图(图3):
在这里插入图片描述
在第一张图中,最左侧的5x5卷积可被表示为两个3x3卷积,而3×3卷积又可以被表示为1x3和3x1卷积。
模块中的滤波器组被扩展(即变得更宽而不是更深),以解决表征性瓶颈。如果该模块没有被拓展宽度,而是变得更深,那么维度会过多减少,造成信息损失。如下图所示(图4):
在这里插入图片描述
论文中Inception v3的结构如下:
在这里插入图片描述

二、代码复现

网络主干依旧由Inception、辅助分类器构成,其中Inception有六类(InceptionA~E和InceptionAux)。
下面的代码是PyTorch中的代码,和论文原文中稍有不同,PyTorch中Inception_v3的结构,如下图所示,其中在InceptionC和InceptionD之间有一个InceptionAux:
在这里插入图片描述
Inception A的结构如图所示:
在这里插入图片描述
Inception B的结构如图所示:
在这里插入图片描述
Inception C的结构如图所示:
在这里插入图片描述
Inception D的结构如图所示:
在这里插入图片描述
Inception E的结构如图所示:
在这里插入图片描述
InceptionAux的结构如图所示:
在这里插入图片描述

模型代码如下:

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms,datasets
import torch.nn.functional as F


#BasicConv2d为带BN+relu的卷积。
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)
        self.relu=nn.ReLU(inplace=True)
    def forward(self, x):
        x=self.conv(x)
        x=self.bn(x)
        x=self.relu(x)
        return x


"""下面开始Inception部分的定义,分别是InceptionA——InceptionE。
注意:这是PyTorch中的Inception的结构,和论文原文中略有不同,结构见图片"""

#InceptionA
"""
第一个branch:
经过branch1x1为带有64个1*1的卷积核,所以生成第一张特征图(35, 35, 64);
第二个branch:
首先经过branch5x5_1为带有48个1*1的卷积核,所以第二张特征图(35, 35, 48),
然后经过branch5x5_2为带有64个5*5大小且填充为2的卷积核,特征图大小依旧不变,因此第二张特征图最终为(35, 35, 64);
第三个branch:
首先经过branch3x3dbl_1为带有64个1*1的卷积核,所以第三张特征图(35, 35, 64),
然后经过branch3x3dbl_2为带有96个3*3大小且填充为1的卷积核,特征图大小依旧不变,因此进一步生成第三张特征图(35, 35, 96),
最后经过branch3x3dbl_3为带有96个3*3大小且填充为1的卷积核,特征图大小和通道数不变,因此第三张特征图最终为(35, 35, 96);
第四个branch:
首先经过avg_pool2d,其中池化核3*3,步长为1,填充为1,所以第四张特征图大小不变,通道数不变,第四张特征图为(35, 35, 192),
然后经过branch_pool为带有pool_features个的1*1卷积,因此第四张特征图最终为(35, 35, pool_features);
最后将四张特征图进行拼接,最终得到(35,35,64+64+96+pool_features)的特征图。
"""
class InceptionA(nn.Module):
    def __init__(self,in_channels,pool_features):
        super(InceptionA,self).__init__()
        self.branch1x1=BasicConv2d(in_channels,64,kernel_size=1)

        self.branch5x5_1=BasicConv2d(in_channels,48,kernel_size=1)
        self.branch5x5_2=BasicConv2d(48,64,kernel_size=5,padding=2)

        self.branch3x3dbl_1=BasicConv2d(in_channels,64,kernel_size=1)
        self.branch3x3dbl_2=BasicConv2d(64,96,kernel_size=3,padding=1)
        self.branch3x3dbl_3=BasicConv2d(96,96,kernel_size=3,padding=1)

        self.branch_pool=BasicConv2d(in_channels,pool_features,kernel_size=1)
    def forward(self, x):
        branch1x1=self.branch1x1(x)

        branch5x5=self.branch5x5_1(x)
        branch5x5=self.branch5x5_2(branch5x5)

        branch3x3dbl=self.branch3x3dbl_1(x)
        branch3x3dbl=self.branch3x3dbl_2(branch3x3dbl)
        branch3x3dbl=self.branch3x3dbl_3(branch3x3dbl)

        branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)
        branch_pool=self.branch_pool(branch_pool)

        outputs=[branch1x1,branch5x5,branch3x3dbl,branch_pool]
        """torch.cat()是PyTorch深度学习框架中的一个函数,用于将多个张量沿着指定的维度拼接在一起。
        具体来说,它可以将多个形状相同的张量按照指定的维度进行拼接,返回一个新的张量。"""
        return torch.cat(outputs,1)


#InceptionB
"""
结果得到输入大小减半,通道数+480的特征图。
假如输入为(35, 35, 288)的数据:
第一个branch:
经过branch3x3为带有384个3*3大小且步长2的卷积核,(35-3+2*0)/2+1=17所以生成第一张特征图(17, 17, 384);
第二个branch:
首先经过branch3x3dbl_1为带有64个1*1的卷积核,特征图大小不变,即(35, 35, 64);
然后经过branch3x3dbl_2为带有96个3*3大小填充1的卷积核,特征图大小不变,即(35, 35, 96),
再经过branch3x3dbl_3为带有96个3*3大小步长2的卷积核,(35-3+2*0)/2+1=17,即第二张特征图为(17, 17, 96);
第三个branch:
经过max_pool2d,池化核大小3*3,步长为2,所以是二倍最大值下采样,通道数保持不变,第三张特征图为(17, 17, 288);
最后将三张特征图进行拼接,最终得到(17(即Hin/2),17(即Win/2),384+96+288(Cin)=768)的特征图。
"""
class InceptionB(nn.Module):
    def __init__(self,in_channels):
        super(InceptionB,self).__init__()
        self.brance3x3=BasicConv2d(in_channels,384,kernel_size=3,stride=2)

        self.brance3x3dbl_1=BasicConv2d(in_channels,64,kernel_size=1)
        self.brance3x3dbl_2=BasicConv2d(64,96,kernel_size=3,padding=1)
        self.brance3x3dbl_3=BasicConv2d(96,96,kernel_size=3,stride=2)
    def forward(self, x):
        branch3x3=self.brance3x3(x)

        branch3x3dbl=self.brance3x3dbl_1(x)
        branch3x3dbl=self.brance3x3dbl_2(branch3x3dbl)
        branch3x3dbl=self.brance3x3dbl_3(branch3x3dbl)

        branch_pool=F.max_pool2d(x,kernel_size=3,stride=2)

        outputs=[branch3x3,branch3x3dbl,branch_pool]

        return torch.cat(outputs,1)


#InceptionC
"""
最终得到输入大小不变,通道数为768的特征图。 
假如输入为(17,17, 768)的数据:
第一个branch:
经过branch1x1为带有192个1*1的卷积核,所以生成第一张特征图(17,17, 192);
第二个branch:
首先经过branch7x7_1为带有c7个1*1的卷积核,所以第二张特征图(17,17, c7),
然后经过branch7x7_2为带有c7个1*7大小且填充为0*3的卷积核,特征图大小不变,进一步生成第二张特征图(17,17, c7),
然后经过branch7x7_3为带有192个7*1大小且填充为3*0的卷积核,特征图大小不变,进一步生成第二张特征图(17,17, 192),因此第二张特征图最终为(17,17, 192);
第三个branch:
首先经过branch7x7dbl_1为带有c7个1*1的卷积核,所以第三张特征图(17,17, c7),
然后经过branch7x7dbl_2为带有c7个7*1大小且填充为3*0的卷积核,特征图大小不变,进一步生成第三张特征图(17,17, c7),
然后经过branch7x7dbl_3为带有c7个1*7大小且填充为0*3的卷积核,特征图大小不变,进一步生成第三张特征图(17,17, c7),
然后经过branch7x7dbl_4为带有c7个7*1大小且填充为3*0的卷积核,特征图大小不变,进一步生成第三张特征图(17,17, c7),
然后经过branch7x7dbl_5为带有192个1*7大小且填充为0*3的卷积核,特征图大小不变,因此第二张特征图最终为(17,17, 192);
第四个branch:
首先经过avg_pool2d,其中池化核3*3,步长为1,填充为1,所以第四张特征图大小不变,通道数不变,第四张特征图为(17,17, 768),
然后经过branch_pool为带有192个的1*1卷积,因此第四张特征图最终为(17,17, 192);
最后将四张特征图进行拼接,最终得到(17, 17, 192+192+192+192=768)的特征图。
"""
class InceptionC(nn.Module):
    def __init__(self,in_channels,channels_7x7):  #c7=channels_7x7
        super(InceptionC,self).__init__()
        self.branch1x1=BasicConv2d(in_channels,192,kernel_size=1)

        self.branch7x7_1=BasicConv2d(in_channels,channels_7x7,kernel_size=1)
        self.branch7x7_2=BasicConv2d(channels_7x7,channels_7x7,kernel_size=(1,7),padding=(0,3))
        self.branch7x7_3=BasicConv2d(channels_7x7,192,kernel_size=(7,1),padding=(3,0))

        self.branch7x7dbl_1=BasicConv2d(in_channels,channels_7x7,kernel_size=1)
        self.branch7x7dbl_2=BasicConv2d(channels_7x7,channels_7x7,kernel_size=(7,1),padding=(3,0))
        self.branch7x7dbl_3=BasicConv2d(channels_7x7,channels_7x7,kernel_size=(1,7),padding=(0,3))
        self.branch7x7dbl_4=BasicConv2d(channels_7x7,channels_7x7,kernel_size=(7,1),padding=(3,0))
        self.branch7x7dbl_5=BasicConv2d(channels_7x7,192,kernel_size=(1,7),padding=(0,3))

        self.branch_pool=BasicConv2d(in_channels,192,kernel_size=1)
    def forward(self, x):
        branch1x1=self.branch1x1(x)

        branch7x7=self.branch7x7_1(x)
        branch7x7=self.branch7x7_2(branch7x7)
        branch7x7=self.branch7x7_3(branch7x7)

        branch7x7dbl=self.branch7x7dbl_1(x)
        branch7x7dbl=self.branch7x7dbl_2(branch7x7dbl)
        branch7x7dbl=self.branch7x7dbl_3(branch7x7dbl)
        branch7x7dbl=self.branch7x7dbl_4(branch7x7dbl)
        branch7x7dbl=self.branch7x7dbl_5(branch7x7dbl)

        branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)
        branch_pool=self.branch_pool(branch_pool)

        outputs=[branch1x1,branch7x7,branch7x7dbl,branch_pool]

        return torch.cat(outputs,1)


#InceptionD
"""
得到输入大小减半,通道数+512的特征图。
假如输入为(17, 17, 768)的数据:
第一个branch:
首先经过branch3x3_1为带有192个1*1的卷积核,所以生成第一张特征图(17, 17, 192);
然后经过branch3x3_2为带有320个3*3大小步长为2的卷积核,(17-3+2*0)/2+1=8,最终第一张特征图(8, 8, 320);
第二个branch:
首先经过branch7x7x3_1为带有192个1*1的卷积核,特征图大小不变,即(17, 17, 192);
然后经过branch7x7x3_2为带有192个1*7大小且填充为0*3的卷积核,特征图大小不变,进一步生成第三张特征图(17,17, 192);
再经过branch7x7x3_3为带有192个7*1大小且填充为3*0的卷积核,特征图大小不变,进一步生成第三张特征图(17,17, 192);
最后经过branch7x7x3_4为带有192个3*3大小步长为2的卷积核,最终第一张特征图(8, 8, 192);
第三个branch:
为max_pool2d,池化核大小3*3,步长为2,所以是二倍最大值下采样,通道数保持不变,第三张特征图为(8, 8, 768);
最后将三张特征图进行拼接,最终得到(8(即Hin/2),8(即Win/2),320+192+768(Cin)=1280)的特征图。
"""
class InceptionD(nn.Module):
    def __init__(self,in_channels):
        super(InceptionD,self).__init__()
        self.branch3x3_1=BasicConv2d(in_channels,192,kernel_size=1)
        self.branch3x3_2=BasicConv2d(192,320,kernel_size=3,stride=2)

        self.branch7x7x3_1=BasicConv2d(in_channels,192,kernel_size=1)
        self.branch7x7x3_2=BasicConv2d(192,192,kernel_size=(1,7),padding=(0,3))
        self.branch7x7x3_3=BasicConv2d(192,192,kernel_size=(7,1),padding=(3,0))
        self.branch7x7x3_4=BasicConv2d(192,192,kernel_size=3,stride=2)
    def forward(self, x):
        branch3x3=self.branch3x3_1(x)
        branch3x3=self.branch3x3_2(branch3x3)

        branch7x7x3=self.branch7x7x3_1(x)
        branch7x7x3=self.branch7x7x3_2(branch7x7x3)
        branch7x7x3=self.branch7x7x3_3(branch7x7x3)
        branch7x7x3=self.branch7x7x3_4(branch7x7x3)

        branch_pool=F.max_pool2d(x,kernel_size=3,stride=2)

        outputs=[branch3x3,branch7x7x3,branch_pool]
        return torch.cat(outputs,1)


#InceptionE
"""
最终得到输入大小不变,通道数为2048的特征图。
假如输入为(8,8, 1280)的数据:
第一个branch:
首先经过branch1x1为带有320个1*1的卷积核,所以生成第一张特征图(8, 8, 320);
第二个branch:
首先经过branch3x3_1为带有384个1*1的卷积核,所以第二张特征图(8, 8, 384),
    经过分支branch3x3_2a为带有384个1*3大小且填充为0*1的卷积核,特征图大小不变,进一步生成特征图(8,8, 384),
    经过分支branch3x3_2b为带有192个3*1大小且填充为1*0的卷积核,特征图大小不变,进一步生成特征图(8,8, 384),
因此第二张特征图最终为两个分支拼接(8,8, 384+384=768);
第三个branch:
首先经过branch3x3dbl_1为带有448个1*1的卷积核,所以第三张特征图(8,8, 448),
然后经过branch3x3dbl_2为带有384个3*3大小且填充为1的卷积核,特征图大小不变,进一步生成第三张特征图(8,8, 384),
    经过分支branch3x3dbl_3a为带有384个1*3大小且填充为0*1的卷积核,特征图大小不变,进一步生成特征图(8,8, 384),
    经过分支branch3x3dbl_3b为带有384个3*1大小且填充为1*0的卷积核,特征图大小不变,进一步生成特征图(8,8, 384),
因此第三张特征图最终为两个分支拼接(8,8, 384+384=768);
第四个branch:
首先经过avg_pool2d,其中池化核3*3,步长为1,填充为1,所以第四张特征图大小不变,通道数不变,第四张特征图为(8,8, 1280),
然后经过branch_pool为带有192个的1*1卷积,因此第四张特征图最终为(8,8, 192);
最后将四张特征图进行拼接,最终得到(8, 8, 320+768+768+192=2048)的特征图。
"""
class InceptionE(nn.Module):
    def __init__(self,in_channels):
        super(InceptionE,self).__init__()
        self.branch1x1=BasicConv2d(in_channels,320,kernel_size=1)

        self.branch3x3_1=BasicConv2d(in_channels,384,kernel_size=1)
        self.branch3x3_2a=BasicConv2d(384,384,kernel_size=(1,3),padding=(0,1))
        self.branch3x3_2b=BasicConv2d(384,384,kernel_size=(3,1),padding=(1,0))

        self.branch3x3dbl_1=BasicConv2d(in_channels,448,kernel_size=1)
        self.branch3x3dbl_2=BasicConv2d(448,384,kernel_size=3,padding=1)
        self.branch3x3dbl_3a=BasicConv2d(384,384,kernel_size=(1,3),padding=(0,1))
        self.branch3x3dbl_3b=BasicConv2d(384,384,kernel_size=(3,1),padding=(1,0))

        self.branch_pool=BasicConv2d(in_channels,192,kernel_size=1)
    def forward(self, x):
        branch1x1=self.branch1x1(x)

        branch3x3=self.branch3x3_1(x)
        branch3x3=[
            self.branch3x3_2a(branch3x3),
            self.branch3x3_2b(branch3x3),
            ]
        branch3x3=torch.cat(branch3x3,1)

        branch3x3dbl=self.branch3x3dbl_1(x)
        branch3x3dbl=self.branch3x3dbl_2(branch3x3dbl)
        branch3x3dbl=[
            self.branch3x3dbl_3a(branch3x3dbl),
            self.branch3x3dbl_3b(branch3x3dbl),
        ]
        branch3x3dbl=torch.cat(branch3x3dbl,1)

        branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)
        branch_pool=self.branch_pool(branch_pool)

        outputs=[branch1x1,branch3x3,branch3x3dbl,branch_pool]
        return torch.cat(outputs,1)



#InceptionAux辅助分类器
"""
在中间层使用中间层特征+辅助分类器,以便最终的损失函数加入该正则化项,优化参数,以达到提升模型分类效果的作用。
结构:Pool——>1x1Conv——>5x5Conv——>FC
"""
class InceptionAux(nn.Module):
    def __init__(self,in_channels,num_classes):
        super(InceptionAux,self).__init__()
        self.conv0=BasicConv2d(in_channels,128,kernel_size=1)
        self.conv1=BasicConv2d(128,768,kernel_size=5)
        self.conv1.stddev=0.01  #stddev: 正态分布的标准差。
        self.fc=nn.Linear(768,num_classes)
        self.fc.stddev=0.001
    def forward(self, x):
        # N x 768 x 17 x 17
        x=F.avg_pool2d(x,kernel_size=5,stride=3)
        # N x 768 x 5 x 5
        x=self.conv0(x)
        # N x 128 x 5 x 5
        x=self.conv1(x)
        # N x 768 x 1 x 1
        # Adaptive average pooling
        x=F.adaptive_avg_pool2d(x,(1,1))
        # N x 768 x 1 x 1
        x = torch.flatten(x, 1)
        # 768
        x=self.fc(x)
        # 1000
        return x
"""InceptionA——E以及InceptionAux定义结束,
下面开始写Inception v3"""


#Inception v3
class InceptionV3(nn.Module):
    def __init__(self,num_classes=1000,aux_logits=True,transform_input=False):
        super(InceptionV3,self).__init__()
        self.aux_logits=aux_logits
        self.transform_input=transform_input
        self.Conv2d_1a_3x3=BasicConv2d(3,32,kernel_size=3,stride=2)
        self.Conv2d_2a_3x3=BasicConv2d(32,32,kernel_size=3)
        self.Conv2d_2b_3x3=BasicConv2d(32,64,kernel_size=3,padding=1)

        self.Conv2d_3b_1x1=BasicConv2d(64,80,kernel_size=1)
        self.Conv2d_4a_3x3=BasicConv2d(80,192,kernel_size=3)

        self.Mixed_5b=InceptionA(192,pool_features=32)
        self.Mixed_5c=InceptionA(256,pool_features=64)
        self.Mixed_5d=InceptionA(288,pool_features=64)

        self.Mixed_6a=InceptionB(288)
        self.Mixed_6b=InceptionC(768,channels_7x7=128)
        self.Mixed_6c=InceptionC(768,channels_7x7=160)
        self.Mixed_6d=InceptionC(768,channels_7x7=160)
        self.Mixed_6e=InceptionC(768,channels_7x7=192)

        if aux_logits:
            self.AuxLogits=InceptionAux(768,num_classes)

        self.Mixed_7a=InceptionD(768)
        self.Mixed_7b=InceptionE(1280)
        self.Mixed_7c=InceptionE(2048)

        self.fc=nn.Linear(2048,num_classes)

    def _forward(self, x):
        # N x 299 x 299 x 3
        x=self.Conv2d_1a_3x3(x)
        # N x 149 x 149 x 32
        x=self.Conv2d_2a_3x3(x)
        # N x 147 x 147 x 32
        x=self.Conv2d_2b_3x3(x)
        # N x 147 x 147 x 64
        x=F.max_pool2d(x,kernel_size=3,stride=2)
        # N x 73 x 73 x 64
        x=self.Conv2d_3b_1x1(x)
        # N x 73 x 73 x 80
        x=self.Conv2d_4a_3x3(x)
        # N x 71 x 71 x 192
        x=F.max_pool2d(x,kernel_size=3,stride=2)
        # N x 35 x 35 x 192
        x=self.Mixed_5b(x)
        # N x 35 x 35 x 256
        x=self.Mixed_5c(x)
        # N x 35 x 35 x 288
        x=self.Mixed_5d(x)
        # N x 35 x 35 x 288
        x=self.Mixed_6a(x)
        # N x 17 x 17 x 768
        x=self.Mixed_6b(x)
        # N x 17 x 17 x 768
        x=self.Mixed_6c(x)
        # N x 17 x 17 x 768
        x=self.Mixed_6d(x)
        # N x 17 x 17 x 768
        x=self.Mixed_6e(x)
        # N x 17 x 17 x 768
        if self.training and self.aux_logits:
            aux=self.AuxLogits(x)
        else:
            aux=None
        # N x 17 x 17 x 768
        x=self.Mixed_7a(x)
        # N x 8 x 8 x 1280
        x=self.Mixed_7b(x)
        # N x 8 x 8 x 2048
        x=self.Mixed_7c(x)
        # N x 8 x 8 x 2048
        # Adaptive average pooling
        x = F.adaptive_avg_pool2d(x, (1, 1))
        # N x 1 x 1 x 2048
        x=F.dropout(x,training=self.training)
        # N x 1 x 1 x 2048
        x=torch.flatten(x, 1)
        # N x 2048
        x=self.fc(x)
        # N x 1000 (num_classes)
        return x,aux
    def forward(self, x):
        x,aux=self._forward(x)
        return x,aux



# 统计模型参数量以及其他指标
import torchsummary

device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 调用并将模型转移到GPU中
model = InceptionV3().to(device)

# 显示网络结构
torchsummary.summary(model, (3, 299, 299))
print(model)

#验证网络是否正确
# if __name__ == "__main__":
#     net = InceptionV3()
#     inputs = torch.randn(2,3,299,299)
#     o1,o2 = net(inputs)
#     print(o1.shape)
#     print(o2.shape)



运行结果

(部分截图)
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值