原先規劃在今明兩天分享LeNet原始論文,但一看居然有47頁@@,兩天下班時段共約六小時,實在會理解不及,決定透過統整網路資訊,先對LeNet這個模型作簡介和Pytorch code理解,論文本身周末在好好統整囉~ (下班騎車路上,突然覺得每天都直接介紹今日目標,實在太生硬單調了,決定之後都來點前言XD
1.Lenet模型_理論學習1st
起源: 由LeCun等人於1998年的LeNet原始論文: Gradient-Based Learning Applied
to Document Recognition所提出,該論文將LeNet應用在光學字元辨識
(Optical character recognition, OCR)和文件中的字體識別(character recognition)。
選擇LeNet模型介紹的原因: 架構簡單且直覺,適合初學入門。
C1、S2、C3、S4、C5、F6、OUTPUT
,其中字母代表神經層種類(C:卷積層、S:池化層、F:全連接層),數字代表層數為第幾層。(0)輸入層(Input Layer)
: 32x32的圖片,通道數為1。(1)C1
: 使用6個5x5的卷積核,且padding=0 & stride=1,會得到28x28的輸出特徵圖(feature map),公式為width - kernel_width +1
(32-5+1=28)。(2)S2
: 降採樣(downsampling,選用最大池化max pooling: 圖片將會縮小到原來的一半),使用6個2x2池化,且padding=0 & stride=2,得到6個14x14的輸出特徵圖,28/2=14。(3)C3
: 使用16個5x5的卷積核,且padding=0 & stride=1,得到16個10x10的輸出特徵圖,14-5+1=10。(4)S4
: 16個2x2池化,且padding=0 & stride=2,得到16個5x5的輸出特徵圖,10/2=5。(5)C5
: 使用120個5x5x16的卷積核,且padding=0 & stride=1,得到120個1x1的輸出特徵圖,5-5+1=1 等同於120個神經元的全連接層。(6)F6
: 84個神經元全連接層,每個神經元都將與前一層C5進行全連接,並接上sigmoid函數。(7)OUTPUT
: 全連接層且為Gaussian Connection,採用RBF函數(徑向歐式距離函數),計算輸入向量和參數向量之間的歐式距離。因為LeNet應用於辨識手寫圖像,數字為0~9共10類,所以輸出層為10個神經元。# 方法一: 建立nn.Module子類別
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(1, 6, 5) # C1: 6個5x5大小的卷積核
self.relu = nn.ReLU()
self.maxpool1 = nn.MaxPool2d(2, 2) # S2: 6個2x2池化
self.conv2 = nn.Conv2d(6, 16, 5) # C3: 16個5x5的卷積核
self.maxpool2 = nn.MaxPool2d(2, 2) # S4: 16個2x2池化
self.fc1 = nn.Linear(16*5*5, 120) # C5: 120個5x5x16的卷積核
self.fc2 = nn.Linear(120, 84) # F6: 84個神經元全連接層
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.conv1(x)
x = self.relu(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = x.view(-1, 16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
output = F.log_softmax(x, dim=1) # 多分類問題,現今多使用softmax
return output
# 方法二: Sequential組裝
class LeNet5(nn.Module):
def __init__(self, num_classes):
super(ConvNeuralNet, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, stride=1, padding=0),
nn.ReLU(),
nn.MaxPool2d(kernel_size = 2, stride = 2))
self.layer2 = nn.Sequential(
nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0),
nn.ReLU(),
nn.MaxPool2d(kernel_size = 2, stride = 2))
self.fc1 = nn.Linear(16*5*5, 120)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(120, 84)
self.relu1 = nn.ReLU()
self.fc3 = nn.Linear(84, num_classes)
self.log_softmax = nn.LogSoftmax()
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = out.reshape(x.size(0), -1)
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.relu1(x)
x = self.fc3(x)
output = self.log_softmax(x, dim=1)
return out
參考:
心得小語:
能透過鐵人賽強迫自己每天都要有新的學習和吸收,覺得其實是挺好的方式~ 原本要趁周末偷跑進度,結果只有多一天的進度(傻眼XD 只能每天腳踏實地努力囉哈哈
今日工時: 50mins*3
有本事任性的人(任性追夢),也要有本事堅強(堅毅前進)
Who is able to be egotistical needs to be strong too.