CNN-1: LeNet-5 卷積神經網絡模型


1、LeNet-5模型簡介

LeNet-5 模型Yann LeCun 教授於 1998 年在論文 Gradient-based learning applied to document         
recognitionr [1] 中提出的,它是第個成功應用於數字識別問題的卷積神經網絡。在 MNIST 數據集
上, LeNet-5 模型可以達到大約 99.2%的正確率。

2、LeNet-5模型結構

LeNet-5 模型總共有 7 層 ,下圖展示了 LeNet-5 模型的架構 。 

 

下面總結 LeNet-5 模型每一層的結構。

第一層: 卷積層

輸入: 原始的圖像像素矩陣(長、寬、色彩), 大小為 32*32*1。

卷積層參數: 過濾器尺寸為 5*5,深度為 6,不使用全 0 填充,步長為1。

輸出:大小為 28*28*6。

分析:因為沒有使用全 0 填充,所以這一層輸出尺寸為 32-5+1=28, 深度為 6;

        該卷積層共有 5*5*1*6+6=156 個參數,其中 6個為偏置項參數;

        因為下一層的節點矩陣有 28*28*6=4704 個節點,每個節點和 5*5=25 個當前層節點相連,

        所以本層卷積層共有 4704*(25+1)=122304 個連接。

第二層: 池化層

輸入: 大小為 28*28*6。

池化層參數: 過濾器尺寸為 2*2,長和寬的步長均為2。

輸出: 大小為 14*14*6

分析: 6 個特征圖共包含 6*2=12 個可訓練參數,以及 14*14*6*(2*2+1)=5880 個連接。

第三層: 卷積層

輸入: 大小為 14*14*6。

卷積層參數: 過濾器尺寸為 5*5,深度為 16,不使用全 0 填充,步長為1。

輸出:大小為 10*10*16。

分析:因為沒有使用全 0 填充,所以這一層輸出尺寸為 14-5+1=10, 深度為 16;

        該卷積層共有 5*5*6*16+16=2416 個參數,其中 16個為偏置項參數;

        因為下一層的節點矩陣有 10*10*16=1600 個節點,每個節點和 5*5=25 個當前層節點相連,

        所以本層卷積層共有 1600*(25+1)=41600 個連接。

第四層: 池化層

輸入: 大小為 10*10*16。

池化層參數: 過濾器尺寸為 2*2,長和寬的步長均為2。

輸出: 大小為 5*5*16。

分析: 16 個特征圖共包含 16*2=32 個可訓練參數,以及 5*5*16*(2*2+1)=2000個連接。

第五層: 全連接層

輸入節點個數: 5*5*16=400。

參數個數: 5*5*16*120+120=48120 

輸出節點個數: 120。

第六層: 全連接層

輸入節點個數: 120。

參數個數: 120*84+84=10164 

輸出節點個數: 84。

第七層: 全連接層

輸入節點個數: 84。

參數個數: 84*10+10=850 

輸出節點個數: 10。

 3、LeNet-5模型TensorFlow實現MNIST數字識別

開發環境: Python - 3.0、TensorFlow - 1.4.0、無GPU

訓練主文件:LeNet5_train.py

# -*- coding: utf-8 -*-
"""
Created on 2017
《TensorFlow 實戰Google深度學習框架 第2版 ,鄭澤宇、梁博文等》
@author: 鄭澤宇、梁博文等
"""
#見書 p151
#6.4.1 經典卷積神經網絡模型——LeNet-5模型

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import LeNet5_infernece
import os
import numpy as np

BATCH_SIZE = 100     #每次選取batch_size個樣本進行訓練
TRAINING_STEPS = 6000   #訓練次數
LEARNING_RATE_BASE = 0.01   #基礎的學習率
global_step = tf.Variable(0, trainable=False)  #全局步長
LEARNING_RATE_DECAY = 0.99   #學習率的衰減率
REGULARIZATION_RATE = 0.0001   #正則化項系數
MOVING_AVERAGE_DECAY = 0.99  #滑動平均衰減率

def train(mnist):
    x = tf.placeholder(tf.float32, [
            BATCH_SIZE,        
            LeNet5_infernece.IMAGE_SIZE, 
            LeNet5_infernece.IMAGE_SIZE,
            LeNet5_infernece.NUM_CHANNELS], 
        name='x-input')
    y_ = tf.placeholder(tf.float32, [None, LeNet5_infernece.OUTPUT_NODE], name='y-input')
    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    y = LeNet5_infernece.inference(x,False,regularizer) 
    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)
   
    train_op = tf.group(train_step, variables_averages_op) 
    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})
            if i % 1000 == 0:
                print("After %d training step(s), loss on training batch is %g." % (step, loss_value))

def main(argv=None):
    mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
    train(mnist)

if __name__ == '__main__':
    main()
View Code

模型子文件:LeNet5_infernece.py

# -*- coding: utf-8 -*-
"""
Created on 2017
《TensorFlow 實戰Google深度學習框架 第2版 ,鄭澤宇》
@author: 鄭澤宇、梁博文等
"""
#見書 p151
#6.4.1 經典卷積神經網絡模型——LeNet-5模型

