天天看點

【圖像分類】一文帶你玩轉LeNet(pytorch)

目錄

摘要

1、 模型結構

各層參數詳解:

1、輸入層-INPUT

2、C1層-卷積層

3、S2層-池化層(下采樣層)

4、C3層-卷積層

5、S4層-池化層(下采樣層)

6、C5層-卷積層

7、F6層-全連接配接層

8、Output層-全連接配接層

各層參數總結

2、模型特性

代碼複現:

LeNet-5是由LeCun在1994年提出的一種用于識别手寫數字和機器印刷字元的卷積神經網絡(Convolutional Neural Network,CNN),是卷積神經網絡的開山之作,其命名來源于作者LeCun的名字,5則是其研究成果的代号,是LeNet的第五個版本。LeNet-5闡述了圖像中像素特征之間的相關性能夠由參數共享的卷積操作所提取,同時使用卷積、下采樣(池化)和非線性映射這樣的組合結構,是目前流行的大多數深度圖像識别網絡的基礎。

【圖像分類】一文帶你玩轉LeNet(pytorch)

如上圖所示,LeNet-5一共包含7層(輸入層不作為網絡結構),分别由2個卷積層、2個下采樣層和3個連接配接層組成。

首先是資料 INPUT 層,輸入圖像的尺寸統一ReSize為32*32。

注意:本層不算LeNet-5的網絡結構,傳統上,不将輸入層視為網絡層次結構之一。

卷積層的計算公式參考這篇文章:CNN基礎——卷積神經網絡的組成_AI浩-CSDN部落格

輸入圖檔:32×32

卷積核大小:5×5

卷積核種類:6

輸出featuremap大小:28×28 (32-5+1)=28

神經元數量:28×28×6

可訓練參數:(5×5+1) × 6(每個濾波器5×5=25個unit參數和一個bias參數,一共6個濾波器)

連接配接數:(5×5+1)×6×28×28=122304

詳細說明:對輸入圖像進行第一次卷積運算(使用 6 個大小為 5×5 的卷積核),得到6個C1特征圖(6個大小為28×28的 feature maps, 32-5+1=28)。我們再來看看需要多少個參數,卷積核的大小為5×5,總共就有6×(5×5+1)=156個參數,其中+1是表示一個核有一個bias。對于卷積層C1,C1内的每個像素都與輸入圖像中的5×5個像素和1個bias有連接配接,是以總共有156×28×28=122304個連接配接(connection)。有122304個連接配接,但是我們隻需要學習156個參數,主要是通過權值共享實作的。

輸入:28×28

采樣區域:2×2

采樣方式:4個輸入相加,乘以一個可訓練參數,再加上一個可訓練偏置。結果通過sigmoid

采樣種類:6

輸出featureMap大小:14×14(28/2)

神經元數量:14×14×6

連接配接數:(2×2+1)×6×14×14

S2中每個特征圖的大小是C1中特征圖大小的1/4。

詳細說明:第一次卷積之後緊接着就是池化運算,使用 2×2核 進行池化,于是得到了S2,6個14×14的 特征圖(28/2=14)。S2這個pooling層是對C1中的2×2區域内的像素求和乘以一個權值系數再加上一個偏置,然後将這個結果再做一次映射。同時有5x14x14x6=5880個連接配接。

輸入:S2中所有6個或者幾個特征map組合

卷積核種類:16

輸出featureMap大小:10×10 (14-5+1)=10

C3中的每個特征map是連接配接到S2中的所有6個或者幾個特征map的,表示本層的特征map是上一層提取到的特征map的不同組合

存在的一個方式是:C3的前6個特征圖以S2中3個相鄰的特征圖子集為輸入。接下來6個特征圖以S2中4個相鄰特征圖子集為輸入。然後的3個以不相鄰的4個特征圖子集為輸入。最後一個将S2中所有特征圖為輸入。

則:可訓練參數:6×(3×5×5+1)+6×(4×5×5+1)+3×(4×5×5+1)+1×(6×5×5+1)=1516

連接配接數:10×10×1516=151600

詳細說明:第一次池化之後是第二次卷積,第二次卷積的輸出是C3,16個10x10的特征圖,卷積核大小是 5×5. 我們知道S2 有6個 14×14 的特征圖,怎麼從6 個特征圖得到 16個特征圖了? 這裡是通過對S2 的特征圖特殊組合計算得到的16個特征圖。具體如下:

【圖像分類】一文帶你玩轉LeNet(pytorch)

C3的前6個feature map(對應上圖第一個紅框的6列)與S2層相連的3個feature map相連接配接(上圖第一個紅框),後面6個feature map與S2層相連的4個feature map相連接配接(上圖第二個紅框),後面3個feature map與S2層部分不相連的4個feature map相連接配接,最後一個與S2層的所有feature map相連。卷積核大小依然為5×5,是以總共有6×(3×5×5+1)+6×(4×5×5+1)+3×(4×5×5+1)+1×(6×5×5+1)=1516個參數。而圖像大小為10×10,是以共有151600個連接配接。

【圖像分類】一文帶你玩轉LeNet(pytorch)

