深度學習之影像分類(五)GoogLeNet網路結構
目錄
- 深度學習之影像分類(五)GoogLeNet網路結構
- 1. 前言
- 2. Inception 結構
- 3. 輔助分類器
- 4. 代碼
本節學習 GoogLeNet 網路結構,學習視頻源于 Bilibili,部分描述參考 大話CNN經典模型:GoogLeNet(從Inception v1到v4的演進),

1. 前言
GoogLeNet 是 2014 年由 Google 團隊提出的,斬獲了當年 ImageNet 競賽中 Classification Task 的第一名,注意是 GoogLeNet,大寫 L 是為了致敬 LeNet,其原始論文為《Going deeper with convolutions》,
如何提升網路性能?一般來說,提升網路性能最直接的辦法就是增加網路深度和寬度,深度指網路層次數量、寬度指神經元數量,但這種方式存在以下問題:
(1)引數太多,如果訓練資料集有限,很容易產生過擬合;
(2)網路越大、引數越多,計算復雜度越大,難以應用;
(3)網路越深,容易出現梯度彌散問題(梯度越往后穿越容易消失),難以優化模型,
解決這些問題的方法當然就是在增加網路深度和寬度的同時減少引數,為了減少引數,自然就想到將全連接變成稀疏連接,但是在實作上,全連接變成稀疏連接后實際計算量并不會有質的提升,因為大部分硬體是針對密集矩陣計算優化的,稀疏矩陣雖然資料量少,但是計算所消耗的時間卻很難減少,
那么,有沒有一種方法既能保持網路結構的稀疏性,又能利用密集矩陣的高計算性能,大量的文獻表明可以將稀疏矩陣聚類為較為密集的子矩陣來提高計算性能,就如人類的大腦是可以看做是神經元的重復堆積,因此,GoogLeNet 團隊提出了 Inception 網路結構,就是構造一種“基礎神經元”結構,來搭建一個稀疏性、高計算性能的網路結構,
該網路的亮點包括:
- 引入了 Inception 結構,融合不同尺度的特征資訊
- 使用 1 × 1 1 \times 1 1×1 的卷積核進行降維以及映射處理(VGGNet 中也有使用)
- 添加兩個輔助分類器幫助訓練(深監督,AlexNet和VGGNet都只有一個輸出層)
- 丟棄全連接層,使用平均池化層(大大減少模型引數)
GoogLeNet 的網路結構圖如下所示:


引數表中 #1x1,#3x3 reduce 等對應的子模塊如下所示:

不包含輔助分類器的 GoogLeNet 的引數量約為 VGG16 的 1/20,且只相當于Alexnet的 1/12,

2. Inception 結構
在 AlexNet 和 VGGNet 中,網路都是串行結構,將一系列卷積層和池化層進行串聯得到網路結構,但是在 Inception 中出現了并行結構,在圖(a)中,將特征矩陣輸入四個分支進行處理,再將處理結果拼接成輸出特征矩陣,需要注意的是:每個分支所得到的特征矩陣的高和寬必須相同,否則我們沒辦法按照通道進行拼接,在圖(b)中,加上了三個
1
×
1
1 \times 1
1×1 卷積層進行降維處理,注意,在 Inception 的 Maxpool 中 stride = 1,也就是保特征圖大小的池化操作,

假設我們不使用 1 × 1 1 \times 1 1×1 卷積層進行降維,對于輸入通道為 512 的特征圖使用 64 個 5 × 5 5 \times 5 5×5 的卷積核進行卷積,那么引數量為 5 × 5 × 512 × 64 = 819200 5 \times 5 \times 512 \times 64 = 819200 5×5×512×64=819200,如果先使用 1 × 1 1 \times 1 1×1 卷積將通道縮小為 24,再使用 64 個 5 × 5 5 \times 5 5×5 的卷積核進行卷積,則引數量為 1 × 1 × 512 × 24 + 5 × 5 × 24 × 64 = 50688 1 \times 1 \times 512 \times 24 + 5 \times 5 \times 24 \times 64= 50688 1×1×512×24+5×5×24×64=50688,而且,感受野一樣!

