MNIST数据集
滑动平均模型
Tensorflow使用tf.train.ExponentialMovingAverage()实现滑动平均模型,在使用随机梯度下降方法训练神经网络时候,使用这个模型可以增强模型的鲁棒性(robust),可以在一定程度上提高模型在测试数据集上的表现。即如果在测试过程中,出现了一些噪声数据,滑动平均模型可以很好地应对这些数据,使这些噪声数据不会对模型的变量造成太大的影响。
滑动平均模型为每个变量维护一个影子变量,其初始值是变量的初始值,每次变量更新时,影子变量的值会更新为:
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
shadow\_variable = shadow\_variable * decay + (1-decay) * variable
shadow_variable=shadow_variable∗decay+(1−decay)∗variable
其中
s
h
a
d
o
w
_
v
a
r
i
a
b
l
e
shadow\_variable
shadow_variable为影子变量,
v
a
r
i
a
b
l
e
variable
variable为初始变量,
d
e
c
a
y
decay
decay为衰减率。
d
e
c
a
y
decay
decay决定了模型更新的速度,
d
e
c
a
y
decay
decay越大,模型更新越慢,越稳定。实际工作中,
d
e
c
a
y
decay
decay一般设置为非常接近1的数(0.99或0.999之类),为了在训练初期加快更新速度,可以提供
n
u
m
_
s
t
e
p
s
num\_steps
num_steps参数,提供这个参数后,
d
e
c
a
y
decay
decay的取值变为:
m
i
n
(
d
e
c
a
y
,
1
+
n
u
m
_
s
t
e
p
10
+
n
u
m
_
s
t
e
p
)
min(decay, \frac{1+num\_step}{10+num\_step})
min(decay,10+num_step1+num_step)
n
u
m
_
s
t
e
p
num\_step
num_step表示实际的训练步数。
如果提供了
n
u
m
_
s
t
e
p
s
num\_steps
num_steps那么在
n
u
m
_
s
t
e
p
num\_step
num_step还比较小的时候,
m
i
n
(
)
min()
min()会取到右边比较小的部分,也就是有一个比较小的
d
e
c
a
y
decay
decay,这个时候模型更新会很快,当
s
t
e
p
step
step增大时,模型更新速度会逐渐降低。
在应用滑动平均模型后,并不会改变变量的值,但是会维护一个影子变量来记录其滑动平均值,获取变量的滑动平均值实际上就是获取变量的影子变量的值,影子变量的值是当前值和更新后的值之间的一个值,相当与限制参数的更新速度,让更新比较缓慢,比较稳妥,但是在训练初期会导致更新比较慢,所以才有提供
n
u
m
_
s
t
e
p
s
num\_steps
num_steps参数使之在训练初期更新比较快。
滑动平均可以看作是变量的过去一段时间取值的均值,相比对变量直接赋值而言,滑动平均得到的值在图像上更加平缓光滑,抖动性更小,不会因为某次的异常取值而使得滑动平均值波动很大。
对神经网络边的权重 weights 使用滑动平均,得到对应的影子变量 shadow_weights。在训练过程仍然使用原来不带滑动平均的权重 weights,不然无法得到 weights 下一步更新的值,又怎么求下一步 weights 的影子变量 shadow_weights。之后在测试过程中使用 shadow_weights 来代替 weights 作为神经网络边的权重,这样在测试数据上效果更好。因为 shadow_weights 的更新更加平滑,对于随机梯度下降而言,更平滑的更新说明不会偏离最优点很远;
设decay=0.999,一个更直观的理解,在最后的1000次训练过程中,模型早已经训练完成,正处于抖动阶段,而滑动平均相当于将最后的1000次抖动进行了平均,这样得到的权重会更加robust。在整个训练过程中影子变量并不会对实际需要训练的变量产生影响啊,后面持久化的变量也不是影子变量。 在训练过程中,为参数维护更新一个影子变量,这样影子变量会停留在最终参数的周围保持稳定。 在测试阶段,使用影子变量代替参数,进行测试。
代码实现MNIST手写数字识别
import tensorflow as tf
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.999
max_steps = 30000
//一般代表训练轮数
training_step = tf.Variable(0,trainable=False)
x = tf.placeholder(tf.float32,[None,784],name="input_x")
y_ = tf.placeholder(tf.float32,[None,10],name="input_y")
with tf.variable_scope("weights"):
weights1 = tf.Variable(tf.truncated_normal([784,500],stddev=0.1))
bias1 = tf.Variable(tf.constant(0.1,shape=[500]))
weights2 = tf.Variable(tf.truncated_normal([500,10],stddev=0.1))
bias2 = tf.Variable(tf.constant(0.1,shape=[10]))
def Hidden_layer(input_tensor,weights1,bias1,weights2,bias2):
layer1 = tf.nn.relu(tf.matmul(input_tensor,weights1) + bias1)
return tf.nn.relu(tf.matmul(layer1,weights2) + bias2)
y = Hidden_layer(x,weights1,bias1,weights2,bias2)
//滑动平均模型:training_step相当于num_step
average_class = tf.train.ExponentialMovingAverage(0.99,training_step)
//维持变量的滑动平均,即对shadow variables进行计算
//var_list必须是Variable或者Tensor objects构成的列表。该方法会为列表中的所有元素创建影子变量,且变量对象的影子变量初始值和变量相同。影子变量
//也会被添加到GraphKeys.MOVING_AVERAGE_VARIABLES集合中。对于Tensor objects,影子变量会被初始化为0,同时被设置为无偏。
//影子变量被设置trainable=False,并且被添加到GraphKeys.MOVING_AVERAGE_VARIABLES集合中,它们会在调用tf.global_variables()时被返回。
//该方法返回一个按照要求更新所有影子变量的操作。同时需要注意的是,apply()可以在不同的var_list下被多次调用。
average_op = average_class.apply(tf.trainable_variables())
//average(var):返回变量的影子变量值,即读取影子变量shadow variables
average_y = Hidden_layer(x,average_class.average(weights1),average_class.average(bias1),average_class.average(weights2),average_class.average(bias2))
//定义损失函数,labels非独热编码
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.argmax(y_,1))
regularizer = tf.contrib.layers.l2_regularizer(0.0001)
regularization = regularizer(weights1) + regularizer(weights2)
loss = tf.reduce_mean(cross_entropy) + regularization
learning_rate = tf.train.exponential_decay(learning_rate,training_step,mnist.train.num_examples/batch_size,learning_rate_decay)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=training_step)
//综合op,也可以用一行代码完成以下功能:train_op = tf.group(train_step,average_op)
with tf.control_dependencies([train_step,average_op]):
# tf.no_op()表示不执行任何操作
train_op = tf.no_op(name="train")
//计算准确率
correct_prediction = tf.equal(tf.argmax(average_y,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
//准备验证集数据
validate_feed = {x:mnist.validation.images,y_:mnist.validation.labels}
//准备测试集数据
test_feed = {x:mnist.test.images,y_:mnist.test.labels}
for i in range(max_steps):
if i % 1000 == 0:
validate_accuracy = sess.run(accuracy,feed_dict=validate_feed)
print("Afer %d training steps,validation accuracy using average model is %g%%" % (i,validate_accuracy*100))
xs,ys = mnist.train.next_batch(batch_size=100)
sess.run(train_op,feed_dict={x:xs,y_:ys})
test_accuracy = sess.run(accuracy,feed_dict=test_feed)
print("Afer %d training steps,test accuracy using average model is %f%%" % (max_steps,test_accuracy*100))
超参数和验证集
超参数:提前设定好的参数,例如学习率等,这些参数难以通过网络进行优化,需要人为调控。
验证集:从训练集中选择的一部分样本,用来评估模型的效果。