天天看點

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

LeNet

  1998年,LeCun提出了第一個真正的卷積神經網絡,也是整個神經網絡的開山之作,稱為LeNet,現在主要指的是LeNet5或LeNet-5,如圖1.1所示。它的主要特征是将卷積層和下采樣層相結合作為網絡的基本機構,如果不計輸入層,該模型共7層,包括2個卷積層,2個下采樣層,3個全連接配接層。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.1

  注:由于在接入全連接配接層時,要将池化層的輸出轉換成全連接配接層需要的次元,是以,必須清晰的知道全連接配接層前feature map的大小。卷積層與池化層輸出的圖像大小,其計算如圖1.2所示。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.2

   本次利用pytorch實作整個LeNet模型,圖中的Subsampling層即可看作如今的池化層,最後一層(輸出層)也當作全連接配接層進行處理。

1 import torch as torch
 2 import torch.nn as nn
 3 class LeNet(nn.Module):
 4     def __init__(self):
 5         super(LeNet,self).__init__()
 6         layer1 = nn.Sequential()
 7         layer1.add_module('conv1',nn.Conv2d(1,6,5))
 8         layer1.add_module('pool1',nn.MaxPool2d(2,2))
 9         self.layer1 = layer1
10 
11         layer2 = nn.Sequential()
12         layer2.add_module('conv2',nn.Conv2d(6,16,5))
13         layer2.add_module('pool2',nn.MaxPool2d(2,2))
14         self.layer2 = layer2
15 
16         layer3 = nn.Sequential()
17         layer3.add_module('fc1',nn.Linear(16*5*5,120))
18         layer3.add_module('fc2',nn.Linear(120,84))
19         layer3.add_module('fc3',nn.Linear(84,10))
20         self.layer3 = layer3
21 
22     def forward(self, x):
23         x = self.layer1(x)
24         x = self.layer2(x)
25         x = x.view(x.size(0),-1)#轉換(降低)資料次元,進入全連接配接層
26         x = self.layer3(x)
27         return x
28 #代入資料檢驗
29 y = torch.randn(1,1,32,32)
30 model = LeNet()
31 model(y)      

 AlexNet

  在2010年,斯坦福大學的李飛飛正式組織并啟動了大規模視覺圖像識别競賽(ImageNet Large Scale Visual Recognition Challenge,ILSVRC)。在2012年,Alex Krizhevsky、Ilya Sutskever提出了一種非常重要的卷積神經網絡模型,它就是AlexNet,如圖1.3所  示,在ImageNet競賽上大放異彩,領先第二名10%的準确率奪得了冠軍,吸引了學術界與工業界的廣泛關注。

  AlexNet神經網絡相比LeNet:

  • 1、 使用ReLU激活函數。在AlexNet之前,神經網絡一般都使用sigmoid或tanh作為激活函數,這類函數在自變量非常大或者非常小時,函數輸出基本不變,稱之為飽和函數。為了提高訓練速度,AlexNet使用了修正線性函數ReLU,它是一種非飽和函數,與 sigmoid 和tanh 函數相比,ReLU分片的線性結構實作了非線性結構的表達能力,梯度消失現象相對較弱,有助于訓練更深層的網絡。
  • 2、 使用GPU訓練。與CPU不同的是,GPU轉為執行複雜的數學和幾何計算而設計,AlexNet使用了2個GPU來提升速度,分别放置一半卷積核。
  • 3、 局部響應歸一化。AlexNet使用局部響應歸一化技巧,将ImageNet上的top-1與top-5錯誤率分别減少了1.4%和1.2%。
  • 4、 重疊池化層。與不重疊池化層相比,重疊池化層有助于緩解過拟合,使得AlexNet的top-1和top-5錯誤率分别降低了0.4%和0.3%。
  • 5、 減少過拟合。AlexNet使用了資料擴增與丢失輸出兩種技巧。資料擴增:a、圖像的平移、翻轉,b、基于PCA的RGB強度調整。丢失輸出技巧(DropOut層),AlexNet以0.5的機率将兩個全連接配接層神經元的輸出設定為0,有效阻止了過拟合現象的發生。
Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.3

  利用pytorch實作AlexNet網絡,由于當時,GPU的計算能力不強,是以Alex采用了2個GPU并行來計算,如今的GPU計算能力,完全可以替代。

