0%

MNIST 4.改进的cnn模型

该模型是TF书中97页的改进版,来源于CSDN,模型使用
tf框架的范围管理scope技术来优化参数设定,最终准确率为0.984

这里主要引入较多参数来改进原有的cnn模型:

  • 使用激活函数去线性化
  • 使用隐藏层即加深层数以解决复杂问题
  • 使用学习率调整更新参数的频度
  • 使用滑动平均模型来调整模型结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# 导入必要的库
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()

参考文献:https://blog.csdn.net/lovefreewind/article/d