inception 有什么好處呢?Szegedy從多個角度進行了解釋(參考百度百科):
解釋1:在直觀感覺上在多個尺度上同時進行卷積,能提取到不同尺度的特征,特征更為豐富也意味著最后分類判斷時更加準確,
解釋2:利用稀疏矩陣分解成密集矩陣計算的原理來加快收斂速度,舉個例子下圖左側是個稀疏矩陣(很多元素都為 0,不均勻分布在矩陣中),和一個 2x2 的矩陣進行卷積,需要對稀疏矩陣中的每一個元素進行計算;如果像下圖右圖那樣把稀疏矩陣分解成2個子密集矩陣,再和 2x2 矩陣進行卷積,稀疏矩陣中 0 較多的區域就可以不用計算,計算量就大大降低,這個原理應用到 inception 上就是要在特征維度上進行分解!傳統的卷積層的輸入資料只和一種尺度(比如 3x3 )的卷積核進行卷積,輸出固定維度(比如 256 個特征)的資料,所有 256 個輸出特征基本上是均勻分布在 3x3 尺度范圍上,這可以理解成輸出了一個稀疏分布的特征集;而 inception 模塊在多個尺度上提取特征(比如 1x1,3x3,5x5 ),輸出的 256 個特征就不再是均勻分布,而是相關性強的特征聚集在一起(比如 1x1 的 96 個特征聚集在一起,3x3 的 96 個特征聚集在一起,5x5 的 64 個特征聚集在一起),這可以理解成多個密集分布的子特征集,這樣的特征集中因為相關性較強的特征聚集在了一起,不相關的非關鍵特征就被榷訓,同樣是輸出 256 個特征,Inception 方法輸出的特征“冗余”的資訊較少,用這樣的“純”的特征集層層傳遞最后作為反向計算的輸入,自然收斂的速度更快,

解釋3:Hebbin 赫布原理,Hebbin 原理是神經科學上的一個理論,解釋了在學習的程序中腦中的神經元所發生的變化,用一句話概括就是 fire togethter, wire together ,赫布認為 “兩個神經元或者神經元系統,如果總是同時興奮,就會形成一種‘組合’,其中一個神經元的興奮會促進另一個的興奮”,比如狗看到肉會流口水,反復刺激后,腦中識別肉的神經元會和掌管唾液分泌的神經元會相互促進,“纏繞”在一起,以后再看到肉就會更快流出口水,用在 Inception 結構中就是要把相關性強的特征匯聚到一起,這有點類似上面的解釋2,把 1x1,3x3,5x5 的特征分開,因為訓練收斂的最終目的就是要提取出獨立的特征,所以預先把相關性強的特征匯聚,就能起到加速收斂的作用,
3. 輔助分類器
在 GoogLeNet 中有兩個結構一模一樣的輔助分類器,分別對 Inception 4a 和 Inception 4d 的輸出結果進行,Inception 4a 的輸出尺寸為 14 × 14 × 512 14 \times 14 \times 512 14×14×512,Inception 4d 的輸出尺寸為 14 × 14 × 528 14 \times 14 \times 528 14×14×528,
輔助分類器其結構如右圖所示,首先經過平均池化下采樣,池化核大小為 5 × 5 5 \times 5 5×5,stride = 3,padding = 0,所以對于 Inception 4a 的輸出特征矩陣變為了 4 × 4 × 512 4 \times 4 \times 512 4×4×512;對于 Inception 4d 的輸出特征矩陣變為了 4 × 4 × 528 4 \times 4 \times 528 4×4×528,然后通過 1 × 1 1 \times 1 1×1 卷積降維到 128 個通道,并使用 ReLU 激活函式,展平后使用 1024 個節點的全連接層以及 ReLU 函式(對應于右圖第一個 FC),然后經過一個 0.7 dropout ratio 的 Dropout 層,防止過擬合,然后經過 1000 個節點的輸出全連接層,