import tensorflow as tf

#1. 設定神經網絡的參數
INPUT_NODE = 784
OUTPUT_NODE = 10

IMAGE_SIZE = 28
NUM_CHANNELS = 1

#第一層卷積層的尺寸和深度
CONV1_DEEP = 32  #第一層卷積層深度
CONV1_SIZE = 5  #第一層卷積層尺寸

#第二層卷積層的尺寸和深度
CONV2_DEEP = 64  #第二層卷積層深度
CONV2_SIZE = 5   #第二層卷積層尺寸

#第五層全連接層節點的個數
FC_SIZE = 512

#第六層全連接層節點的個數
NUM_LABELS = 10

#2. 定義前向傳播的過程
#參數:輸入(四維矩陣)、是否為訓練/測試、正則化參數(參數變量管理)
def inference(input_tensor, train, regularizer):
    
    #聲明第一層卷積層的變量並實現前向傳播過程。
    #使用不同的命名空間來隔離不同層的變量。
    #輸入: 28*28*1     輸出: 28*28*32
    with tf.variable_scope('layer1-conv1'):
        #定義卷積過濾器。
        #定義卷積層參數:前兩個為尺寸 5*5 、第三個為當前層節點矩陣的深度 1、第四個為卷積層的深度 32
        conv1_weights = tf.get_variable(
            "weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        #定義偏置項為 0,及下一層節點矩陣的深度 32(參數共享)
        conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer=tf.constant_initializer(0.0))
        #tf.nn.conv2d 提供了一個方便的卷積層前向傳播函數
        #參數1:當前層的節點矩陣,四維矩陣,第一維度對應一個輸入batch,如第一張圖片,第二張圖片..
        #參數2:卷積層參數
        #參數3:不同維度上的步長(第一維、最后一維必須為1)
        #參數4:提供'SAME'和'VALLD'選擇,'SAME'為添加全0填充,'VALLD'為不添加,填充的目的是矩陣尺寸在卷積層不變。
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
        #tf.nn.bias_add提供給每個conv節點加上偏置項,再將計算結果通過ReLU激活函數完成去線性化
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))
    
    #聲明第二層池化層前向傳播過程
    #使用不同的命名空間來隔離不同層的變量。
    #tf.nn.max_pool 提供了一個方便的最大池化層的前向傳播過程。
    #tf.nn.avg_pool 提供了一個方便的平均池化層的前向傳播過程,兩者參數一致。
    #參數1:四維矩陣,第一維度對應一個輸入batch,如第一張圖片,第二張圖片..
    #參數2:ksize為過濾器參數,常為[1, 2, 2, 1]、[1, 3, 3, 1]
    #參數3:不同維度上的步長(第一維、最后一維必須為1)
    #參數4:提供'SAME'和'VALLD'選擇,'SAME'為添加全0填充,'VALLD'為不添加
    #輸入: 28*28*32     輸出: 14*14*32
    with tf.name_scope("layer2-pool1"):
        pool1 = tf.nn.max_pool(relu1, ksize = [1,2,2,1],strides=[1,2,2,1],padding="SAME")

    #聲明第三層卷積層的變量並實現前向傳播過程。
    #使用不同的命名空間來隔離不同層的變量。
    #輸入: 14*14*32     輸出: 14*14*64
    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))
        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))
    
    #聲明第四層池化層前向傳播過程
    #輸入: 14*14*64     輸出: 7*7*64(不包括數據處理)
    with tf.name_scope("layer4-pool2"):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
        pool_shape = pool2.get_shape().as_list()
        nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
        reshaped = tf.reshape(pool2, [pool_shape[0], nodes])

    #聲明第五層全連接層的變量並實現前向傳播過程
    #輸入:向量長度為3136   輸出:向量長度為512
    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))
        fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
        if train: fc1 = tf.nn.dropout(fc1, 0.5)

    #聲明第六層全連接層的變量並實現前向傳播過程
    #輸入:向量長度為512   輸出:向量長度為10
    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
View Code

實驗結果

After 1 training step(s), loss on training batch is 5.95725.
After 1001 training step(s), loss on training batch is 0.664706.
After 2001 training step(s), loss on training batch is 0.670048.
After 3001 training step(s), loss on training batch is 0.638539.
After 4001 training step(s), loss on training batch is 0.743027.
After 5001 training step(s), loss on training batch is 0.638279.
result

MNIST數據集下載

Yann LeCun's MNIST page 提供了 MNIST 訓練集與測試集數據的下載。

下載后,解壓在一個文件夾即可。

參考文獻

[1] Lecun Y , Bottou L , Bengio Y , et al. Gradient-based learning applied to document recognition[J]. Proceedings of the IEEE, 1998, 86(11):2278-2324.

[2] 鄭澤宇、梁博文等. TensorFlow 實戰Google深度學習框架 第2版 [M] , 北京:電子工業出版社,2018.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM