1. 程式人生 > 其它 >手寫分類模型--GoogLeNet(Inceptionv1)

手寫分類模型--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