【图像分类】【深度学习】【Pytorch版本】 GoogLeNet(InceptionV3)模型算法详解

【图像分类】【深度学习】【Pytorch版本】 GoogLeNet(InceptionV3)模型算法详解

文章目录

  • 【图像分类】【深度学习】【Pytorch版本】 GoogLeNet(InceptionV3)模型算法详解
  • 前言
  • GoogLeNet(InceptionV3)讲解
    • Factorized Convolutions卷积分解
    • InceptionV3结构Ⅰ
    • InceptionV3结构Ⅱ
    • InceptionV3结构Ⅲ
    • InceptionV3特殊结构
    • 辅助分类器
    • GoogLeNet(InceptionV3)模型结构
  • GoogLeNet(InceptionV3) Pytorch代码
  • 完整代码
  • 总结


前言

GoogLeNet(InceptionV3)是由谷歌的Szegedy, Christian等人在《Rethinking the Inception Architecture for Computer Vision【CVPR-2016】》【论文地址】一文中提出的带有Factorized Convolutions(分解卷积)的改进模型,即在InceptionV2的基础上将标准的卷积操作分解为1xN卷积和Nx1卷积两个步骤,这种分解模式可以显著降低参数量和计算量,同时在一定程度上保持了特征提取的能力。


GoogLeNet(InceptionV3)讲解

GoogLeNet团队在给出了一些通用的网络设计准则,以期望在不提高网络参数量的前提下提升网络的表达能力:

  1. 避免特征图(feature map)表达瓶颈:从理论上讲,尺寸(seize)才包含了相关结构等重要因素,维度(channel)仅仅提供了信息内容的粗略估计,因此特征图的尺寸应该从输入到输出慢慢减小,避免使用极端压缩。
  2. 更高的维度特征图更容易获得网络的局部表达:在卷积网络结构中,增加非线性能够使得更多的特征解耦合,相互独立的特征更多,输入的信息就被分解的更彻底,分解的子特征间相关性低,子特征内部相关性高,因此高维特征带有更多的判别信息,会更容易收敛。
  3. 在低维特征上的空间聚合(池化)不会(极少)损失太多信息:相邻的位置的信息具有强相关性,即使进行了降维,也不会带来太多的损失,并且维数的降低,也能够加速网络学习。
  4. 平衡网络的宽度与深度:最优的网络应该在每一层网络宽度和网络深度之间有个很好的平衡。

Factorized Convolutions卷积分解

分解卷积的主要目的是为了减少网络中的参数,主要方法有:大卷积分解成小卷积,小卷积分解为非对称卷积。

大卷积分解成小卷积: 大尺度的卷积可以获得更大的感受野,但是也带来参数量的增加,VggNet表明使用大于大卷积核(大于3×3)完全可以由一系列的3×3卷积核来替代,即使用小卷积核串联来替代大卷积核。因此在InceptionV2中已经通过堆叠两层3×3的卷积核的方式替代一层5×5的卷积核,这样的连接方式在保持感受野范围的同时又减少了参数量,不会造成表达缺失,降低网络性能,并且可以避免表达瓶颈,加深非线性表达能力。
小卷积分解为非对称卷积: 3x3卷积是能够完全获取上下文信息(上、下、左、右)的最小卷积核,是否能把小卷积核分解的更小呢?在InceptionV3中,GoogLeNet团队考虑了非对称卷积分解,引入了将一个较大的二维卷积拆成两个较小的一维卷积的做法,即任意n×n的卷积都可以通过1×n卷积后接n×1卷积来替代,非对称卷积能够降低运算量,并且不会降低模型的整体表征能力。

InceptionV3结构Ⅰ

与InceptionV2结构相同,即5x5卷积使用两个3x3的卷积代替,目的是减少参数量和计算量——大卷积分解成小卷积。

InceptionV3结构Ⅱ

将InceptionV2结构中3x3的卷积使用1x3和3x1的卷积组合来代替,5x5的卷积使用俩个1x3和3x1的卷积组合来代替,目的也是减少参数量和计算量———小卷积分解为非对称卷积。

采用这种分解在模型的早期网络层上不能有效发挥作用,但是在中等特征图大小(m×m,其中m在12和20之间的范围)上取得了非常好的效果。