1 import torch.nn as nn
 2 import torch
 3 
 4 class AlexNet(nn.Module):
 5     def __init__(self,num_classes):
 6         super(AlexNet,self).__init__()
 7         self.features = nn.Sequential(
 8             nn.Conv2d(3,64,11,4,padding=2),
 9             # inplace=True,是對于Conv2d這樣的上層網絡傳遞下來的tensor直接進行修改,好處就是可以節省運算記憶體,不用多儲存變量
10             nn.ReLU(inplace=True),
11             nn.MaxPool2d(kernel_size=3,stride=2),
12 
13             nn.Conv2d(64,192,kernel_size=5,padding=2),
14             nn.ReLU(inplace=True),
15             nn.MaxPool2d(kernel_size=3,stride=2),
16 
17             nn.Conv2d(192,384,kernel_size=3,padding=1),
18             nn.ReLU(inplace=True),
19             nn.Conv2d(384,256,kernel_size=3,padding=1),
20             nn.ReLU(inplace=True),
21 
22             nn.Conv2d(256,256,kernel_size=3,padding=1),
23             nn.ReLU(inplace=True),
24             nn.MaxPool2d(kernel_size=3,stride=1)
25         )
26         self.classifier = nn.Sequential(
27             nn.Dropout(),
28             nn.Linear(256*6*6,4096),
29             nn.ReLU(inplace=True),
30             nn.Dropout(),
31             nn.Linear(4096,4096),
32             nn.ReLU(inplace=True),
33             nn.Linear(4096,num_classes)
34         )
35     def forward(self, x):
36         x = self.features(x)
37         x = x.view(x.size(0),-1)
38         x = self.classifier(x)
39         return x      

VGGNet

  在2014年,參加ILSVRC競賽的“VGG”隊在ImageNet上獲得了比賽的亞軍。VGG的核心思想是利用較小的卷積核來增加網絡的深度。常用的有VGG16、VGG19兩種類型。VGG16擁有13個卷積層(核大小均為3*3),5個最大池化層,3個全連接配接層。VGG19擁有16個卷積層(核大小均為3*3),5個最大池化層,3個全連接配接層,如圖1.4所示。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.4

  加深結構都使用ReLU激活函數,VGG19比VGG16的差別在于多了3個卷積層,利用pytorch實作整VG16模型,VGG19同理。

1 import torch as torch
 2 import torch.nn as nn
 3 
 4 class VGG16(nn.Module):
 5     def __init__(self,num_classes):
 6         super(VGG16,self).__init__()
 7         self.features = nn.Sequential(
 8             nn.Conv2d(3,64,kernel_size=3,padding=1),
 9             nn.ReLU(inplace=True),
10             nn.Conv2d(64,64,kernel_size=3,padding=1),
11             nn.ReLU(inplace=True),
12 
13             nn.Conv2d(64,128,kernel_size=3,padding=1),
14             nn.ReLU(inplace=True),
15             nn.Conv2d(128, 128, kernel_size=3, padding=1),
16             nn.ReLU(inplace=True),
17 
18             nn.Conv2d(128, 256, kernel_size=3, padding=1),
19             nn.ReLU(inplace=True),
20             nn.Conv2d(256, 256, kernel_size=3, padding=1),
21             nn.ReLU(inplace=True),
22             nn.Conv2d(256, 256, kernel_size=3, padding=1),
23             nn.ReLU(inplace=True),
24 
25             nn.Conv2d(256, 512, kernel_size=3, padding=1),
26             nn.ReLU(inplace=True),
27             nn.Conv2d(512, 512, kernel_size=3, padding=1),
28             nn.ReLU(inplace=True),
29             nn.Conv2d(512, 512, kernel_size=3, padding=1),
30             nn.ReLU(inplace=True),
31 
32             nn.Conv2d(512, 512, kernel_size=3, padding=1),
33             nn.ReLU(inplace=True),
34             nn.Conv2d(512, 512, kernel_size=3, padding=1),
35             nn.ReLU(inplace=True),
36             nn.Conv2d(512, 512, kernel_size=3, padding=1),
37             nn.ReLU(inplace=True)
38         )
39 
40         self.classifier = nn.Sequential(
41             nn.Linear(512*7*7,4096),
42             nn.ReLU(inplace=True),
43             nn.Dropout(),
44 
45             nn.Linear(4096,4096),
46             nn.ReLU(True),
47             nn.Dropout(),
48 
49             nn.Linear(4096,num_classes)
50         )
51     def forward(self, x):
52         x = self.features(x),
53         x = x.view(x.size(0),-1)
54         x = self.classifier(x)
55         return x      

