手写分类模型--GoogLeNet(Inceptionv1)

手写代码主要原因就是为了面试,可以不用把所有的分类模型手写一遍,写个10个左右就差不多了,之后再手写一两个检测代码分割代码,差不多就对整体有个概念了.
完整代码在我github上

GoogLeNet主要有inceveption块组成,GoogLeNet跟VGG一样,在主体卷积部分中使用5个模块(block),每个模块之间使用步幅为2的3×3最大池化层来减小输出高宽,其基本inception块如下图所示:
在这里插入图片描述
inception主要由4条并行路线组成,前三条路线主要用来提取不同不同尺寸下信息,中间两个路径为了减小模型参数,所以输出通道数小
这个部分对应的代码为:(代码很简单)

class Inception(nn.Module):
    def __init__(self, in_c, out_1, out_2, out_3, out_4):
        super(Inception, self).__init__()#子类继承父类时,重写init函数要调用suoer()方法
        self.p_1 = nn.Conv2d(in_c, out_1, kernel_size=1)#padding默认为0,stride默认为1,这里所以没写
        self.p_21 = nn.Conv2d(in_c, out_2[0], kernel_size=1)
        self.p_22 = nn.Conv2d(out_2[0], out_2[1], kernel_size=3, padding=1)#注意padding
        self.p_31 = nn.Conv2d(in_c, out_3[0], kernel_size=1)
        self.p_32 = nn.Conv2d(out_3[0], out_3[1], kernel_size=5, padding=2)#注意padding
        self.p_41 = nn.MaxPool2d(kernel_size=3, padding=1, stride=1)#这里写参数stride=1,是因为源码中maxpool2d的stride默认为None
        self.p_42 = nn.Conv2d(in_c, out_4, kernel_size=1)

    def forword(self, x):
        p1 = F.relu(self.p_1(x))#因为当时还没有提出bn,所以这里没有bn
        p2 = F.relu(self.p_22(F.relu(self.p_21(x))))
        p3 = F.relu(self.p_32(F.relu(self.p_31(x))))
        p4 = F.relu(self.p_42(F.relu(self.p_41(x))))

        return torch.cat((p1, p2, p3, p4), dim=1)

然后先定义2个类,一个是全局平均池化,全局平均池化接在最后一个block后面;一个是展平操作,用来将全局平均池化后的tensor进行展平,以连接全连接层

#这和是b5最后接的全局平均池化层,将hxw的特征图变为1x1
class GlobalAvgPool2d(nn.Module):
    def __init__(self):
        super(GlobalAvgPool2d, self).__init__()
    def forward(self, x):
        return F.avg_pool2d(x, kernel_size=x.size()[2:])

#这里是将最后得到的(b,c,1,1)的特征图展平,和全连接层进行连接
class FlattenLayer(nn.Module):
    def __init__(self):
        super(FlattenLayer, self).__init__()
    def forward(self, x):
        return x.view(x.shape[0], -1)

最后就是完整的网络代码

#GoogLeNet和VGG一样,有5个block块,每个block块由步幅为2的3x3最大池化层减小输出宽度
b1 = nn.Sequential(
    nn.Conv2d(3, 64, kernel_size=7, stride= 2, padding=3),
    nn.ReLU(),                      ##这里用到了nn.ReLU,前面定义inception块时用了F.relu,其实没啥差别
    nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)

b2 = nn.Sequential(
    nn.Conv2d(64, 64, kernel_size=1),
    nn.Conv2d(64, 192, kernel_size=3, padding=1),
    nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)

#前2个模块为普通的卷积,从第三个模块开始引入了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)
)

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),
    GlobalAvgPool2d()
                   )

net = nn.Sequential(b1, b2, b3, b4, b5, FlattenLayer(), nn.Linear(1024, 10))

输出看一下每层的结果:

X = torch.rand(1, 3, 256, 256)
for blk in net.children():
    X = blk(X)
    print('output shape: ', X.shape)

在这里插入图片描述

好的,到这里GoogLeNet就写完了,明天就写一下resnet

猜你喜欢

转载自blog.csdn.net/AWhiteDongDong/article/details/111173345
今日推荐