tensorflow实践-mnist数字识别(1)
简单版本
利用tensorflow含有的mnist数据集训练一个简单的数字识别神经网络。
在一个脚本中实现一个完整的训练过程并每隔固定的轮数,在测试集中验证准确率。
脚本中几个注意的点
- 使用了随迭代变化的学习率。可以控制模型在训练初期的时候以较大的学习率进行调整,而到了后期以较小的学习率进行微调参数,不至于来回摆动。
- 使用滑动平率模型,用来维护一个影子变量,可以在一定程度上提高在测试集上的表现,使得模型更加稳健。
- 在损失函数中,增加正则损失,防止模型过拟合。
衰减的学习率
- 计算公式:
d
=
l
r
∗
d
e
c
y
∗
e
x
p
(
g
l
o
b
a
l
S
t
e
b
/
b
a
s
e
S
t
e
p
)
d=lr * decy *exp(globalSteb/baseStep)
d=lr∗decy∗exp(globalSteb/baseStep)
- d :学习率
- lr:初始学习率
- decy:学习率衰减率
- globalSteb:当前训练的轮数
- baseStep:训练完一遍数据所需要的轮数,一般为训练数据除以batch数
- 实现方式:tf.train.exponential_decay(lr, globalStep, baseStep, decy, staircase=True)
- 参数:staircase; 当这个参数设置为True的时候,这个时候的指数优化会变成阶梯函数,这个时候学习率是阶梯下降的,可以设置base_step为训练完一遍数据需要的迭代轮数。这个值默认为false。
滑动平均模型
- 计算公式:
- s h a d o w V a r i a b l e = s h a d o w V a r i a b l e ∗ d e c a y + ( 1 − d e c a y ) ∗ v a r i a b l e shadowVariable = shadowVariable * decay + (1-decay) * variable shadowVariable=shadowVariable∗decay+(1−decay)∗variable
-
d
e
c
a
y
=
m
i
n
(
u
s
e
r
D
e
c
a
y
,
(
1
+
n
u
m
U
p
d
a
t
e
s
)
/
(
10
+
n
u
m
U
p
d
a
t
e
s
)
)
decay = min(userDecay, (1+numUpdates)/(10+numUpdates))
decay=min(userDecay,(1+numUpdates)/(10+numUpdates))
- shadowVariable:影子变量
- variable:真实的变量
- userDecay:用户定义的滑动平均衰减率
- numUpdates:此时的训练轮数
- 实现方式:
- 通过 ema=tf.train.ExponentialMovingAverage(user_decay, num_updates) 初始化
- maintrain_ema = ema.apply([v1]) 在变量上运用滑动平均
正则损失
- 计算方式:通过对每一个参数进行惩罚,当参数量很多时,损失就会大很多。正则方式(L1,L2)
- 通过 tf.contrib.layers.l2_regularizer(lambda)(w) 进行调用L2正则损失
完整脚本
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 6 13:52:31 2019
@author: JustMo
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# mnist = input_data.read_data_sets("/wind/ainfs/jupyterspace/yxmo/Tensorflow/", one_hot=True)
## 定义初始化参数,对于一些
INPUT_NODE = 784 #因为是32*32的图片格式
OUTPUT_NODE = 10 #输出的是数字的分类
###网络参数
LAYYER1_NODE = 500 #定义第一层网络的节点数
BATCH_SIZE = 100 #定义一个batch训练的数据量,batch越小越接近随机梯度下降,batch越大越接近梯度下降
TRAINING_STEPS = 30000 #训练的轮数
###各种率参数
LEARNING_RATE_BASE = 0.8 #初始的学习率,越大参数的优化越快
LEARNING_RATE_DECAY = 0.99 #学习率的衰减率
REGULARIZATION_RATE = 0.0001 #模型复杂度的正则参数
MOVING_AVERAGE_DECAY = 0.99 #滑动平均衰减率,初始比较大,可以在训练前期更新得更快
## 定义模型的网络框架
## 接下来定义一个函数,用来计算每一轮的前向传播训练结果,这里定义了一个三层的全连接网络
## 最后一层由于有加入softmax的损失函数,所以可以不用加激活函数
## 还加入了滑动平均模型
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
#当没有提供滑动平均类的时候,直接使用参数当前的取值
if avg_class == None:
##进行前向计算
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
##因为准备用封装了softmax的cross_enrtopy,所以最后一层没有加激活函数直接输出
return tf.matmul(layer1, weights2) + biases2
else:
##使用avg_class.average来计算变量滑动平均值,然后再利用变量进行前向的计算
layer1 = tf.nn.relu(tf.matmul(input_tensor, avg_class.average(weights1)) + avg_class.average(biases1))
return tf.matmul(layer1, avg_class.average(weights2)) + avg_class.average(biases2)
def train(mnist):
##定义输入
x = tf.placeholder(dtype=tf.float32, shape=(None, INPUT_NODE), name="x-input")
y_ = tf.placeholder(dtype=tf.float32, shape=(None, OUTPUT_NODE), name="y-input")
##定义权重
weights1 = tf.Variable(tf.random_normal([INPUT_NODE, LAYYER1_NODE], stddev=0.1, seed=1, dtype=tf.float32), name="w-1")
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYYER1_NODE]), dtype=tf.float32, name="b-1")
weights2 = tf.Variable(tf.random_normal([LAYYER1_NODE, OUTPUT_NODE], stddev=0.1, seed=1, dtype=tf.float32), name="w-2")
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]), dtype=tf.float32, name="b-2")
##定义输出
##这里的avg_class设置为None,所以没有进行滑动平均
y = inference(x, None, weights1, biases1, weights2, biases2)
##接下来进行定义当前运行的轮数,用来进行更新学习率以及调整滑动平均衰减率,这个变量是不可训练的
global_step = tf.Variable(0, trainable=False)#初始状态训练轮数为0
##定义滑动平均的声明
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
##在定义好了滑动平均声明之后,对整个网络中的可训练的参数使用滑动平均
variable_averages_op = variable_averages.apply(tf.trainable_variables())
##利用滑动平均计算前向传播的结果
average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)
##定义好各个变量之后,需要定义损失函数,确定优化方向,这里因为是多分类所以使用的是softmax
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.argmax(y_, 1), logits=y)
##对整个batch的数据计算平均的损失
cross_entropy_mean = tf.reduce_mean(cross_entropy)
##为了防止过拟合,这里除了使用交叉验证之外,加入正则项约束,因为一般不对偏置进行正则,所以这里不用trainable_variables
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
regularization = regularizer(weights1) + regularizer(weights2)
##计算总的损失
loss = cross_entropy_mean + regularization
##在定义反向传播优化参数之前,需要对学习率进行设置,因为这里将要使用随训练轮数增加进行改变,注意global_step是没有传值的
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")
##上面的两行代码可以用 train_op = tf.group([train_step, variable_averages_op]) 等价
##上面就是训练过程定义,接下来需要验证使用了滑动平均模型的神经网络的传播结果正确性,故需要计算准确率
###先判断滑动平均预测出来的结果与真实结果的对比情况
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))
##为了直观看结果,这种判断结果进行量化,cast将转换为实数型
accuracy = tf.reduce_mean(tf.cast(correct_prediction, dtype=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):
##为了判断模型效果,每1000轮训练看一次测试结果
if i % 1000 == 0:
##看训练的参数在验证集上的准确率
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("在训练轮数为 %d 时,数据在验证集上的准确率为 %g"%(i,validate_acc))
print("global_step %d"%(sess.run(global_step)))
#产生新的一轮迭代训练数据进行模型的训练
xs, ys = mnist.train.next_batch(BATCH_SIZE)
##并调用train_op进行反向迭代以及滑动平均变量的计算
sess.run(train_op, feed_dict={x:xs, y_:ys})
##当训练完所有轮之后,看整个模型在测试集上的一个预测效果
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("经过了 %d 轮的训练之后,模型在测试集上的准确率为 %g"%(TRAINING_STEPS, test_acc))
def main(argv=None):
#声明处理mnist的类,在程序运行的时候自动下载数据
mnist = input_data.read_data_sets("/path/to/MNIST_data/", one_hot=True)
train(mnist)
if __name__ == "__main__":
##tensorflow提供了一个主程序的入口,tf.app.run()会调用上面的main函数
tf.app.run()
参考资料
tensorflow:实战Google深度学习框架。1
tensorflow:实战Google深度学习框架,郑泽宇、梁博文、顾思宇。 ↩︎