GoogLeNet

  GoogLeNet專注于加深網絡結構,與此同時引入了新的基本結構——Inception子產品,進而來增加網絡的寬度。GoogLeNet一共22層,它沒有全連接配接層,在2014年的比賽中獲得了冠軍。

  每個原始Inception子產品由previous layer、并行處理層及filter concatenation層組成,如圖1.5。并行處理層包含4個分支,即1*1卷積分支,3*3卷積分支,5*5卷積分支和3*3最大池化分支。一個關于原始Inception子產品的最大問題是,5*5卷積分支即使采用中等規模的卷積核個數,在計算代價上也可能是無法承受的。這個問題在混合池化層之後會更為突出,很快的出現計算量的暴漲。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

   圖1.5

  為了克服原始Inception子產品上的困難,GoogLeNet推出了一個新款,即采用1*1的卷積層來降低輸入層的次元,使網絡參數減少,是以減少網絡的複雜性,如圖1.6。是以得到降維Inception子產品,稱為inception V1。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

 圖1.6

  從GoogLeNet中明顯看出,共包含9個Inception V1子產品,如圖1.7所示。所有層均采用了ReLU激活函數。

 

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

 圖1.7

  自從2014年過後,Inception子產品不斷的改進,現在已發展到V4。GoogLeNet V2中的Inception參考VGGNet用兩個3*3核的卷積層代替了具有5*5核的卷積層,與此同時減少了一個輔助分類器,并引入了Batch Normalization(BN),它是一個非常有用的正則化方法。V3相對于V2的學習效率提升了很多倍,并且訓練時間大大縮短了。在ImageNet上的top-5錯誤率為4.8%。Inception V3通過改進V2得到,其核心思想是将一個較大的n*n的二維卷積拆成兩個較小的一維卷積n*1和1*n。Inception V3有三種不同的結構(Base的大小分别為35*35、17*17、8*8),如圖1.8所示,其中分支可能嵌套。GoogLeNet也隻用了一個輔助分類器,在ImageNet上top-5的錯誤率為3.5%。Inception V4是一種與Inception V3類似或更複雜的網絡子產品。V4在ImageNet上top-5的錯誤率為3.08%。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.8

  接下來利用pytorch實作GoogLeNet中的Inception V2子產品,其實整個GoogLeNet都是由Inception子產品構成的。

1 import torch.nn as nn
 2 import torch as torch
 3 import torch.nn.functional as F
 4 import torchvision.models.inception
 5 class BasicConv2d(nn.Module):
 6     def __init__(self,in_channels,out_channels,**kwargs):
 7         super(BasicConv2d,self).__init__()
 8         self.conv = nn.Conv2d(in_channels,out_channels,bias=False,**kwargs)
 9         self.bn = nn.BatchNorm2d(out_channels,eps=0.001)
