天天看點

用 Python 從零開始建構 Inception Network

介紹

随着越來越多的高效體系結構出現在世界各地的研究論文中,深度學習體系結構正在迅速發展。這些研究論文不僅包含了大量的資訊,而且為新的深度學習體系結構的誕生提供了一條新的途徑,它們通常難以解析。為了了解這些論文,人們可能需要多次閱讀那篇論文,甚至可能需要閱讀其他相關論文。Inception 就是其中之一。

Inception 網絡是 CNN 圖像分類器發展過程中的一個重要裡程碑。在此架構之前,大多數流行的 CNN 或分類器隻是使用越來越深的堆疊卷積層以獲得更好的性能。

另一方面,Inception 網絡經過精心設計,非常深入和複雜。它使用了許多不同的技術來推動其性能;無論是速度還是準确性。

什麼是Inception?

Inception Network(ResNet)是Christian Szegedy、Wei Liu、Yangqing Jia介紹的著名深度學習模型之一。Pierre Sermanet、Scott Reed、Dragomir Anguelov、Dumitru Erhan、Vincent Vanhoucke 和 Andrew Rabinovich在 2014 年的論文“Going deeper with convolutions” [1]中。

後來演化出了不同版本的 Inception 網絡。這是 Sergey Ioffe、Christian Szegedy、Jonathon Shlens、Vincent Vanhouck 和 Zbigniew Wojna在 2015 年題為“Rethinking the Inception Architecture for Computer Vision” [2] 的論文中提出的。Inception模型被歸類為最受歡迎和最常用的深度學習模型之一。

設計原則

–少數通用設計原則和優化技術的提議被證明對有效地擴充卷積網絡很有用。

–在網絡體系結構的早期,避免代表性瓶頸。

–如果網絡具有更多不同的過濾器,這些過濾器将具有更多不同的特征圖,則網絡将學習得更快。

–降維的空間聚合可以在低維嵌入上完成,而不會損失太多表示能力。

–通過寬度和深度之間的平衡,可以實作網絡的最佳性能。

初始子產品

初始子產品(naive)

用 Python 從零開始建構 Inception Network

資料來源:'Going Deeper with Convolution ' 論文

最優局部稀疏結構的近似

  • 處理各種尺度的視覺/空間資訊,然後聚合
  • 從計算上看,這有點樂觀
  • 5×5 卷積非常花費開銷

Inception 子產品(降維)

用 Python 從零開始建構 Inception Network

來源:'Going Deeper with Convolution' 論文

降維是必要和有動力的(網絡中的網絡)

  • 通過 1×1 卷積實作
  • 深入思考學習池化,而不是高度/寬度的最大/平均池化。

初始架構

使用降維的inception子產品,建構了深度神經網絡架構(Inception v1)。架構如下圖所示:

用 Python 從零開始建構 Inception Network

Inception 網絡線性堆疊了 9 個這樣的 Inception 子產品。它有 22 層深(如果包括池化層,則為 27 層)。在最後一個 inception 子產品的最後,它使用了全局平均池化。

  • 對于降維和修正線性激活,使用了 128 個濾波器的 1×1 卷積。
  • 具有 1024 個單元的全連接配接層的修正線性激活。
  • 使用 dropout 層丢棄輸出的比例為 70%。
  • 使用線性層作為分類器的 softmax 損失。

Inception 網絡的流行版本如下:

  • Inception v1
  • Inception v2
  • Inception v3
  • Inception v4
  • Inception-ResNet

讓我們從頭開始建構 Inception v1(GoogLeNet):

Inception 架構多次使用 CNN 塊和 1×1、3×3、5×5 等不同的過濾器,是以讓我們為 CNN 塊建立一個類,它采用輸入通道和輸出通道以及 batchnorm2d 和 ReLu 激活.

class conv_block(nn.Module):
    def __init__(self, in_channels, out_channels, **kwargs):
        super(conv_block, self).__init__()
        self.relu = nn.ReLU()
        self.conv = nn.Conv2d(in_channels, out_channels, **kwargs)
        self.batchnorm = nn.BatchNorm2d(out_channels)
   def forward(self, x):
       return self.relu(self.batchnorm(self.conv(x)))


           

然後為inception module建立一個降維的類,參考上圖,從1×1 filter輸出,reduction 3×3,然後從3×3 filter輸出,reduction 5×5,然後從5×5輸出 和 1×1 池中輸出。

class Inception_block(nn.Module):
    def __init__(
        self, in_channels, out_1x1, red_3x3, out_3x3, red_5x5, out_5x5, out_1x1pool
    ):
        super(Inception_block, self).__init__()
        self.branch1 = conv_block(in_channels, out_1x1, kernel_size=(1, 1))

self.branch2 = nn.Sequential(
conv_block(in_channels, red_3x3, kernel_size=(1, 1)),
conv_block(red_3x3, out_3x3, kernel_size=(3, 3), padding=(1, 1)),
)

self.branch3 = nn.Sequential(
conv_block(in_channels, red_5x5, kernel_size=(1, 1)),
conv_block(red_5x5, out_5x5, kernel_size=(5, 5), padding=(2, 2)),
)

self.branch4 = nn.Sequential(
nn.MaxPool2d(kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),
conv_block(in_channels, out_1x1pool, kernel_size=(1, 1)),
)

def forward(self, x):
return torch.cat(
[self.branch1(x), self.branch2(x), self.branch3(x), self.branch4(x)], 1
)

           

