Tensorflow例項:(卷積神經網路)LeNet-5模型
通過卷積層、池化層等結構的任意組合得到的神經網路有無限多種,怎樣的神經網路更有可能解決真實的影象處理問題?本文通過LeNet-5模型,將給出卷積神經網路結構設計的一個通用模式。
LeNet-5模型
LeNet-5模型是Yann LeCun教授於1998年在論文Gradient-based learning applied to document recognition中提出的,它是第一個成功應用於數字識別問題的卷積神經網路。LeNet-5模型一共有7層,下圖展示了LeNet-5模型的架構:
第一層,卷積層
這一層的輸入就是原始的影象畫素32*32*1。第一個卷積層過濾器尺寸為5*5,深度為6,不使用全0填充,步長為1。所以這一層的輸出:28*28*6,卷積層共有5*5*1*6+6=156個引數
第二層,池化層
這一層的輸入為第一層的輸出,是一個28*28*6的節點矩陣。本層採用的過濾器大小為2*2,長和寬的步長均為2,所以本層的輸出矩陣大小為14*14*6。
第三層,卷積層
本層的輸入矩陣大小為14*14*6,使用的過濾器大小為5*5,深度為16.本層不使用全0填充,步長為1。本層的輸出矩陣大小為10*10*16。本層有5*5*6*16+16=2416個引數。
第四層,池化層
本層的輸入矩陣大小10*10*16。本層採用的過濾器大小為2*2,長和寬的步長均為2,所以本層的輸出矩陣大小為5*5*16。
第五層,全連線層
本層的輸入矩陣大小為5*5*16,在LeNet-5論文中將這一層成為卷積層,但是因為過濾器的大小就是5*5,所以和全連線層沒有區別。如果將5*5*16矩陣中的節點拉成一個向量,那麼這一層和全連線層就一樣了。本層的輸出節點個數為120,總共有5*5*16*120+120=48120個引數。
第六層,全連線層
本層的輸入節點個數為120個,輸出節點個數為84個,總共引數為120*84+84=10164個。
第七層,全連線層
本層的輸入節點個數為84個,輸出節點個數為10個,總共引數為84*10+10=850
Tensorflow實現LeNet-5
下面給出一個Tensorflow的程式來實現一個類似LeNet-5模型的卷積神經網路來解決MNIST數字識別問題。
LeNet_inference.py
# _*_ coding: utf-8 _*_
import tensorflow as tf
# 配置神經網路的引數
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
# 定義卷積神經網路的前向傳播過程。這裡添加了一個新的引數train,用於區別訓練過程和測試過程。在這個程式中將用到dropout方法
# dropout可以進一步提升模型可靠性並防止過擬合(dropout過程只在訓練時使用)
def inference(input_tensor, train, regularizer):
with tf.variable_scope('layer1-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('bias', [CONV1_DEEP],
initializer=tf.constant_initializer(0.0))
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))
with tf.name_scope('layer2-pool1'):
pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
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))
with tf.name_scope('layer4-pool2'):
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
pool2_shape = pool2.get_shape().as_list()
nodes = pool2_shape[1] * pool2_shape[2] * pool2_shape[3]
reshaped = tf.reshape(pool2, [pool2_shape[0], nodes])
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.0))
fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
if train:
fc1 = tf.nn.dropout(fc1, 0.5)
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.0))
logit = tf.matmul(fc1, fc2_weights) + fc2_biases
return logit
LeNet_train.py
# _*_ coding: utf-8 _*_
import os
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
# 載入mnist_inference.py中定義的常量和前向傳播的函式
import LeNet_inference
# 配置神經網路的引數
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAIN_STEPS = 30000
MOVING_AVERAGE_DECAY = 0.99
MODEL_SAVE_PATH = "./model/"
MODEL_NAME = "model3.ckpt"
def train(mnist):
x = tf.placeholder(tf.float32, [BATCH_SIZE, LeNet_inference.IMAGE_SIZE,
LeNet_inference.IMAGE_SIZE,
LeNet_inference.NUM_CHANNELS], name='x-input')
y_ = tf.placeholder(tf.float32, [None, LeNet_inference.OUTPUT_NODE], name='y-input')
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
y = LeNet_inference.inference(x, train, regularizer)
global_step = tf.Variable(0, trainable=False)
variable_average = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
variable_average_op = variable_average.apply(
tf.trainable_variables())
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.argmax(y_, 1), logits=y)
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=global_step, decay_steps=mnist.train.num_examples / BATCH_SIZE,
decay_rate=LEARNING_RATE_DECAY)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
with tf.control_dependencies([train_step, variable_average_op]):
train_op = tf.no_op(name='train')
saver = tf.train.Saver()
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAIN_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
xs = np.reshape(xs, [BATCH_SIZE, LeNet_inference.IMAGE_SIZE,
LeNet_inference.IMAGE_SIZE,
LeNet_inference.NUM_CHANNELS])
_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
if i % 1000 == 0:
print("After %d training steps, loss on training"
"batch is %g" % (step, loss_value))
saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)
def main(argv=None):
mnist = input_data.read_data_sets("E:\科研\TensorFlow教程\MNIST_data", one_hot=True)
train(mnist)
if __name__ == '__main__':
tf.app.run()
如何設計卷積神經網路架構
下面的正則化公式總結了一些經典的用於圖片分類問題的卷積神經網路架構:
“+”表示一層或多層,“?”表示有或者沒有
除了LeNet-5模型,2012年ImageNet ILSVRC影象分類挑戰的第一名AlexNet模型、2013年ILSVRC第一名ZF Net模型已經2014年第二名VGGNet模型的架構都滿足上面的正則表示式。
如何設定卷積層或池化層配置
- 過濾器的尺寸:1或3或5,有些網路中有過7甚至11
- 過濾器的深度:逐層遞增。每經過一次池化層之後,卷積層深度*2
- 卷積層的步長:一般為1,有些也會使用2甚至3
- 池化層:最多的是max_pooling,過濾器邊長一般為2或者3,步長一般為2或3