C3與S2中前3個圖相連的卷積結構如下圖所示:

【圖像分類】一文帶你玩轉LeNet(pytorch)

上圖對應的參數為 3×5×5+1,一共進行6次卷積得到6個特征圖,是以有6×(3×5×5+1)參數。 為什麼采用上述這樣的組合了?論文中說有兩個原因:1)減少參數,2)這種不對稱的組合連接配接的方式有利于提取多種組合特征。

輸入:10×10

采樣種類:16

輸出featureMap大小:5×5(10/2)

神經元數量:5×5×16=400

連接配接數:16×(2×2+1)×5×5=2000

S4中每個特征圖的大小是C3中特征圖大小的1/4

詳細說明:S4是pooling層,視窗大小仍然是2×2,共計16個feature map,C3層的16個10x10的圖分别進行以2x2為機關的池化得到16個5x5的特征圖。有5x5x5x16=2000個連接配接。連接配接的方式與S2層類似。

輸入:S4層的全部16個單元特征map(與s4全相連)

卷積核種類:120

輸出featureMap大小:1×1(5-5+1)

可訓練參數/連接配接:120×(16×5×5+1)=48120

詳細說明:C5層是一個卷積層。由于S4層的16個圖的大小為5x5,與卷積核的大小相同,是以卷積後形成的圖的大小為1x1。這裡形成120個卷積結果。每個都與上一層的16個圖相連。是以共有(5x5x16+1)x120 = 48120個參數,同樣有48120個連接配接。C5層的網絡結構如下:

【圖像分類】一文帶你玩轉LeNet(pytorch)

輸入:c5 120維向量

計算方式:計算輸入向量和權重向量之間的點積,再加上一個偏置,結果通過sigmoid函數輸出。

可訓練參數:84×(120+1)=10164

詳細說明:6層是全連接配接層。F6層有84個節點,對應于一個7x12的比特圖,-1表示白色,1表示黑色,這樣每個符号的比特圖的黑白色就對應于一個編碼。該層的訓練參數和連接配接數是(120 + 1)x84=10164。F6層的連接配接方式如下:

【圖像分類】一文帶你玩轉LeNet(pytorch)

Output層也是全連接配接層,共有10個節點,分别代表數字0到9,且如果節點i的值為0,則網絡識别的結果是數字i。采用的是徑向基函數(RBF)的網絡連接配接方式。假設x是上一層的輸入,y是RBF的輸出,則RBF輸出的計算方式是:

【圖像分類】一文帶你玩轉LeNet(pytorch)

上式w_ij 的值由i的比特圖編碼确定,i從0到9,j取值從0到7*12-1。RBF輸出的值越接近于0,則越接近于i,即越接近于i的ASCII編碼圖,表示目前網絡輸入的識别結果是字元i。該層有84x10=840個參數和連接配接。

【圖像分類】一文帶你玩轉LeNet(pytorch)

上圖是LeNet-5識别數字3的過程。

網絡層 輸入尺寸 核尺寸 輸出尺寸 可訓練參數量

卷積層C_1 32×32×1 5×5×1/1,6 28×28×6 (5×5×1+1)×6

下采樣層S_2 28×28×6 2×2/2 14×14×6 (1+1)×6

卷積層C_3 14×14×6 5×5×6/1,16 10×10×16 1516

下采樣層S_4 10×10×16 2×2/2 5×5×16 (1+1)×16

卷積層C_5 5×5×16 5×5×16/1,120 1×1×120 (5×5×16+1)×120

全連接配接層F6 1×1×120 120×84 1×1×84 (120+1)×84

輸出層 1×1×84 84×10 1×1×10 (84+1)×10

卷積網絡使用一個3層的序列組合:卷積、下采樣(池化)、非線性映射(LeNet-5最重要的特性,奠定了目前深層卷積網絡的基礎)

使用卷積提取空間特征

使用映射的空間均值進行下采樣

使用tanh或sigmoid最終的分類器

層間的稀疏連接配接矩陣以避免巨大的計算開銷

import torch

from torch import nn

from torch.nn import functional as F

from torchsummary import summary

# 輸入大小為32x32

class LeNet(nn.Module):

   def __init__(self):

       super(LeNet, self).__init__()

       self.conv1 = nn.Conv2d(1, 6, 5)

       self.conv2 = nn.Conv2d(6, 16, 5)

       self.fc1 = nn.Linear(16 * 5 * 5, 120)

       self.fc2 = nn.Linear(120, 84)

       self.fc3 = nn.Linear(84, 10)

       self.pool = nn.MaxPool2d(2, 2)

   def forward(self, x):

       x = self.pool(F.relu(self.conv1(x)))

       x = self.pool(F.relu(self.conv2(x)))

       x = x.view(x.size()[0], -1)

       x = F.relu(self.fc1(x))

       x = F.relu(self.fc2(x))

       x = self.fc3(x)

       return x

if __name__ == '__main__':

   device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

   model = LeNet()

   model.to(device)

   summary(model, (1, 32, 32))

【圖像分類】一文帶你玩轉LeNet(pytorch)