首先是mnist_train.py的修改如下:
import os
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np
# 加载mnist_inference.py中定义的常量和前向传播的函数
import mnist_inference
# 配置神经网络的参数
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 30000
MOVING_AVERAGE_DECAY = 0.99
# 模型保存的路径和文件名
MODEL_SAVE_PATH = "/path/to/model/"
MODEL_NAME= "model.ckpt"
def train(mnist):
# 定义输入输出placehoder.
x = tf.placeholder(
tf.float32, [BATCH_SIZE,mnist_inference.IMAGE_SIZE,
mnist_inference.IMAGE_SIZE,
mnist_inference.NUM_CHANNELS],name='x-input'
)
y_ = tf.placeholder(
tf.float32,[None,mnist_inference.OUTPUT_NODE],name='y-input'
)
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
# 直接使用mnist_inference.py中定义的前向传播过程
y = mnist_inference.inference(x, train, regularizer)
global_step = tf.Variable(0,trainable=False)
variable_averages = tf.train.ExponentialMovingAverage(
MOVING_AVERAGE_DECAY, global_step
)
variable_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
)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
with tf.control_dependencies([train_step,variable_averages_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(TRAINING_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
reshaped_xs = np.reshape(xs, (BATCH_SIZE,
mnist_inference.IMAGE_SIZE,
mnist_inference.IMAGE_SIZE,
mnist_inference.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))
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("/path/to/mnist_data",one_hot=True)
train(mnist)
if __name__ == '__main__':
tf.app.run()
然后是mnis_inference.py的修改,代码如下:
import tensorflow as tf
#定义网络结构,输入、输出和隐藏层的节点数
#28*28,相当于图片的像素
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
#重置default graph计算图以及nodes节点
tf.reset_default_graph()
#定义神经网络的前向传播过程,定义了一个新参数train,用于区分训练过程和测试过程。在这个程序中将用到
#dropout方法,用来进一步提升模型的可靠性并防止过拟合,dropout过程只在训练时使用。
def inference(input_tensor,train,regularizer):
#声明第一层神经网络的变量并完成前向传播过程。tf.variable_scope生成了一个上下文管理器,来管理已经创建的变量。
#使用不同的命名变量来隔离不同层的变量,可以让每一层中的变量命名只需要考虑在当前层的作用,而不需要担心重名
#的问题。这里定义的卷积层输入为28*28*1,因为使用了全0填充,所以输出为28*28*32的矩阵。
with tf.variable_scope('layer1-conv1'):
#这里通过tf.get_variable和tf.Variable没有本质区别,因为在训练或是测试中没有在同一个程序中多次调用这个
#函数。如果在同一个程序中多次调用,第一次调用后要将reuse设置为True。
#声明了输入到隐藏层的权重变量和偏执项
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填充。全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))
#实现第二层池化层的前向传播过程,选用最大池化层,过滤器边长为2,使用全0填充且移动步长
#为2.这一层的输入是上一层的输出,即输入为28*28*32,输出为14*14*32的矩阵。
with tf.name_scope('layer2-pool1'):
#第一个参数为当前层的节点矩阵,这是一个四维矩阵。ksize提供了过滤器的尺寸,四维数组的第一个和最后一个数必须为1
#,这意味着池化层过滤器是不可以跨不同输入样例或者节点矩阵深度的。strides提供了步长信息,第一维和
#最后一维也只能是1,表明池化层不能减少节点矩阵深度或输入样例个数。padding是否全0填充。
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'):
#这里通过tf.get_variable和tf.Variable没有本质区别,因为在训练或是测试中没有在同一个程序中多次调用这个
#函数。如果在同一个程序中多次调用,第一次调用后要将reuse设置为True。
#声明了输入到隐藏层的权重变量和偏执项
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))
#实现第四层池化层的前向传播过程,和第二层的结构一样,输入为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')
#将第四层池化层的输出转化为第五层全连接层的输入。第四层输出为7*7*64的矩阵,然而全连接层
#需要输入的格式为向量,所以在这里需要将这个7*7*64的矩阵拉直成一个向量。pool2.get_shape()
#函数可以得到第四层输出矩阵的维度而不需要手工计算。注意每一层神经网络的输入输出都为一个
#batch的矩阵,所以这里得到的维度也包含了一个batch中的数据个数。
pool_shape = pool2.get_shape().as_list()
# print(pool_shape) 结果为[100,7,7,64]
#计算矩阵拉直成向量后的长度,是矩阵长宽及深度的乘积,pool_shape[0]是一个batch中的数据个数。
nodes = pool_shape[1]*pool_shape[2]*pool_shape[3]
# print(pool_shape[1],pool_shape[2],pool_shape[3],pool_shape[0])
#通过tf.reshape函数将第四层得输出变为一个batch的向量。
reshaped = tf.reshape(pool2,[pool_shape[0],nodes])
# print(reshaped)
#声明第五层的变量并实现前向传播过程。这一层的输入为拉直后的一组向量。长度为7*7*64,输出
#的长度为512的向量。这一层引入了dropout的概念,训练时会随机将部分节点输出改为0,,避免
#过拟合,从而使模型在测试数据上的表现更好,一般只用在全连接层。
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的向量,这一层的输出通过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
后续补充详细过程。