10     def forward(self, x):
11         x = self.conv(x)
12         x = self.bn(x)
13         return F.relu(x,inplace=True)
14 
15 class Inception(nn.Module):
16     def __init__(self,in_channels,pool_features):
17         super(Inception,self).__init__()
18         self.branch1X1 = BasicConv2d(in_channels,64,kernel_size = 1)
19 
20         self.branch5X5_1 = BasicConv2d(in_channels,48,kernel_size = 1)
21         self.branch5X5_2 = BasicConv2d(48,64,kernel_size=5,padding = 2)
22 
23         self.branch3X3_1 = BasicConv2d(in_channels,64,kernel_size = 1)
24         self.branch3X3_2 = BasicConv2d(64,96,kernel_size = 3,padding = 1)
25         # self.branch3X3_2 = BasicConv2d(96, 96, kernel_size=1,padding = 1)
26 
27         self.branch_pool = BasicConv2d(in_channels,pool_features,kernel_size = 1)
28     def forward(self, x):
29         branch1X1 = self.branch1X1(x)
30 
31         branch5X5 = self.branch5X5_1(x)
32         branch5X5 = self.branch5X5_2(branch5X5)
33 
34         branch3X3 = self.branch3X3_1(x)
35         branch3X3 = self.branch3X3_2(branch3X3)
36 
37         branch_pool = F.avg_pool2d(x,kernel_size = 3,stride = 1,padding = 1)
38         branch_pool = self.branch_pool(branch_pool)
39 
40         outputs = [branch1X1,branch3X3,branch5X5,branch_pool]
41         return torch.cat(outputs,1)      

ResNet

  随着神經網絡的深度不斷的加深,梯度消失、梯度爆炸的問題會越來越嚴重,這也導緻了神經網絡的學習與訓練變得越來越困難。有些網絡在開始收斂時,可能出現退化問題,導緻準确率很快達到飽和,出現層次越深、錯誤率反而越高的現象。讓人驚訝的是,這不是過拟合的問題,僅僅是因為加深了網絡。這便有了ResNet的設計,ResNet在2015年的ImageNet競賽獲得了冠軍,由微軟研究院提出,通過殘差子產品能夠成功的訓練高達152層深的網絡,如圖1.10所示。

ReNet與普通殘差網絡不同之處在于,引入了跨層連接配接(shorcut connection),來構造出了殘差子產品。

  在一個殘差子產品中,一般跨層連接配接隻有跨2~3層,如圖1.9所示,但是不排除跨更多的層,跨一層的實驗效果不理想。在去掉跨連接配接層,用其輸出用H(x),當加入跨連接配接層時,F(x) 與H(x)存在關系:F(x):=H(x)-X,稱為殘差子產品。既可以用全連接配接層構造殘差子產品,也可以用卷積層構造殘差子產品。基于殘差子產品的網絡結構非常的深,其深度可達1000層以上。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.9

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.10

  用于ImageNet的5種深層殘差網絡結構,如圖1.11所示。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.11

  從何凱明的論文中也讀到plain-18、plain-34(即未加shotcut層)錯誤率比ResNet-18、ResNet-34(加了shotcut層)大了很多,如圖1.12所示。

Pytorch入門實戰二:LeNet、AleNet、VGG、GoogLeNet、ResNet模型詳解

圖1.12

  下面利用pytorch實作ReNet的殘差學習單元,此處參考了torchvision的model。

1 import torch.nn as nn
 2 def conv3x3(in_planes, out_planes, stride=1):
 3     """3x3 convolution with padding"""
 4     return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
 5                      padding=1, bias=False)
 6 class BasicBlock(nn.Module):
 7     expansion = 1
 8     def __init__(self, inplanes, planes, stride=1, downsample=None):
 9         super(BasicBlock, self).__init__()
10         self.conv1 = conv3x3(inplanes, planes, stride)
11         self.bn1 = nn.BatchNorm2d(planes)
12         self.relu = nn.ReLU(inplace=True)
13         self.conv2 = conv3x3(planes, planes)
14         self.bn2 = nn.BatchNorm2d(planes)
15         self.downsample = downsample
16         self.stride = stride
17 
18     def forward(self, x):
19         residual = x
20         out = self.conv1(x)
21         out = self.bn1(out)
22         out = self.relu(out)
23         out = self.conv2(out)
24         out = self.bn2(out)
25         if self.downsample is not None:
26             residual = self.downsample(x)
27         out += residual
28         out = self.relu(out)
29         return out      

當然,不管是LeNet,還是VGGNet,亦或是ResNet,這些經典的網絡結構,pytorch的torchvision的model中都已經實作,并且還有預訓練好的模型,可直接對模型進行微調便可使用。