LeNet-5 经典卷积网络模型浅析
基本的一些卷積基礎可以參考:CNN基礎知識(2),這里不再介紹
1. LeNet-5 卷積模型
LeNet-5 卷積模型可以達到大約99.2%的正確率,LeNet-5 模型總共有7層
下面詳細介紹LeNet-5 模型每一層的結構。
第一層,卷積層
這一層的輸入是原始的圖像像素,LeNet-5 模型接受的輸入層大小是32x32x1。第一卷積層的過濾器的尺寸是5x5,深度(卷積核種類)為6,不使用全0填充,步長為1。因為沒有使用全0填充,所以這一層的輸出的尺寸為32-5+1=28,深度為6。這一層卷積層參數個數是5x5x1x6+6=156個參數(可訓練參數),其中6個為偏置項參數。因為下一層的節點矩陣有有28x28x6=4704個節點(神經元數量),每個節點和5x5=25個當前層節點相連,所以本層卷積層總共有28x28x6x(5x5+1)個連接。
第二層,池化層
這一層的輸入是第一層的輸出,是一個28x28x6=4704的節點矩陣。本層采用的過濾器為2x2的大小,長和寬的步長均為2,所以本層的輸出矩陣大小為14x14x6。原始的LeNet-5 模型中使用的過濾器和這里將用到的過濾器有些許的差別,這里不過多介紹。
第三層,卷積層
本層的輸入矩陣大小為14x14x6,使用的過濾器大小為5x5,深度為16。本層不使用全0填充,步長為1。本層的輸出矩陣大小為10x10x16。按照標準卷積層本層應該有5x5x6x16+16=2416個參數(可訓練參數),10x10x16x(5x5+1)=41600個連接。
第四層,池化層
本層的輸入矩陣大小是10x10x16,采用的過濾器大小是2x2,步長為2,本層的輸出矩陣大小為5x5x16。
第五層,全連接層
本層的輸入矩陣大小為5x5x16。如果將此矩陣中的節點拉成一個向量,那么這就和全連接層的輸入一樣了。本層的輸出節點個數為120,總共有5x5x16x120+120=48120個參數。
第六層,全連接層
本層的輸入節點個數為120個,輸出節點個數為84個,總共參數為120x84+84=10164個。
第七層,全連接層
LeNet-5 模型中最后一層輸出層的結構和全連接層的結構有區別,但這里我們用全連接層近似的表示。本層的輸入節點為84個,輸出節點個數為10個,總共有參數84x10+10=850個。
2.Tensorflow訓練類似LeNet-5 模型的卷積網絡來解決MNIST數字識別問題
(1)前向傳播過程:LeNet5_infernece.py
# coding: utf-8# In[2]:import tensorflow as tf# #### 1. 設定神經網絡的參數# In[ ]:INPUT_NODE = 784 #輸入層的節點數,這里是圖片的像素 OUTPUT_NODE = 10 # 輸出層的節點數,這里是類別的數目IMAGE_SIZE = 28 # 圖片大小 NUM_CHANNELS = 1 # 數字通道 NUM_LABELS = 10 # 數字類別# 第一層卷積層的尺寸和深度 CONV1_DEEP = 32 CONV1_SIZE = 5 # 第二層卷積層的尺寸和深度 CONV2_DEEP = 64 CONV2_SIZE = 5 # 全連接層的節點個數 FC_SIZE = 512# #### 2. 定義前向傳播的過程# In[ ]:# 這里添加了一個新的參數train,用于區分訓練過程和測試過程。在這個過程中將用到dropout方法,該方法可以進一步提升 # 模型的可靠性并防止過擬合,dropout過程只在訓練過程中使用。 def inference(input_tensor, train, regularizer):# 聲明第一層卷積層的變量并實現前向傳播過程。通過使用不同的命名空間來隔離不同層的變量,這可以讓每一層中的# 變量命名只需要考慮在當前層的作用,而不用擔心重名的問題。和標準的LeNet-5模型不大一樣,這里定義的卷積層輸入# 為28x28x1的原始MNIST圖片像素。因為卷積層中使用了全0填充,所以輸出為28x28x32的矩陣。with tf.variable_scope('layer1-conv1'): # 在名字為layer1-conv1的命名空間內創建名字為weight的變量conv1_weights = tf.get_variable("weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],initializer=tf.truncated_normal_initializer(stddev=0.1)) # 初始化滿足正太分布的截隨機數conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer=tf.constant_initializer(0.0))# 使用邊長為5,深度為32的過濾器,過濾器移動的步長為1,且使用全0填充。tf.nn.conv2d函數的第一個輸入為當前# 層的節點矩陣(這個矩陣是四維矩陣,第一維對應的是輸入一個batch,后面三維對應一個節點矩陣)。第二個參數# 提供了卷積層的權重,第三個參數為不同維度上的步長(雖然提供的是一個長度為4的數組,但第一維和最后一維的數字一定要是1# 這是因為卷積層的步長只對矩陣的長和寬有效)。最后一個參數是填充,其中'SAME'表示全0填充,'VALLD'表示不添加。conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')# tf.nn.bias_add提供了一個方便的函數給每一個節點加上偏置項,注意這里不能直接使用加法,因為矩陣上的不同位置# 上的節點都需要加上同樣的偏置項。然后再通過RELU激活函數完成去線性化。relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))# 實現第二層池化層的前向層的傳播過程。這里用的是最大池化層,池化層過濾器的邊長為2,使用全0填充,并且移動的步長# 為2.這一層的輸入是上一層的輸出。也就是輸入為28x28x32的矩陣,輸出為14x14x32的矩陣。with tf.name_scope("layer2-pool1"):pool1 = tf.nn.max_pool(relu1, ksize = [1,2,2,1],strides=[1,2,2,1],padding="SAME")# 聲明第三層卷積層的變量并實現前向傳播過程。這一層的輸入為14x14x32的矩陣,輸出為14x14x64的矩陣。with tf.variable_scope("layer3-conv2"):conv2_weights = tf.get_variable("weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],initializer=tf.truncated_normal_initializer(stddev=0.1))conv2_biases = tf.get_variable("bias", [CONV2_DEEP], initializer=tf.constant_initializer(0.0))# 使用邊長為5,深度為64的過濾器,過濾器移動的步長為1,且使用全0填充。conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))# 實現第四層池化層的前向傳播過程。這一層和第二層的結構是一樣的。這一層的輸入為14x14x64的矩陣,輸出為7x7x64# 的矩陣。with tf.name_scope("layer4-pool2"):pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')# 將第四層池化層的輸出轉化為第五層全連接層的輸入格式。第四層的輸出為7x7x64的矩陣,然而第五層全連接層的# 需要的輸入格式為向量,所以這里需要將這個7x7x64的矩陣拉直成一個向量。pool2.get_shape函數可以得到第四層# 輸出矩陣的維度而不需要自己去計算,注意因為每一層神經網絡的輸入輸出都為一個batch的矩陣,所以這里得到的維度# 也包含了一個batch中數據的個數。pool_shape = pool2.get_shape().as_list()# 計算將矩陣拉直成向量之后的長度,這個長度就是矩陣長寬及深度的乘積。注意這里pool_shape[0]# 為一個batch中數據的個數。nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]# 通過tf.reshape函數將第四層的輸出變成一個batch的向量。reshaped = tf.reshape(pool2, [pool_shape[0], nodes])# 聲明第五層全連接層的變量并實現前向傳播過程。這一層的輸入是拉直的一組向量,向量長度為3136,輸出是一組長度為# 512的向量。這里引入dropout的概念。dropout在訓練時會隨機將部分節點的輸出改為0,dropout可以避免過擬合問題從而# 可以使得模型在測試數據上的效果更好。dropout一般只在全連接層使用而不是卷積層或者池化層使用。with tf.variable_scope('layer5-fc1'):fc1_weights = tf.get_variable("weight", [nodes, FC_SIZE],initializer=tf.truncated_normal_initializer(stddev=0.1))# 只有全連接層的權重需要加入正則化if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))fc1_biases = tf.get_variable("bias", [FC_SIZE], initializer=tf.constant_initializer(0.1))# 通過RELU激活函數完成去線性化。其中tf.matmul實現的是矩陣乘法fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)if train: fc1 = tf.nn.dropout(fc1, 0.5)# 聲明第六層全連接層的變量并實現前向傳播過程。這一層的輸入為一組長為512的向量,輸出為一組長為10的向量。# 這一層的輸出通過softmax之后就得到了最后的分類結果。with tf.variable_scope('layer6-fc2'):fc2_weights = tf.get_variable("weight", [FC_SIZE, NUM_LABELS],initializer=tf.truncated_normal_initializer(stddev=0.1))if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))fc2_biases = tf.get_variable("bias", [NUM_LABELS], initializer=tf.constant_initializer(0.1))logit = tf.matmul(fc1, fc2_weights) + fc2_biases# 返回第六層的輸出return logit(2)訓練過程
# coding: utf-8# In[ ]:import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import LeNet5_infernece import os import numpy as np# #### 1. 定義神經網絡相關的參數# In[ ]:BATCH_SIZE = 100 LEARNING_RATE_BASE = 0.01 LEARNING_RATE_DECAY = 0.99 REGULARIZATION_RATE = 0.0001 TRAINING_STEPS = 6000 MOVING_AVERAGE_DECAY = 0.99# #### 2. 定義訓練過程# In[ ]:# 調整輸入數據placeholder的格式,輸入為一個四維的矩陣 def train(mnist):# 定義輸入為4維矩陣的placeholderx = tf.placeholder(tf.float32, [ BATCH_SIZE, # 第一維表示一個batch中樣例的個數 LeNet5_infernece.IMAGE_SIZE, # 第二維和第三維表示圖片的尺寸LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.NUM_CHANNELS], # 第四維表示圖片的深度,對于RGB格式的圖片深度是5name='x-input')# 定義輸出的placeholdery_ = tf.placeholder(tf.float32, [None, LeNet5_infernece.OUTPUT_NODE], name='y-input')regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)# 直接使用LeNet5_infernece.py中定義的前向傳播過程y = LeNet5_infernece.inference(x,False,regularizer)global_step = tf.Variable(0, trainable=False)# 定義損失函數、學習率、滑動平均操作以及訓練過程。variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)variables_averages_op = variable_averages.apply(tf.trainable_variables())cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))cross_entropy_mean = tf.reduce_mean(cross_entropy)loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,global_step,mnist.train.num_examples / BATCH_SIZE, LEARNING_RATE_DECAY,staircase=True)train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)with tf.control_dependencies([train_step, variables_averages_op]):train_op = tf.no_op(name='train')# 初始化TensorFlow持久化類。saver = tf.train.Saver()with tf.Session() as sess:tf.global_variables_initializer().run()# 在訓練過程中不再測試模型在驗證數據上的表現,驗證和測試的過程將會有一個獨立的程序來完成。for i in range(TRAINING_STEPS):xs, ys = mnist.train.next_batch(BATCH_SIZE)reshaped_xs = np.reshape(xs, (BATCH_SIZE,LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.IMAGE_SIZE,LeNet5_infernece.NUM_CHANNELS))_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: reshaped_xs, y_: ys})# 每1000輪輸出一次當前的訓練情況if i % 100 == 0:print("After %d training step(s), loss on training batch is %g." % (step, loss_value))# #### 3. 主程序入口# In[ ]:def main(argv=None):mnist = input_data.read_data_sets(r"C:\Users\LiLong\Desktop\MNIST_datasets", one_hot=True)train(mnist)if __name__ == '__main__':main()(3)運行結果:
Extracting C:\Users\LiLong\Desktop\MNIST_datasets\train-images-idx3-ubyte.gz Extracting C:\Users\LiLong\Desktop\MNIST_datasets\train-labels-idx1-ubyte.gz Extracting C:\Users\LiLong\Desktop\MNIST_datasets\t10k-images-idx3-ubyte.gz Extracting C:\Users\LiLong\Desktop\MNIST_datasets\t10k-labels-idx1-ubyte.gz After 1 training step(s), loss on training batch is 3.83871. After 101 training step(s), loss on training batch is 0.869112. After 201 training step(s), loss on training batch is 0.876754. After 301 training step(s), loss on training batch is 0.804194. After 401 training step(s), loss on training batch is 0.890419. After 501 training step(s), loss on training batch is 0.774354. After 601 training step(s), loss on training batch is 0.815839. After 701 training step(s), loss on training batch is 0.689843. After 801 training step(s), loss on training batch is 0.772164. After 901 training step(s), loss on training batch is 0.776492. After 1001 training step(s), loss on training batch is 0.699668. After 1101 training step(s), loss on training batch is 0.8183. After 1201 training step(s), loss on training batch is 0.751548. After 1301 training step(s), loss on training batch is 0.688748. After 1401 training step(s), loss on training batch is 0.653208. After 1501 training step(s), loss on training batch is 0.678878. ...由于程序運行太慢,我把打印輸出改成了100次打印一次訓練的損失結果。這里只是訓練模型,沒有在MNIST上測試,但有實驗表明該模型的效果很好,可達到99.4%的正確率。
3. 總結
然而每一總網絡都不可能解決所有的問題。但有一個通用的用于圖片分類的卷積神經網絡架構:輸入層—>(卷積層+—>池化層?)+—>全連接層+
其中“+”表示一個或多個,“?”表示沒有或者一個。
有了卷積網絡的架構,那么每一層卷積層或者池化層中的配置需要如何設置呢?其實是沒有一個標準的,經驗總結是這樣的:
- 卷積層的過濾器邊長不會超過5,一般為3或者1,但也有些卷積神經網絡中處理輸入的卷積層中使用了邊長為7,甚至為11的過濾器。
- 過濾器的深度大部分卷積神經網絡都采用逐層遞增的方式。卷積層的步長一般為1,但是在有些模型中也會使用2,或者3作為步長。
- 池化層的配置相對簡單些,用的最多的是最大池化層,池化層的過濾器邊長一般為2或者3,步長也一般為2或者3。
參考:《Tensorflow實戰Google深度學習框架》
總結
以上是生活随笔為你收集整理的LeNet-5 经典卷积网络模型浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 摩托罗拉发布手机 延续经典翻盖设计
- 下一篇: java的数据类型、变量类型笔记总结