4. 代碼
import torch.nn as nn
import torch
import torch.nn.functional as F
class GoogLeNet(nn.Module):
def __init__(self, num_classes=1000, aux_logits=True, init_weights=False):
super(GoogLeNet, self).__init__()
self.aux_logits = aux_logits
self.conv1 = BasicConv2d(3, 64, kernel_size=7, stride=2, padding=3)
self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.conv2 = BasicConv2d(64, 64, kernel_size=1)
self.conv3 = BasicConv2d(64, 192, kernel_size=3, padding=1)
self.maxpool2 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception3a = Inception(192, 64, 96, 128, 16, 32, 32)
self.inception3b = Inception(256, 128, 128, 192, 32, 96, 64)
self.maxpool3 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception4a = Inception(480, 192, 96, 208, 16, 48, 64)
self.inception4b = Inception(512, 160, 112, 224, 24, 64, 64)
self.inception4c = Inception(512, 128, 128, 256, 24, 64, 64)
self.inception4d = Inception(512, 112, 144, 288, 32, 64, 64)
self.inception4e = Inception(528, 256, 160, 320, 32, 128, 128)
self.maxpool4 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception5a = Inception(832, 256, 160, 320, 32, 128, 128)
self.inception5b = Inception(832, 384, 192, 384, 48, 128, 128)
if self.aux_logits:
self.aux1 = InceptionAux(512, num_classes)
self.aux2 = InceptionAux(528, num_classes)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.dropout = nn.Dropout(0.4)
self.fc = nn.Linear(1024, num_classes)
if init_weights:
self._initialize_weights()
def forward(self, x):
# N x 3 x 224 x 224
x = self.conv1(x)
# N x 64 x 112 x 112
x = self.maxpool1(x)
# N x 64 x 56 x 56
x = self.conv2(x)
# N x 64 x 56 x 56
x = self.conv3(x)
# N x 192 x 56 x 56
x = self.maxpool2(x)
# N x 192 x 28 x 28
x = self.inception3a(x)
# N x 256 x 28 x 28
x = self.inception3b(x)
# N x 480 x 28 x 28
x = self.maxpool3(x)
# N x 480 x 14 x 14
x = self.inception4a(x)
# N x 512 x 14 x 14
if self.training and self.aux_logits: # eval model lose this layer
aux1 = self.aux1(x)
x = self.inception4b(x)
# N x 512 x 14 x 14
x = self.inception4c(x)
# N x 512 x 14 x 14
x = self.inception4d(x)
# N x 528 x 14 x 14
if self.training and self.aux_logits: # eval model lose this layer
aux2 = self.aux2(x)
x = self.inception4e(x)
# N x 832 x 14 x 14
x = self.maxpool4(x)
# N x 832 x 7 x 7
x = self.inception5a(x)
# N x 832 x 7 x 7
x = self.inception5b(x)
# N x 1024 x 7 x 7
x = self.avgpool(x)
# N x 1024 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: # eval model lose this layer
return x, aux2, aux1
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)
class Inception(nn.Module):
def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):
super(Inception, self).__init__()
self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
self.branch2 = nn.Sequential(
BasicConv2d(in_channels, ch3x3red, kernel_size=1),
BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1) # 保證輸出大小等于輸入大小
)
self.branch3 = nn.Sequential(
BasicConv2d(in_channels, ch5x5red, kernel_size=1),
BasicConv2d(ch5x5red, ch5x5, kernel_size=5, padding=2) # 保證輸出大小等于輸入大小
)
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)
class InceptionAux(nn.Module):
def __init__(self, in_channels, num_classes):
super(InceptionAux, self).__init__()
self.averagePool = nn.AvgPool2d(kernel_size=5, stride=3)
self.conv = BasicConv2d(in_channels, 128, kernel_size=1) # output[batch, 128, 4, 4]
self.fc1 = nn.Linear(2048, 1024)
self.fc2 = nn.Linear(1024, num_classes)
def forward(self, x):
# aux1: N x 512 x 14 x 14, aux2: N x 528 x 14 x 14
x = self.averagePool(x)
# aux1: N x 512 x 4 x 4, aux2: N x 528 x 4 x 4
x = self.conv(x)
# N x 128 x 4 x 4
x = torch.flatten(x, 1)
x = F.dropout(x, 0.5, training=self.training)
# N x 2048
x = F.relu(self.fc1(x), inplace=True)
x = F.dropout(x, 0.5, training=self.training)
# N x 1024
x = self.fc2(x)
# N x num_classes
return x
class BasicConv2d(nn.Module):
def __init__(self, in_channels, out_channels, **kwargs):
super(BasicConv2d, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, **kwargs)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
x = self.conv(x)
x = self.relu(x)
return x
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296684.html
標籤:其他
