tf框架的范围管理scope技术来优化参数设定,最终准确率为0.984
这里主要引入较多参数来改进原有的cnn模型:
- 使用激活函数去线性化
- 使用隐藏层即加深层数以解决复杂问题
- 使用学习率调整更新参数的频度
- 使用滑动平均模型来调整模型结果
# 导入必要的库
import tensorflow as tf
import os
from tensorflow.examples.tutorials.mnist import input_data
# 下载数据,打印数据信息
mnist = input_data.read_data_sets('/MNIST_data/', one_hot=True)
print("Training data size: ", mnist.train.num_examples)
print("Validating data size: ", mnist.validation.num_examples)
print("Testing data size: ", mnist.test.num_examples)
print("Example training data: ", mnist.train.images[0] )
print("Example training data label: ", mnist.train.labels[0])
# 声明全局变量
INPUT_NODE = 784 # 输入层节点数,图片是28*28*1的格式,每个像素点对应一个节点就是784
OUTPUT_NODE = 10 # 输出层节点数,0-9十个数字
LAYER1_NODE = 500 # 第一个隐藏层的节点数
BATCH_SIZE = 100 # batch的大小,越大训练过程越接近梯度下降,越小越接近随机梯度下降
LEARNING_RATE_BASE = 0.8 # 基础的学习率
LEARNING_RATE_DECAY = 0.99 # 学习率的衰减值
REGULARIZATION_RATE = 0.0001 # 正则化的λ系数
TRAINING_STEPS = 30000 # 训练的轮数
MOVING_AVERAGE_DECAY = 0.99 # 滑动平均衰减率
def get_weight_variable(shape, regualrizer):
# get_variable()获取这个参数的现有变量或创建一个新变量。获取的参数根据"name"指定
# 生成的值服从具有指定平均值和标准偏差的正态分布,
# 如果生成的值大于平均值2个标准偏差的值则丢弃重新选择。
# stddev 要生成的随机值的标准偏差
weights = tf.get_variable("weights", shape,
initializer=tf.random_normal_initializer(stddev=0.1))
if regualrizer != None:
# 传入的参数regualrizer是一个函数
# 如果定义了正则化函数(L1或者L2),则计算weights的正则化参数,并加入
# 名为“losses”的集合
tf.add_to_collection("losses", regualrizer(weights))
return weights
def inference(x, regularizer):
"""
辅助函数,给定神经网络的输入和所有参数,计算向前传播的结果
定义了一个relu激活的三层全连接网络(输入层,隐藏层,输出层)
"""
# variable_scope()用于定义创建变量(层)的操作的上下文管理器。此上下文管理器验证(可选)的
# values来自同一图形,确保图形是默认图形,并推送名称范围和变量范围
with tf.variable_scope('layer1', reuse = False):
weights = get_weight_variable([INPUT_NODE, LAYER1_NODE], regularizer)
biases = tf.get_variable("biases", [LAYER1_NODE],
initializer=tf.constant_initializer(0.0))
layer1 = tf.nn.relu(tf.matmul(x, weights) + biases)
with tf.variable_scope('layer2', reuse = False):
weights = get_weight_variable([LAYER1_NODE, OUTPUT_NODE], regularizer)
biases = tf.get_variable("biases", [OUTPUT_NODE],
initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
def train(mnist):
"""训练模型"""
x = tf.placeholder(tf.float32, shape=[None, INPUT_NODE], name="x-input")
y_ = tf.placeholder(tf.float32, shape=[None, OUTPUT_NODE], name="y-input")
# 定义正则化的函数
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
# 向前传播求出y
y = inference(x, regularizer)
# 定义训练的轮数,需要用trainable=False参数指定不训练这个变量,
# 这样同时也可以避免这个变量被计算滑动平均值
global_step = tf.Variable(0, trainable=False)
# 给定滑动平均衰减速率和训练轮数,初始化滑动平均类
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,
global_step)
# 用tf.trainable_variable()获取所有可以训练的变量列表,全部使用滑动平均
variables_averages_op = variable_averages.apply(tf.trainable_variables())
# 定义损失函数
# 因为标准答案是一个长度为10的一维数组,argmax可以从这个矩阵(y_)的轴为1的部分取最大值的序号
# 在sparse_softmax_cross_entropy_with_logits()中,要将原来为one-hot形式的labels
# 转换为数字标签[1],[2],...的格式。
# tf.argmax(Y,asix),axis = 0 或 1,分别表示按列或按行返回最大值的序号。
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)
# 给损失加上正则化的损失
# 使用get_collection获取losses集合的全部值的列表,然后用add_n求列表的所有值的和
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
)
# 优化损失函数
# global_step初始值为0,在loss更新后会+1,用来记录更新的次数
# 返回值是训练之后的梯度,会随着global_step递增
train_step = tf.train.GradientDescentOptimizer(
learning_rate).minimize(loss, global_step=global_step)
# 反向传播更新参数之后需要更新每一个参数的滑动平均值,用下面的代码可以一次完成这两个操作
# train_step计算所有参数的梯度,variables_averages_op对所有参数进行滑动平均(利用train_step)
with tf.control_dependencies([train_step, variables_averages_op]):
train_op = tf.no_op(name="train")
# y是计算得出的预测答案,而y_是正确答案,用argmax获取答案的序号(也即是数字的值)
# equal()判断两个答案是否相等,是就返回True,否就返回False
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
# cast()把一个布尔类型的数转换为实数,然后用reduce_mean计算平均值,获取准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
# 初始化全局变量
tf.global_variables_initializer().run()
validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
test_feed = {x: mnist.test.images, y_: mnist.test.labels}
# 开始迭代
for i in range(TRAINING_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
sess.run(train_op, feed_dict={x:xs, y_:ys})
# tensorflow的数据集特有的一种batch_size获取方法
if i % 1000 == 0:
# 获取计算之后的loss和global_step
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("After %d traing times, validate accuracy using average model is %g"
% (i, validate_acc))
# 使用模型训练测试集,获取最终的准确率
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("After %d traing times, test accuracy using average model is %g"
% (TRAINING_STEPS, test_acc))
# 主函数定义
def main(argv=None):
tf.reset_default_graph()
mnist = input_data.read_data_sets('/MNIST_data/', one_hot=True)
train(mnist)
if __name__ == "__main__":
main()