使用3x3的卷积代替5x5的卷积,输出512通道特征图,输出128通道特征图:
参数量:512×3×3×128+128×3×3×128=737280
计算量:512×3×3×128×W×H+128×3×3×128×W×H=737280×W×H
W×H是特征图尺寸,假设卷积层的输入输出特征图尺寸保持一致
使用1x3和3x1的卷积组合代替5x5的卷积,输出512通道特征图,输出128通道特征图:
参数量:512×1×3×128+128×3×1×128+128×1×3×128+128×3×1×128=344064
计算量:512×1×3×128×W×H+128×3×1×128×W×H+128×1×3×128×W×H+128×3×1×128×W×H=344064×W×H

InceptionV3结构Ⅲ

该结构主要用于扩充通道数,网络变得更宽,该结构被放置在所以放在GoogLeNet(InceptionV3)的最后。

InceptionV3特殊结构

在传统方法中,卷积神经网络使用池化等操作以减小特征图大小。先池化再进行卷积升维会导致瓶颈结构,过程中将丢失很多信息,对于后面输出的特征图提取的图像中的特征将会更少;先卷积升维再进行池化,计算量将增加三倍,增加了计算成本:

GoogLeNet(InceptionV3)的改进方案采用一种并行的降维结构,在扩充通道数的同时下采样减小特征图大小,既减少计算量保证了计算效率又避免了瓶颈结构。
替换GoogLeNet(InceptionV1)模型中的MaxPool。

辅助分类器

GoogLeNet(InceptionV1)引入了辅助分类器的概念,最初的动机是为了将有用的梯度反向传递到网络低层,解决梯度消失的问题,提高网络的收敛能力,保证网络训练正常进行。
GoogLeNet(InceptionV3)的实验则发现1.辅助分类器并不能保证收敛更快,并且有无辅助分类器,训练过程基本保持一致,只有在训练的最后阶段,有辅助分类器略微高于无辅助分类器的网络;2.位于网络低层的辅助分类器对最终结果没有影响;3.辅助分类器充当了正则化器,如果辅助分类器带有BN或Dropout层那么主分类器性能会更好。

GoogLeNet(InceptionV3)中的辅助分类器同样不直接用于最终的预测结果。在训练过程中,辅助分类器的损失函数会被加权,并与主分类器的损失函数相结合。在推理阶段,辅助分类器被舍弃,仅使用主分类器进行预测。

GoogLeNet(InceptionV3)模型结构

下图是原论文给出的关于 GoogLeNet(InceptionV3)模型结构的详细示意图:

GoogLeNet(InceptionV3)在图像分类中分为两部分:backbone部分: 主要由InceptionV3模块、卷积层和池化层(汇聚层)组成,分类器部分: 由主分类器和辅助分类器组成。
博主仿造GoogLeNet(InceptionV1)的结构绘制了以下GoogLeNet(InceptionV3)的结构。


GoogLeNet(InceptionV3) Pytorch代码

卷积层组: 卷积层+BN层+激活函数

# 卷积组: Conv2d+BN+ReLU
class BasicConv2d(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
        super(BasicConv2d, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

InceptionV3模块Ⅰ: 卷积层组+池化层

就是原始的InceptionV2模块

# InceptionV3A:BasicConv2d+MaxPool2d
class InceptionV3A(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3A, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+3×3卷积
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)   # 保证输出大小等于输入大小
        )
        # 1×1卷积++3×3卷积+3×3卷积
        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, padding=1)         # 保证输出大小等于输入大小
        )
        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

InceptionV3模块Ⅱ: 卷积层组+池化层

# InceptionV3B:BasicConv2d+MaxPool2d
class InceptionV3B(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3B, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+1×3卷积+3×1卷积
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=[1, 3], padding=[0, 1]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[3, 1], padding=[1, 0])   # 保证输出大小等于输入大小
        )
        # 1×1卷积+1×3卷积+3×1卷积+1×3卷积+3×1卷积
        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=[1, 3], padding=[0, 1]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[3, 1], padding=[1, 0]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[1, 3], padding=[0, 1]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[3, 1], padding=[1, 0])  # 保证输出大小等于输入大小
        )
        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

InceptionV3模块Ⅲ: 卷积层组+池化层

