一、 前言
網絡有5層(不考慮沒有參數的層,所以是LeNet-5),包含3個卷積層,2個池化層,2個全連接層,No padding。
LeNet-5(Gradient-Based Learning Applied to Document Recognition)是
1998年提出的
專注於銀行手寫體識別的卷積神經網絡。因此,
輸入是單通道的灰度圖像, 圖像
分辨率不高。 當時人們
不使用padding,共
包含60k個params。
二、 LeNet-5網絡的基本結構

LeNet-5網絡規模不大,主要包含卷積層,池化層,全連接層,
是深度學習模型的基礎。
三、 網絡結構詳述:
1. Input層
輸入的是灰度圖像,輸入圖像的尺寸為32*32*1。
2. C1層 - 卷積層
- 輸入尺寸: 32*32*1
- 卷積核大小: 5*5*1
- 卷積核個數:6
- 可訓練參數: (5*5+1)*6 = 156
- 輸出feature map大小: 28*28*6
3. S2層 - 池化層 用於降采樣upsample
- 輸入尺寸: 28*28*6;
- pool濾波器大小:2*2;
- 池化方式:4個輸入相加,乘以一個可訓練權重,再加上一個可訓練偏執bias; ----帶有參數的池化方式(與現在的池化方式有所不同,近期如果使用LeNet-5,均是使用現在的池化方式進行處理)
- 濾波器個數:6
- 可訓練參數:(1+1)*6 = 12 --- 帶參數的池化
- 輸出feature map大小: 14*14*6

4. C3層 - 卷積層
- 輸入尺寸: 14*14*6
- 卷積核大小: 5*5
- 卷積核個數:16
- 它是一種不對稱的組合方式。即不是16個 5*5*6 的卷積核 --- 近期使用LeNet-5,此處使用5*5*6的卷積核16個。
-
- 有6個 5*5*3 的卷積核 生成6個feature_map -- 第一個紅框
- 有6個 5*5*4 的卷積核 生成6個feature_map -- 第二個紅框
- 有3個 5*5*4 的卷積核 生成3個feature_map -- 第三個紅框
- 有1個 5*5*6 的卷積核 生成1個feature_map -- 第四個紅框
每個卷積核均有一個bias,即有(5x5x3+1)x6 + (5x5x4 + 1) x 3 + (5x5x4 +1)x6 + (5x5x6+1)x1 =1516 個訓練參數。 ----10*10*16
- 可訓練參數:1516
- 輸出feature map 大小:10*10*16

上圖解析: 行號是輸入feature map的6個通道,列號是輸出feature map的16個通道。(也是每個卷積核作用的結果),X表示卷積核分別作用在輸入feature map的指定通道上,可以看出,每一組卷積核均會將所有輸入通道數量遍歷一遍。
5. S4層 - 池化層
- 輸入尺寸:10*10*16;
- pool濾波器大小:2*2; pool方式與S2相同
- 濾波器個數:16
- 可訓練參數個數: (1+1)*16 = 32
- 輸出feature map大小:5*5*16
6. C5層 - 卷積層
- 輸入尺寸: 5*5*16
- 卷積核大小:5*5
- 卷積核個數:120
- 可訓練參數個數: (5*5+1)*120 = 3120
- 輸出尺寸大小: 1*1*120
7. F6層 - 全連接層
-
- 輸入神經元個數: 120
- 輸出神經元個數: 84
- 可訓練參數:84*(120+1) = 10164


8. F7層 - 全連接層 output層
- 神經元個數: 10 (10個數字)
- 可訓練參數個數:84*(10+1) = 924
- the output layer is composed of Euclidean Radial Basis Fuction units(RBF).

上式w_ij 的值由i的比特圖編碼確定,i從0到9,j取值從0到7*12-1。RBF輸出的值越接近於0,則越接近於i,即越接近於i的ASCII編碼圖,表示當前網絡輸入的識別結果是字符i。

該論文原版中,池化后采用的是sigmoid激活函數。
四、 總結
LeNet-5是一種用於
手寫體識別的非常
高效的卷積神經網絡。其
結構簡單,參數量較少。
現在代碼的實現與論文描述的不同之處體現為以下四點:
- 卷積方式: 不采用論文中描述的不對稱卷積;
- 池化方式:采用無參的池化方式;
- 激活函數:池化后激活函數由sigmoid 變為ReLU
- 輸出層會添加softmax激活函數
1 def model(self, is_trained=True): 2 with slim.arg_scope([slim.conv2d, slim.fully_connected], 3 weights_initializer=tf.truncated_normal_initializer(stddev=0.01), 4 weights_regularizer=slim.l2_regularizer(0.005), 5 biases_initializer=tf.constant_initializer(0)): 6 with slim.arg_scope([slim.conv2d], padding="valid"): 7 net = slim.conv2d(self.input_image, 6, kernel_size=[5, 5], stride=1, scope="conv_1") 8 net = slim.max_pool2d(net, [2, 2], 2, scope="pool_2") 9 net = slim.conv2d(net, 16, kernel_size=[5, 5], stride=1, scope="conv_3") 10 net = slim.max_pool2d(net, [2, 2], 2, scope="pool_4") 11 net = slim.conv2d(net, 120, kernel_size=[5, 5], stride=1, scope="conv_5") 12 net = slim.flatten(net, scope='flatten') 13 net = slim.fully_connected(net, 84, activation_fn=tf.nn.relu6, scope="fc_6") 14 net = slim.dropout(net, self.keep_prob, is_training=is_trained, scope='dropout') 15 digits = slim.fully_connected(net, 11, activation_fn=tf.nn.softmax, scope="fc_7") 16 return digits