讓我們保留下圖作為參考并開始建構網絡。

用 Python 從零開始建構 Inception Network

來源:'Going Deeper with Convolution' 論文

建立一個類作為 GoogLeNet

class GoogLeNet(nn.Module):
    def __init__(self, aux_logits=True, num_classes=1000):
        super(GoogLeNet, self).__init__()
        assert aux_logits == True or aux_logits == False
        self.aux_logits = aux_logits

# Write in_channels, etc, all explicit in self.conv1, rest will write to
# make everything as compact as possible, kernel_size=3 instead of (3,3)
self.conv1 = conv_block(
in_channels=3,
out_channels=64,
kernel_size=(7, 7),
stride=(2, 2),
padding=(3, 3),
)

self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.conv2 = conv_block(64, 192, kernel_size=3, stride=1, padding=1)
self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

# In this order: in_channels, out_1x1, red_3x3, out_3x3, red_5x5, out_5x5, out_1x1pool
self.inception3a = Inception_block(192, 64, 96, 128, 16, 32, 32)
self.inception3b = Inception_block(256, 128, 128, 192, 32, 96, 64)
self.maxpool3 = nn.MaxPool2d(kernel_size=(3, 3), stride=2, padding=1)

self.inception4a = Inception_block(480, 192, 96, 208, 16, 48, 64)
self.inception4b = Inception_block(512, 160, 112, 224, 24, 64, 64)
self.inception4c = Inception_block(512, 128, 128, 256, 24, 64, 64)
self.inception4d = Inception_block(512, 112, 144, 288, 32, 64, 64)
self.inception4e = Inception_block(528, 256, 160, 320, 32, 128, 128)
self.maxpool4 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

self.inception5a = Inception_block(832, 256, 160, 320, 32, 128, 128)
self.inception5b = Inception_block(832, 384, 192, 384, 48, 128, 128)

self.avgpool = nn.AvgPool2d(kernel_size=7, stride=1)
self.dropout = nn.Dropout(p=0.4)
self.fc1 = nn.Linear(1024, num_classes)

if self.aux_logits:
self.aux1 = InceptionAux(512, num_classes)
self.aux2 = InceptionAux(528, num_classes)
else:
self.aux1 = self.aux2 = None

def forward(self, x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
# x = self.conv3(x)
x = self.maxpool2(x)

x = self.inception3a(x)
x = self.inception3b(x)
x = self.maxpool3(x)

x = self.inception4a(x)

# Auxiliary Softmax classifier 1
if self.aux_logits and self.training:
aux1 = self.aux1(x)

x = self.inception4b(x)
x = self.inception4c(x)
x = self.inception4d(x)

# Auxiliary Softmax classifier 2
if self.aux_logits and self.training:
aux2 = self.aux2(x)

x = self.inception4e(x)
x = self.maxpool4(x)
x = self.inception5a(x)
x = self.inception5b(x)
x = self.avgpool(x)
x = x.reshape(x.shape[0], -1)
x = self.dropout(x)
x = self.fc1(x)

if self.aux_logits and self.training:
return aux1, aux2, x
else:
return x

           

然後為輸出層定義一個類,如論文中提到的 dropout=0.7 和一個帶有 softmax 的線性層來輸出 n_classes。

class InceptionAux(nn.Module):
    def __init__(self, in_channels, num_classes):
        super(InceptionAux, self).__init__()
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(p=0.7)
        self.pool = nn.AvgPool2d(kernel_size=5, stride=3)
        self.conv = conv_block(in_channels, 128, kernel_size=1)
        self.fc1 = nn.Linear(2048, 1024)
        self.fc2 = nn.Linear(1024, num_classes)

           
    def forward(self, x):
        x = self.pool(x)
        x = self.conv(x)
        x = x.reshape(x.shape[0], -1)
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)

        return x

           

然後程式應該如下所示對齊。

– Class GoogLeNet

– Class Inception_block

– Class InceptionAux

– Class conv_block

然後最後讓我們寫一小段測試代碼來檢查我們的模型是否工作正常。

if __name__ == "__main__":
    # N = 3 (Mini batch size)
    x = torch.randn(3, 3, 224, 224)
    model = GoogLeNet(aux_logits=True, num_classes=1000)
    print(model(x)[2].shape)

           

輸出應如下所示

用 Python 從零開始建構 Inception Network

整個代碼可以在這裡通路:

https://github.com/BakingBrains/Deep_Learning_models_implementation_from-scratch_using_pytorch_/blob/main/Inception_.py

[1]. Christian Szegedy, Wei Liu, Yangqing Jia. Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke and Andrew Rabinovich: Going deeper with convolutions, Sep 2014, DOI: https://arxiv.org/pdf/1409.4842v1.pdf

[2]. Sergey Ioffe , Christian Szegedy, Jonathon Shlens, Vincent Vanhouck and Zbigniew Wojna: Rethinking the Inception Architecture for Computer Vision, Dec 2015, DOI:https://arxiv.org/pdf/1512.00567.pdf

☆ END ☆

如果看到這裡,說明你喜歡這篇文章,請轉發、點贊。微信搜尋「uncle_pn」,歡迎添加小編微信「 mthler」,每日朋友圈更新一篇高品質博文。

↓掃描二維碼添加小編↓

用 Python 從零開始建構 Inception Network

繼續閱讀