# InceptionV3C:BasicConv2d+MaxPool2d
class InceptionV3C(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3C, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+1×3卷积+3×1卷积
        self.branch2_0 = BasicConv2d(in_channels, ch3x3red, kernel_size=1)
        self.branch2_1 = BasicConv2d(ch3x3red, ch3x3, kernel_size=[1, 3], padding=[0, 1])
        self.branch2_2 = BasicConv2d(ch3x3red, ch3x3, kernel_size=[3, 1], padding=[1, 0])

        # 1×1卷积+3×3卷积+1×3卷积+3×1卷积
        self.branch3_0 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),
        )
        self.branch3_1 = BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=[1, 3], padding=[0, 1])
        self.branch3_2 = BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=[3, 1], padding=[1, 0])

        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2_0 = self.branch2_0(x)
        branch2 = torch.cat([self.branch2_1(branch2_0), self.branch2_2(branch2_0)], dim=1)
        branch3_0 = self.branch3_0(x)
        branch3 = torch.cat([self.branch3_1(branch3_0), self.branch3_2(branch3_0)], dim=1)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

InceptionV3特殊模块(三分支): 卷积层组+池化层

# InceptionV3D:BasicConv2d+MaxPool2d
class InceptionV3D(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3D, self).__init__()
        # ch1x1:没有1×1卷积
        # 1×1卷积+3×3卷积,步长为2
        self.branch1 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, stride=2)   # 保证输出大小等于输入大小
        )
        # 1×1卷积+3×3卷积+3×3卷积,步长为2
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),   # 保证输出大小等于输入大小
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, stride=2)         # 保证输出大小等于输入大小
        )
        # 3×3池化,步长为2
        self.branch3 = nn.Sequential(nn.MaxPool2d(kernel_size=3, stride=2))
        # pool_proj:池化层后不再接卷积层

    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        # 拼接
        outputs = [branch1,branch2, branch3]
        return torch.cat(outputs, 1)

辅助分类器: 池化层+卷积层组+全连接层+dropout

# 辅助分类器:AvgPool2d+BasicConv2d+Linear+dropout
class InceptionAux(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(InceptionAux, self).__init__()

        self.averagePool = nn.AvgPool2d(kernel_size=5, stride=3)
        self.conv1 = BasicConv2d(in_channels=in_channels, out_channels=128, kernel_size=1)
        self.conv2 = BasicConv2d(in_channels=128, out_channels=768, kernel_size=5, stride=1)
        self.dropout = nn.Dropout(p=0.7)
        self.linear = nn.Linear(in_features=768, out_features=out_channels)
    def forward(self, x):
        # N x 768 x 17 x 17
        x = self.averagePool(x)
        # N x 768 x 5 x 5
        x = self.conv1(x)
        # N x 128 x 5 x 5
        x = self.conv2(x)
        # N x 768 x 1 x 1
        x = x.view(x.size(0), -1)
        # N x 768
        out = self.linear(self.dropout(x))
        # N x num_classes
        return out

完整代码

GoogLeNet(InceptionV3)的输入图像尺寸是299×299

import torch.nn as nn
import torch
from torchsummary import summary

class GoogLeNetV3(nn.Module):
    def __init__(self, num_classes=1000, aux_logits=True, init_weights=False):
        super(GoogLeNetV3, self).__init__()
        self.aux_logits = aux_logits
        # 3个3×3卷积替代7×7卷积
        self.conv1_1 = BasicConv2d(3, 32, kernel_size=3, stride=2)
        self.conv1_2 = BasicConv2d(32, 32, kernel_size=3, stride=1)
        self.conv1_3 = BasicConv2d(32, 64, kernel_size=3, stride=1, padding=1)
        # 池化层
        self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.conv2 = BasicConv2d(64, 80, kernel_size=3)
        self.conv3 = BasicConv2d(80, 192, kernel_size=3, stride=2)
        self.conv4 = BasicConv2d(192, 192, kernel_size=3, padding=1)

        self.inception3a = InceptionV3A(192, 64, 48, 64, 64, 96, 32)
        self.inception3b = InceptionV3A(256, 64, 48, 64, 64, 96, 64)
        self.inception3c = InceptionV3A(288, 64, 48, 64, 64, 96, 64)

        self.inception4a = InceptionV3D(288, 0, 384, 384, 64, 96, 0)
        self.inception4b = InceptionV3B(768, 192, 128, 192, 128, 192, 192)
        self.inception4c = InceptionV3B(768, 192, 160, 192, 160, 192, 192)
        self.inception4d = InceptionV3B(768, 192, 160, 192, 160, 192, 192)
        self.inception4e = InceptionV3D(768, 0, 384, 384, 64, 128, 0)

        if self.aux_logits == True:
            self.aux = InceptionAux(in_channels=768, out_channels=num_classes)

        self.inception5a = InceptionV3C(1280, 320, 384, 384, 448, 384, 192)
        self.inception5b = InceptionV3C(2048, 320, 384, 384, 448, 384, 192)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.dropout = nn.Dropout(0.5)
        self.fc = nn.Linear(2048, num_classes)
        if init_weights:
            self._initialize_weights()

    def forward(self, x):
        # N x 3 x 299 x 299
        x = self.conv1_1(x)
        # N x 32 x 149 x 149
        x = self.conv1_2(x)
        # N x 32 x 147 x 147
        x = self.conv1_3(x)
        #  N x 32 x 147 x 147
        x = self.maxpool1(x)
        # N x 64 x 73 x 73
        x = self.conv2(x)
        # N x 80 x 71 x 71
        x = self.conv3(x)
        # N x 192 x 35 x 35
        x = self.conv4(x)
        # N x 192 x 35 x 35
        x = self.inception3a(x)
        # N x 256 x 35 x 35
        x = self.inception3b(x)
        # N x 288 x 35 x 35
        x = self.inception3c(x)
        # N x 288 x 35x 35
        x = self.inception4a(x)
        # N x 768 x 17 x 17
        x = self.inception4b(x)
        # N x 768 x 17 x 17
        x = self.inception4c(x)
        # N x 768 x 17 x 17
        x = self.inception4d(x)
        # N x 768 x 17 x 17
        if self.training and self.aux_logits:    # eval model lose this layer
            aux = self.aux(x)
        # N x 768 x 17 x 17
        x = self.inception4e(x)
        # N x 1280 x 8 x 8
        x = self.inception5a(x)
        # N x 2048 x 8 x 8
        x = self.inception5b(x)
        # N x 2048 x 7 x 7
        x = self.avgpool(x)
        # N x 2048 x 1 x 1
        x = torch.flatten(x, 1)
        # N x 1024
        x = self.dropout(x)
        x = self.fc(x)
        # N x 1000(num_classes)
        if self.training and self.aux_logits:  # 训练阶段使用
            return x, aux
        return x
    # 对模型的权重进行初始化操作
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)

