1 模型结构
LeNet-5是一个专为手写数字识别而设计的最经典的卷积神经网络,由Yann LeCun教授于1998年提出。
第一层:INPUT层输入的是32*32分辨率的黑白图像;
第二层:C1层是一个卷积层,由6个特征图(Feature Map)组成,这个卷积层的卷积核尺寸为5*5,深度为6,没有使用全0填充且步长为1,所以得到的每个特征图有28*28 ((32-5+1)*(32-5+1))个神经元。参数数量为156 (5*5*1*6+6)个,其中6个为偏置项参数。在6个 Feature Map中共有4704 (28*28*6)个单元,每个单元和输入层的25个单元连接,所以卷积层和输入层之间的连接数为 122304 ((25+1)*4704);
第三层:S2是一个下采样层,有6个14*14大小的 Feature Map,每个特征图都是由第二层经过一个 2*2 最大池化操作得来,长和宽的步长均为2。也即 S2层 每一个特征图的每一个单元都与C1层对应的特征图中 2*2 大小的区域相连;
第四层:C3是一个卷积层,由第三层的特征图经过一个卷积操作得到,卷积核的尺寸大小为 5*5。本层的 16 个特征图不是一对一地连接到第三层地 6 个特征图地输出,而是有着固定地连接关系。
第五层:S4是一个下采样层,有16个 5*5 大小的特征图,每个特征图都是由第四层经过一个 2*2 的最大池化操作得来,长和宽的步长均为2.也即 S4层每一个特征图的每一个单元都与C3层对应的特征图中 2*2 大小的区域相连。
第六层:C5层是一个卷积层,由120个特征图组成,这个卷积层的卷积核尺寸为 5*5, 没有使用全0 填充且步长为1,所以得到的每个特征图有 1*1 ((5-5+1)*(5-5+1)个神经元,也就是C5只有120个神经元。尽管在 LeNet-5模型的论文中将 C5 层视作卷积层,但是基本和全连接层没有区别;
第七层:F6是一个全连层,有 84个神经元,与C5层构成全连关系,所以连接数与参数都是 10164 ((120+1)*84)个。 该层计算输入向量与偏置参数的矩阵乘法,每个单元还加入了偏置项,最后经由 sigmoid激活函数传递到 OUTPUT层;
第八层:OUTPUT层也是一个全连层,共有10个单元,这10个单元分别代表着数字 0~9. 判断标准是 如果某个单元输出为 0,那么该单元在本层中的位置就是网络识别得出的数字。产生这样的输出是因为 本层单元计算的是 径向基函数:
2 TensorFlow实现
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./MNIST_data", one_hot=True)
batch_size = 100
learning_rate = 0.01
learning_rate_decay = 0.99
max_steps = 60000
def hidden_layer(input_tensor, regularizer, avg_class, resuse):
#创建第一个卷积层,得到特征图大小为32@28*28
with tf.variable_scope("C1_conv",reuse=resuse):
conv1_weights = tf.get_variable("weight", [5,5,1,32], initializer=tf.truncated_normal_initializer(stddev=0.1))
conv1_biases = tf.get_variable("bias", [32], 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))
#创建第一个池化层,池化后的结果为32@14*14
with tf.name_scope("S2-max_pool",):
pool1 = tf.nn.max_pool(relu1, ksize=[1,2,2,1], strides=[1,2,2,1], padding="SAME")
#创建第二个卷积层
with tf.variable_scope("C3_conv",reuse=resuse):
conv2_weights = tf.get_variable("weights", [5,5,32,64], initializer=tf.truncated_normal_initializer(stddev=0.1))
conv2_biases = tf.get_variable("bias", [64], 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("S4-max_pool",):
pool2 = tf.nn.max_pool(relu2, ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
shape = pool2.get_shape().as_list()
nodes = shape[1]*shape[2]*shape[3]
reshaped = tf.reshape(pool2, [shape[0], nodes])
#创建第一个全连层
with tf.variable_scope("layer5-full1",reuse=resuse):
Full_connection1_weights = tf.get_variable("weight",[nodes, 512], initializer=tf.truncated_normal_initializer(stddev=0.1))
#对全连层的权重加入正则化
tf.add_to_collection("losses", regularizer(Full_connection1_weights))
Full_connection1_biases = tf.get_variable("bias", [512], initializer=tf.constant_initializer(0.1))
if avg_class == None:
Full_1 = tf.nn.relu(tf.matmul(reshaped, Full_connection1_weights)+ Full_connection1_biases)
else:
Full_1 = tf.nn.relu(tf.matmul(reshaped, avg_class.average(Full_connection1_weights))+avg_class.average(Full_connection1_biases))
#创建第二个全连层
with tf.variable_scope("layer6-full2", reuse=resuse):
Full_connection2_weights = tf.get_variable("weights", [512, 10], initializer=tf.truncated_normal_initializer(stddev=0.1))
#对全连层的权重加入正则化
tf.add_to_collection("losses",regularizer(Full_connection2_weights))
Full_connection2_biases = tf.get_variable("bias", [10], initializer=tf.constant_initializer(0.1))
if avg_class == None:
result = tf.matmul(Full_1, Full_connection2_weights)+Full_connection2_biases
else:
result = tf.matmul(Full_1, avg_class.average(Full_connection2_weights))+avg_class.average(Full_connection2_biases)
return result
with tf.device('/gpu:0'):
x = tf.placeholder(tf.float32, [batch_size, 28, 28, 1], name="x-input")
y_ = tf.placeholder(tf.float32, [None, 10], name="y-input")
regularizer = tf.contrib.layers.l2_regularizer(0.0001)
#注意reuse参数取值为False
y = hidden_layer(x,regularizer,avg_class=None,resuse=False)
training_step = tf.Variable(0, trainable=False)
variable_averages = tf.train.ExponentialMovingAverage(0.99, training_step)
variables_averages_op = variable_averages.apply(tf.trainable_variables())
#注意reuse参数取值为True
average_y = hidden_layer(x, regularizer, variable_averages, resuse=True)
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, training_step, mnist.train.num_examples/batch_size,
learning_rate_decay, staircase=True)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=training_step)
with tf.control_dependencies([train_step, variables_averages_op]):
train_op = tf.no_op(name='train')
crorent_predicition = tf.equal(tf.arg_max(average_y, 1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(crorent_predicition, tf.float32))
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
tf.global_variables_initializer().run()
for i in range(max_steps):
if i % 1000 == 0:
x_var, y_var = mnist.validation.next_batch(batch_size)
reshaped_x2 = np.reshape(x_var, (batch_size,28,28,1))
validate_feed = {x: reshaped_x2, y_: y_var}
validate_accuracy = sess.run(accuracy, feed_dict=validate_feed)
print ("After %d training step, validation accuracy""using average model is %g%%" %(i, validate_accuracy * 100))
x_train, y_train = mnist.train.next_batch(batch_size)
#用于训练的数据需要进行reshape处理
reshaped_xs = np.reshape(x_train, (batch_size, 28, 28, 1))
sess.run(train_op, feed_dict={x:reshaped_xs, y_:y_train})
本文详细解析了LeNet-5卷积神经网络的结构与原理,包括其在手写数字识别领域的应用,从输入层到输出层的各层功能与参数配置,并通过TensorFlow实现LeNet-5模型。
1373

被折叠的 条评论
为什么被折叠?



