1. LeNet神經網絡介紹
LeNet神經網絡由深度學習三巨頭之一的Yan LeCun提出,他同時也是卷積神經網絡 (CNN,Convolutional Neural Networks)之父。LeNet主要用來進行手寫字元的識别與分類,并在美國的銀行中投入了使用。LeNet的實作确立了CNN的結構,現在神經網絡中的許多内容在LeNet的網絡結構中都能看到,例如卷積層,Pooling層,ReLU層。雖然LeNet早在20世紀90年代就已經提出了,但由于當時缺乏大規模的訓練資料,計算機硬體的性能也較低,是以LeNet神經網絡在處理複雜問題時效果并不理想。雖然LeNet網絡結構比較簡單,但是剛好适合神經網絡的入門學習。
2. LeNet神經網絡結構
LeNet的神經網絡結構圖如下:
LeNet網絡的執行流程圖如下:
2.1 LeNet第一層(卷積運算)
接下來我們來具體的一層層的分析LeNet的網絡結構。首先要了解圖像(輸入資料)的表示。在LeNet網絡中,輸入圖像是手寫字元,圖像的表示形式為二維資料矩陣,如下圖所示:
LeNet網絡除去輸入輸出層總共有六層網絡。第一層是卷積層(C1層),卷積核的大小為
5\*5
,卷積核數量為
6
個,輸入圖像的大小為
32*32
,是以輸入資料在進行第一層卷積之後,輸出結果為大小為
28*28
,數量為
6
個的feature map。卷積操作如下面兩幅圖所示:
卷積操作的過程可描述為:卷積核在圖像上滑動,滑動步長為1(即每次移動一格,水準方向從左到右,到最右邊之後再從最左邊開始,向下移動一格,重複從左到右滑動),當卷積核與圖像的一個局部塊重合時進行卷積運作,卷積計算方式為圖像塊對應位置的數與卷積核對應位置的數相乘,然後将所有相乘結果相加即為feature map的值,相乘累加之後的結果位于卷積核中心點的位置,是以如果是
3\*3
的卷積核,feature map比原圖像在水準和垂直方向上分别減少兩行(上下各一行)和兩列(左右各一列),是以上面圖像原圖為
5*5
,卷積核為
3\*3
,卷積結果大小為
3*3
,即
(5-2)*(5-2)
,如果卷積核為
5*5
,則卷積結果大小為
(5-4)*(5-4)
。上圖中的卷積核為:
由于神經網絡層與層的結構是通過連接配接來實作的,是以輸入層與第一個卷積層的連接配接數量應為
(32-2-2)\*(32-2-2)\*(5\*5+1)\*6= 28\*28\*156 =122304
。
卷積的作用主要是:通過卷積運算,可以使原信号特征增強,并且降低噪音。在圖像上卷積之後主要是減少圖像噪聲,提取圖像的特征。例如sobel算子就是一種卷積運算,主要是提取圖像的邊緣特征。卷積網絡能很好地适應圖像的平移不變性:例如稍稍移動一幅貓的圖像,它仍然是一幅貓的圖像。卷積操作保留了圖像塊之間的空間資訊,進行卷積操作的圖像塊之間的相對位置關系沒有改變。圖像在不同卷積核上進行卷積之後的效果圖如下:
2.2 LeNet第二層(pooling運算)
圖像在LeNet網絡上進行第一層卷積之後,結果為大小為
28*28
,數量為
6
個的feature map。LeNet網絡的第二層為pooling層(S2層),也稱為下采樣。在圖像進行中,下采樣之後,圖像的大小會變為原來的
1/4
,即水準方向和垂直方向上圖像大小分别減半。Pooling有多種,這裡主要介紹兩種,max-pooling和average-pooling。max-pooling即為從四個元素中選取一個最大的來表示這四個元素,average-pooling則用四個元素的平均值來表示這四個元素。Pooling示意圖如下:
在LeNet在進行第二層Pooling運算後,輸出結果為
14*14
的
6
個feature map。其連接配接數為
(2*2+1) * 14 * 14 *6 = 5880
。Pooling層的主要作用就是減少資料,降低資料緯度的同時保留最重要的資訊。在資料減少後,可以減少神經網絡的緯度和計算量,可以防止參數太多過拟合。LeNet在這一層是将四個元素相加,然後乘以參數w再加上偏置b,然後計算sigmoid值。
2.3 LeNet第三層(卷積運算)
LeNet第三層(C3層)也是卷積層,卷積核大小仍為
5*5
,不過卷積核的數量變為
16
個。第三層的輸入為
14*14
的
6
個feature map,卷積核大小為
5*5
,是以卷積之後輸出的feature map大小為
10*10
,由于卷積核有
16
個,是以希望輸出的feature map也為
16
個,但由于輸入有
6
個feature map,是以需要進行額外的處理。輸入的
6
個feature map與輸出的
16
個feature map的關系圖如下:
如上圖所示,第一個卷積核處理前三幅輸入的feature map,得出一個新的feature map。
2.4 LeNet第四層(Pooling運算)
上一層卷積運算之後,結果為大小為
10*10
的
16
個feature map,是以在第四層(S4層)進行pooling運算之後,輸出結果為
16
個大小為
5*5
的feature map。與S2層進行同樣的操作。
2.5 LeNet第五層
LeNet第五層是卷積層(C5層),卷積核數目為120個,大小為
5*5
,由于第四層輸出的feature map大小為
5*5
,是以第五層也可以看成全連接配接層,輸出為120個大小為
1*1
的feature map。
2.6 LeNet第六層
LeNet第六層是全連接配接層(F6層),有84個神經元(84與輸出層的設計有關),與C5層全連接配接。
2.7 LeNet各層的參數變化
-
C1
輸入大小:32*32
核大小:5*5
核數目:6
輸出大小:28*28*6
訓練參數數目:(5*5+1)*6=156
連接配接數:(5*5+1)*6*(32-2-2)*(32-2-2)=122304
-
S2
輸入大小:28*28*6
核大小:2*2
核數目:1
輸出大小:14*14*6
訓練參數數目:2*6=12,2=(w,b)
連接配接數:(2*2+1)*1*14*14*6 = 5880
-
C3
輸入大小:14*14*6
核大小:5*5
核數目:16
輸出大小:10*10*16
訓練參數數目:6*(3*5*5+1) + 6*(4*5*5+1) + 3*(4*5*5+1) + 1*(6*5*5+1)=1516
連接配接數:(6*(3*5*5+1) + 6*(4*5*5+1) + 3*(4*5*5+1) + 1*(6*5*5+1))*10*10=151600
-
S4
輸入大小:10*10*16
核大小:2*2
核數目:1
輸出大小:5*5*16
訓練參數數目:2*16=32
連接配接數:(2*2+1)*1*5*5*16=2000
-
C5
輸入大小:5*5*16
核大小:5*5
核數目:120
輸出大小:120*1*1
訓練參數數目:(5*5*16+1)*120*1*1=48120(因為是全連接配接)
連接配接數:(5*5*16+1)*120*1*1=48120
-
F6
輸入大小:120
輸出大小:84
訓練參數數目:(120+1)*84=10164
連接配接數:(120+1)*84=10164
3. LeNet在Caffe中的配置
LeNet神經網絡結構在Caffe中的配置檔案如下:
name: "LeNet" //神經網絡名字
//本層隻有top,沒有bottom,說明是資料輸入層
layer {
name: "mnist" //layer名字
type: "Data" //層的資料類型,如果是Data,說明是leveldb或lmdb
top: "data" //top表示輸入資料,類型為data
top: "label" //top表示輸入資料,類型為label,(data,label)配對是分類模型所必需的
//一般訓練的時候和測試的時候,模型的層是不一樣的。該層(layer)是屬于訓練階段的層,還是屬于測試階段的層,需要用include來指定。如果沒有include參數,則表示該層既在訓練模型中,又在測試模型中。
include {
phase: TRAIN
}
//資料的預處理,可以将資料變換到定義的範圍内。如設定scale為0.00390625,實際上就是1/255, 即将輸入資料由0-255歸一化到0-1之間
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/mnist/mnist_train_lmdb"
batch_size: 64
backend: LMDB
}
}
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/mnist/mnist_test_lmdb"
batch_size: 100
backend: LMDB
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "ip1"
type: "InnerProduct"
bottom: "pool2"
top: "ip1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
複制