# InceptionV3A:BasicConv2d+MaxPool2d
class InceptionV3A(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3A, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+3×3卷积
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)   # 保证输出大小等于输入大小
        )
        # 1×1卷积++3×3卷积+3×3卷积
        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, padding=1)         # 保证输出大小等于输入大小
        )
        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

# InceptionV3B:BasicConv2d+MaxPool2d
class InceptionV3B(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3B, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+1×3卷积+3×1卷积
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=[1, 3], padding=[0, 1]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[3, 1], padding=[1, 0])   # 保证输出大小等于输入大小
        )
        # 1×1卷积+1×3卷积+3×1卷积+1×3卷积+3×1卷积
        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=[1, 3], padding=[0, 1]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[3, 1], padding=[1, 0]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[1, 3], padding=[0, 1]),
            BasicConv2d(ch3x3, ch3x3, kernel_size=[3, 1], padding=[1, 0])  # 保证输出大小等于输入大小
        )
        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

# InceptionV3C:BasicConv2d+MaxPool2d
class InceptionV3C(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3C, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+1×3卷积+3×1卷积
        self.branch2_0 = BasicConv2d(in_channels, ch3x3red, kernel_size=1)
        self.branch2_1 = BasicConv2d(ch3x3red, ch3x3, kernel_size=[1, 3], padding=[0, 1])
        self.branch2_2 = BasicConv2d(ch3x3red, ch3x3, kernel_size=[3, 1], padding=[1, 0])

        # 1×1卷积+3×3卷积+1×3卷积+3×1卷积
        self.branch3_0 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),
        )
        self.branch3_1 = BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=[1, 3], padding=[0, 1])
        self.branch3_2 = BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=[3, 1], padding=[1, 0])

        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2_0 = self.branch2_0(x)
        branch2 = torch.cat([self.branch2_1(branch2_0), self.branch2_2(branch2_0)], dim=1)
        branch3_0 = self.branch3_0(x)
        branch3 = torch.cat([self.branch3_1(branch3_0), self.branch3_2(branch3_0)], dim=1)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

# InceptionV3D:BasicConv2d+MaxPool2d
class InceptionV3D(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV3D, self).__init__()
        # ch1x1:没有1×1卷积
        # 1×1卷积+3×3卷积,步长为2
        self.branch1 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, stride=2)   # 保证输出大小等于输入大小
        )
        # 1×1卷积+3×3卷积+3×3卷积,步长为2
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),   # 保证输出大小等于输入大小
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, stride=2)         # 保证输出大小等于输入大小
        )
        # 3×3池化,步长为2
        self.branch3 = nn.Sequential(nn.MaxPool2d(kernel_size=3, stride=2))
        # pool_proj:池化层后不再接卷积层

    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        # 拼接
        outputs = [branch1,branch2, branch3]
        return torch.cat(outputs, 1)

