24/10/13 算法笔记 GoogLeNet

发布于:2024-10-17 ⋅ 阅读:(8) ⋅ 点赞:(0)

在GoogLeNet中,基本的卷积块被称为Inception块

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)

inception块的亮点:

多路径并行处理,增加了网络的宽度

1*1卷积被用来减少或增加通道数,同时减少计算量

不同大小的卷积核使得网络能够捕捉不同尺度的特征

使用最大汇聚层,在空间上降低特征图的尺寸,同时保留最重要的信息

在最大汇聚层后立即使用1*1卷积层,有助于减少参数量,使得特征图的通道数可以调整

滤波器(filter)的组合

它们可以用各种滤波器尺寸探索图像,这意味着不同大小的滤波器可以有效地识别不同范围的图像细节。 同时,我们可以为不同的滤波器分配不同数量的参数。

GoogLeNet模型

逐层实现

第一层:64个通道,7*7卷积层

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))

第二层:第一个卷积层64个通道,1*1卷积层,第二个卷积层使用将通道数量增加三倍的3*3卷积层,这对于inception块中的第二条路径

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))

第三个模块串联两个完整的Inception块

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))

第四模块更加复杂, 它串联了5个Inception块

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))

第五模块包含输出通道数为256+320+128+128=832和384+384+128+128=1024的两个Inception块。

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))

问题:

什么时候需要些forward函数?

        自定义卷积层的时候:创建一个具有特殊行为的卷积层,比如改变卷积核的大小,步长,填充或者添加特定的激活函数组合时,需要继承现有的层并重写forward函数

        组合卷积操作:当你想要将卷积与其他操作(如批量归一化,激活函数,dropout等)组合在一起时,可以通过定义forward函数来实现

        实现特殊模型时:如Inception网络,ResNet等,其中包含了多个并行的卷积路径,需要在forward函数中实现这些路径的数据流动

        集成外部操作:如果要集成一些外部操作,比如自定义的数学函数或从其他库导入的操作时,在forward中实现

        修改前向传播逻辑:如果需要根据输入数据的不同特性动态改变前向传播行为,如使用条件卷积