Tensorflow學習——6 LeNet-5的研究
阿新 • • 發佈:2019-01-10
1、LeNet-5的成就
LeNet-5是Yann LeCun在1998年設計的用於手寫數字識別的卷積神經網路,當年美國大多數銀行就是用它來識別支票上面的手寫數字的,它是早期卷積神經網路中最有代表性的實驗系統之一,LeNet-5在MNIST資料集上可以達到99.2%的準確率。
2、LeNet-5的優點
LeNet-5一共有七層
- 每一個卷積層包含三個部分:卷積操作、池化操作和非線性啟用函式
- 使用卷積來提取空間特徵
- 下采樣層的平均池化層
- 層與層之間的稀疏連線減少計算複雜度
3、LeNet-5的網路圖
LeNet有七層,卷積–>池化–>卷積–>池化–>全連線–>全連線–>全連線
第一層,卷積層
輸入是原始的影象畫素,LeNet-5接受的輸入是32×32×1的,第一個卷積層的過濾器大小是5×5,深度為6,不使用全0補充,步長為1。
所以這一層的輸出為(32-5+1=28)【注out_length = (in_length-filter_length+1)/stride_length】,所以第一次的引數有5×5×1×6+6=158個,其中5×5是卷積核的length和width,1是輸入層的通道數,第一個6是卷積核深度,第2個6是偏置項的引數。
連線數是122304個,因為下一層的節點是28×28×6=4704個節點,每一個節點和5×5個當前節點相連,所以本層有(5×5+1)×28×28×6=122304個連線。
第二層,池化層
輸入的矩陣是28×28×6的矩陣,過濾器的大小是2×2,長和寬都是2,使用全0填充,所以輸出矩陣是14×14×6
(input_width+2*pad-pool_size)/stride+1
(28+2×0-2)/2 + 1
第三層,卷積層
輸入是14×14×6的矩陣節點,第二個卷積層的過濾器大小是5×5,深度為16,不使用全0補充,步長為1。
所以這一層的輸出為10×10×16 (14-5+1=10)【注out_length = (in_length-filter_length+1)/stride_length】,所以第一次的引數有5×5×6×16+16=2416個,其中5×5是卷積核的length和width,6是輸入層的通道數,第一個16是卷積核深度,第2個16是偏置項的引數。
連線數是41600個,因為下一層的節點是10×10×16個節點,每一個節點和5×5個當前節點相連,所以本層有(5×5+1)×10×10×16=41600個連線。
第四層,池化層
輸入的矩陣是10×10×16的矩陣,過濾器的大小是2×2,長和寬都是2,使用全0填充,所以輸出矩陣是5×5×16
第五層,全連線層
輸入的矩陣是5×5×16的矩陣,輸出的節點個數是120,所以引數為5×5×16×120+120=48120
第六層,全連線層
輸入的節點是120個,輸出是84個,引數120×84 +84 = 10164
第五層,全連線層
輸入的節點是84個,輸出是10個,引數84×10+10= 850
4、LeNet-5的程式碼
# _*_ encoding=utf8 _*_
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import os
import numpy as np
# 配置神經網路的引數
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
# 定義神經網路的前向傳播v
def inference(input_tensor,train,regularizer):
# 第一層,輸入是28*28*1 input_tensor, 卷積核 5 *5 *1*6 最後兩個是影象通道數和卷積核個數,
# strides 為1 用0補齊
# 輸出是28*28*32
with tf.variable_scope("layer-conv1"):
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("biases",[CONV1_DEEP],initializer=tf.constant_initializer(0.0))
#使用邊長為5,深度32的過濾器,移動的步長為1,
conv1 = tf.nn.conv2d(input_tensor,conv1_weights,strides=[1,1,1,1],padding="SAME")
relu1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))
# 第二層,池化層,最大池化,輸入是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.name_scope("layer-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("biased",[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('layer-pool2'):
pool2 = tf.nn.max_pool(
relu2,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME"
)
# 將池化層的輸出轉化為全連線層的資料格式,也是就是reshape一下,第四層輸出是矩陣,第五層輸入是一個向量,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,避免過擬合,這個一般只是在全連線層使用,
with tf.variable_scope("layer-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(
"biased",[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,通過softmax可以得到最後的分類
with tf.variable_scope("layer-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(
"biases",[NUM_CHANNELS],
initializer=tf.constant_initializer(0.1)
)
logit = tf.matmul(fc1,fc2_weights) + fc2_biases
return logit
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
# 訓練模型的過程
def train(mnist):
# 定義輸出為4維矩陣的placeholder
x = tf.placeholder(tf.float32, [
BATCH_SIZE,
IMAGE_SIZE,
IMAGE_SIZE,
NUM_CHANNELS],
name='x-input')
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input')
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
y = 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,
IMAGE_SIZE,
IMAGE_SIZE,
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))
if __name__ == '__main__':
mnist = input_data.read_data_sets("../bin/data", one_hot=True)
train(mnist)