# 辅助分类器:AvgPool2d+BasicConv2d+Linear+dropout
class InceptionAux(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(InceptionAux, self).__init__()

        self.averagePool = nn.AvgPool2d(kernel_size=5, stride=3)
        self.conv1 = BasicConv2d(in_channels=in_channels, out_channels=128, kernel_size=1)
        self.conv2 = BasicConv2d(in_channels=128, out_channels=768, kernel_size=5, stride=1)
        self.dropout = nn.Dropout(p=0.7)
        self.linear = nn.Linear(in_features=768, out_features=out_channels)
    def forward(self, x):
        # N x 768 x 17 x 17
        x = self.averagePool(x)
        # N x 768 x 5 x 5
        x = self.conv1(x)
        # N x 128 x 5 x 5
        x = self.conv2(x)
        # N x 768 x 1 x 1
        x = x.view(x.size(0), -1)
        # N x 768
        out = self.linear(self.dropout(x))
        # N x num_classes
        return out

# 卷积组: Conv2d+BN+ReLU
class BasicConv2d(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
        super(BasicConv2d, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

if __name__ == '__main__':
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model = GoogLeNetV3().to(device)
    summary(model, input_size=(3, 299, 299))

summary可以打印网络结构和参数,方便查看搭建好的网络结构。


总结

尽可能简单、详细的介绍了小卷积分解为非对称卷积的原理和在卷积神经网络中的作用,讲解了GoogLeNet(InceptionV3)模型的结构和pytorch代码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

根据关键词搜索阿里巴巴商品数据列表接口|阿里巴巴商品列表数据接口|阿里巴巴商品API接口|阿里巴巴API接口

阿里巴巴也提供了根据关键词搜索商品数据列表的接口,方便开发者根据关键词搜索商品并进行相关操作。 请求参数可以包括: q:搜索关键字 start_price:开始价格 end_price:结束价格 page:页码 cat&#xff1…

9步打造个人ip

什么是个人IP? 就是一个人创造出来的属于自己的有个性有价值的,能让他人记住你,信任你,认可你的东西。 如何强化个人IP呢? 需要一些必要的条件如专业性、耐心、勤奋等等要知道,打造IP是一个见效慢的过程&am…

Multisim数电仿真实验——SOS循环序列信号发生器

目录 一、前言二、设计思路2.1序列信号的实现2.2SOS信号的循环再现 三、最终电路图 一、前言 SOS电路是一种简单而重要的电子电路,用于产生和传输紧急信号。我们将介绍SOS电路的连接思路,包括所需的组件选择以及信号的连接方式。 二、设计思路 2.1序列…

复杂度分析

目录 一.算法效率 二.大O渐进表示法 三.时间复杂度 常见的时间复杂度: 时间复杂度计算练习: 四.空间复杂度 常见的空间复杂度: 空间复杂度计算练习: 一.算法效率 追求算法效率: 找到问题解法:算法需…

什么是原生IP与广播IP?如何区分?为什么需要用原生IP?

在代理IP中,我们常常听到原生IP与广播IP,二者有何区别?如何区分呢?下面为大家详细讲解。 一、什么是原生IP 原生IP地址是互联网服务提供商(ISP)直接分配给用户的真实IP地址,无需代理或转发。此…

轻量封装WebGPU渲染系统示例<32>- 若干线框对象(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/WireframeEntityTest.ts 当前示例运行效果: 此示例基于此渲染系统实现,当前示例TypeScript源码如下: export class WireframeEntityTest {private mRsc…

拜耳阵列(Bayer Pattern)以及常见彩色滤波矩阵(CFA)

一、拜耳阵列的来源 图像传感器将光线转化成电流,光线越亮,电流的数值就越大;光线越暗,电流的数值就越小。图像传感器只能感受光的强弱,无法感受光的波长。由于光的颜色由波长决定,所以图像传播器无法记录…

博客系统页面设计

目录 前言 1.预期效果 1.1博客列表页效果 1.2博客详情页效果 1.3博客登陆页效果 2.实现博客列表页 2.1实现导航栏 2.2实现版心 2.3实现个人信息 2.4实现博客列表 3.实现博客正文页 3.1引入导航栏 3.2引入版心 3.3引入个人信息 3.4实现博客正文 4.实现博客登陆页…

【寒武纪(7)】MLU的cntoolkit:Cambricon-BANG架构和使用分析,MLU并行计算的硬件抽象、编程模型以及调优思路

文章目录 硬件抽象1存储1.1.1 存储层次访存一致 计算模型1 Core核内同步和并行2 核间并行和同步 编程模型1、Kernel计算规模 任务类型执行示例 性能调优性能调优实践参考 cambricon BANG架构是基础的,高度抽象的,向用户暴露统一编程模型和编程接口&#…

Go 理解零值

在 Go 语言中,零值(Zero Value)是指在声明变量但没有显式赋值的情况下,变量会被自动赋予一个默认值。这个默认值取决于变量的类型,不同类型的变量会有不同的零值。零值是 Go 语言中的一个重要概念,因为它确…

Pytest UI自动化测试实战实例

环境准备 序号库/插件/工具安装命令1确保您已经安装了python3.x2配置python3pycharmselenium2开发环境3安装pytest库 pip install pytest 4安装pytest -html 报告插件pip install pytest-html5安装pypiwin32库(用来模拟按键)pip install pypiwin32 6安装openpyxl解析excel文…

教你如何优化MySQL慢查询SQL语句?快速提升系统性能!

前言 应用系统性能测试过程中,性能优化是绕不开的话题,对测试人员而言,性能优化的第一站就是SQL语句的优化与分析。因此本文主要以MySQL数据库为例,介绍常见的慢查询SQL语句执行效率分析与优化方法和简单示例,为致力于…

【原创】V2024中化解电力行业设备表的五年难题

我这个人今生注定不能“大富大贵”,因为我的缺点实在太多了,其中非常重要的一项是:脸盲!简单来说就是很容易把不同的人搞混,记住名字的时候没记住面相,记住面相的时候又把名字给忘了,尴尬的人生…

Pod详细介绍

目录 Pod 1、Pod基础概念 2、集群中Pod的使用方式 1)一个Pod中运行一个容器 2)一个Pod中运行多个容器 3、Pod的类型 1)控制器管理的Pod 2)自助式Pod 3)静态Pod 4、Pod中容器的分类 1)基础容器&#xf…

day26_css

今日内容 零、 复习昨日 一、CSS 零、 复习昨日 HTML - 页面基本骨架结构,内容展现 CSS - 美化页面,布局 JS - 动起来 一 、引言 1.1CSS概念 ​ 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)…

首周聚焦百度智能云千帆大模型平台使用,《大模型应用实践》实训营11月16日开讲!

百度智能云千帆大模型平台官方出品的《大模型应用实践》实训营本周正式上线!这是百度智能云推出的首个系列课程,课程内容满满干货! 11月16日本周四即将开课,首周由百度智能云千帆大模型平台产品经理以及百度智能云千帆资深用户知…

什么是自动化测试框架?常用的自动化测试框架有哪些?

无论是在自动化测试实践,还是日常交流中,经常听到一个词:框架。之前学习自动化测试的过程中,一直对“框架”这个词知其然不知其所以然。 最近看了很多自动化相关的资料,加上自己的一些实践,算是对“框架”…

【echarts】实现单线与多线滚轮联动、隐藏拖拽、关闭动画

单线滚轮联动 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>ECharts DataZoom</title><script src"https://cdn.jsdelivr.net/npm/echarts5.2.0/dist/echarts.min.js"></script> </hea…

为什么软件可以被破解,但是压缩包却破解不了?

为什么软件可以被破解&#xff0c;但是压缩包却破解不了&#xff1f; 软件的加密和压缩包的加密不是同一种加密。 压缩包的加密是传统意义上数据的加密&#xff0c;就是用一个密钥&#xff08;密码&#xff09;&#xff0c;对原始数据进行一些数学运算&#xff0c;得到一个密文…

数据结构与算法之美学习笔记:19 | 散列表(中):如何打造一个工业级水平的散列表?

目录 前言如何设计散列函数&#xff1f;装载因子过大了怎么办&#xff1f;如何避免低效的扩容&#xff1f;如何选择冲突解决方法&#xff1f;工业级散列表举例分析解答开篇内容小结 前言 本节课程思维导图&#xff1a; 今天&#xff0c;我们就来学习一下&#xff0c;如何设计一…