(五)TensorFlow实现MNIST全连接神经网络实践

1 MNSIT数据集

     MNIST是一个非常简单的手写体数字识别数据集,由70000张 28*28像素的黑白图片组成。在进行神经网络模型的设计前,需要先获取这个数据集,Yann LeCun(深度学习的一个大牛)教授的网站 http://yann.lecun.com/exdb/mnist给出了数据集的下载。我们在该网站下载4个压缩包,其中 t10k 开头的压缩包是测试集,train开头的压缩包是训练集。

       下载完数据集后,我们首先对MNIST数据集进行加载,并且测试数据是否正确:

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#指定数据的加载路径
mnist = input_data.read_data_sets("./MNIST_data", one_hot=True)
#输出训练集、测试集的信息
print ("Training data and label size:")
print (mnist.train.images.shape, mnist.train.labels.shape)
print ("Testing data and label size:")
print (mnist.test.images.shape, mnist.test.labels.shape)
print ("Validating data and label size:")
print (mnist.validation.images.shape, mnist.validation.labels.shape)

print ("Example training data:", mnist.train.images[0])
print ("Example training label:", mnist.train.labels[0])

           

       通过上面代码,我们可以看到数据的维度,处理后的图片由二维28*28变为了一维 784,总共有55000张。舍弃图像二维特征而转变为一维的原因是神经网络的输入是一个特征向量,所以在此把一张二维图像的像素矩阵防到一个一维数组中可以方便TensorFlow将图片的像素矩阵提供给神经网络的输入层。

2 网络的设计

      我们设计一个基于MNIST数据集的用于手写体数字识别的全连接神经网络,这个网络只有一个隐藏层,不会输出图片对应的数字,而是输出图片分类的概率。在进行网络设计前,我们需设置网络的相关参数,比如初始学习率参数、学习率衰减参数、训练的轮数和参与每一轮训练的数据batch的大小。

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    #设置每一轮训练的Batch的大小
learning_rate = 0.8     #初始学习率
learning_rate_decay = 0.999     #学习率的衰减
max_steps = 30000       #最大训练步数

#定义存储训练数的变量,在使用Tensorflow训练神经网络时,一般会将代表训练轮数的变量通过trainable参数设置为不可训练
training_step = tf.Variable(0, trainable=False)

    设置完相关参数后,就可以定义网络的权重参数、偏置参数和前向传播过程。我们将网络实现为一个含有单隐藏层的全连接结构,并在得到隐层的时候使用ReLU激活函数。网络的前向传播过程定义为hidden_layer()函数,y得到了前向传播的结果。x在运行会话时会feed图片数据,y_ 在运行会话时feed答案数据(label)。

#定义得到隐藏层和输出层的前向传播计算方式,激活函数用relu()
def hidden_layer(input_tensor,weights1,biases1,weights2,biases2,layer_name):
    layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1)+biases1)
    return tf.matmul(layer1, weights2)+biases2

x = tf.placeholder(tf.float32, [None, 784], name="x-input")
y_ = tf.placeholder(tf.float32, [None, 10], name="y-output")
#生成隐藏层参数,其中weights1包含784*500=392000个参数
weights1 = tf.Variable(tf.truncated_normal([784,500], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[500]))
#生成输出层参数,其中weights2包含500*10=5000个参数
weights2 = tf.Variable(tf.truncated_normal([500, 10], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[10]))
#计算经过神经网络前向传播后得到的y值
y = hidden_layer(x,weights1,biases1,weights2,biases2,'y')

       为了采用随机梯度下降算法训练神经网络时提高模型在测试数据上的表现,TensorFlow提供了一种在变量上使用滑动平均的方法,通常称之为滑动平均模型。实现一个变量的滑动平均需要首先通过 train.ExponentialMovingAverage()函数初始化一个滑动平均类,同时需要向函数提供一个衰减率(Decay)参数,这个衰减率将用于控制模型的更新的速度。

        滑动平均算法会对每一个变量的影子变量进行维护,这个影子变量的初始值就是相应变量的初始值,如果该变量发生变化,则影子变量也会变化。average()函数真正执行了影子变量的计算,在使用时,对其传入需要进行计算的变量即可,然后将滑动平均的方法应用到隐藏层和输出层的权重参数与偏置参数。

#初始化一个滑动平均类,其衰减率为0.99
#为了使模型在训练前期可以更新得更快,这里提供了num_updates参数,并设置为当前网络得训练轮数
averages_class = tf.train.ExponentialMovingAverage(0.99, training_step)
#定义一个更新变量滑动平均值得操作需要向滑动平均类得apply()函数提供一个参数列表
#train_variables()函数返回集合图上 Graph.TRAINABLE_VARIABLES中得元素,这个集合得元素就是所哟没有指定trainable_variables=False得参数
averages_op = averages_class.apply(tf.trainable_variables())

#再次计算经过神经网络前向传播后得到得y值,这里使用了滑动平均,但记住滑动平均值只是一个影子变量
average_y = hidden_layer(x, averages_class.average(weights1),
                         averages_class.average(biases1),
                         averages_class.average(weights2),
                         averages_class.average(biases2),'average_y')

      在得到网络得预测结果后就可以计算交叉熵损失了,我们将 softmax分类法和交叉熵损失得计算搭配在一起使用,所以TensorFlow提供了统一得封装——sparse_softmax_cross_entropy_with_logits()函数,该函数用于分类的样本只能被划分为某一类,适合我们的案例。在得到交叉损失熵后,我们可以计算权重参数的L2正则,并将正则损失和交叉熵损失糅合在一起计算总损失。我们选择原理最简单的随机梯度下降(SGD)优化器,学习率采用指数衰减的形式。

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)
                            #计算L2正则化损失函数
regularization = regularizer(weights1)+regularizer(weights2)
                            #计算模型的正则损失
loss = tf.reduce_mean(cross_entropy)+regularization
                            #总损失

#用指数衰减法设置学习率,这里staircase参数采用默认false,即学习率连续衰减
learning_rate = tf.train.exponential_decay(learning_rate, training_step, mnist.train.num_examples/batch_size,learning_rate_decay)

#使用GradientDescentOptimizer优化算法来优化交叉熵损失和正则化损失
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=training_step)
#在训练这个模型时,没过一遍数据既需要通过反向传播来更新神经网络的参数,有需要每一个参数的滑动平均值
#control_dependencied()用于完成这样的一次性多次操作
#同样的操作也可以使用下面的代码完成:
with tf.control_dependencies([train_step, averages_op]):
    train_op = tf.no_op(name="train")

     equal()函数能够通过True或者False值返回比较之后的结果,由于一个batch中包含多个样本,所以在得到这个比较结果后,我们还需要对其进行数值类型转化以及所有样本上的求平均操作:

crorent_predicition = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))

accuracy = tf.reduce_mean(tf.cast(crorent_predicition, tf.float32))

      随后我们创建会话运行模型,会话内首先要做的事初始化全部已经创建的变量,然后通过for循环执行训练过程的迭代。在此循环中,使用 read_data_sets()函数返回的类提供的 next_batch()函数获取一个 batch的训练数据,并feed给 x 和 y_。循环中还要做的就是执行模型训练结果在验证集上的验证。以下是会话部分代码:

with tf.Session() as sess:
    tf.gloabl_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(max_steps):
        if i % 1000 == 0:
            validate_accuracy = sess.run(accuracy,feed_dict=validate_feed)
            print ("After %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_accuracy)
        print ("After %d training steps, test accuracy using average model is %g%%" %(i, test_accuracy))
        

3 超参数和验证集

     对于一个机器学习算法,通常会设置一些参数来控制算法本身的行为,习惯上将这些参数称为“超参数”,例如初始学习率,学习率衰减以及迭代的轮数等。

      权重参数和偏置参数等会在网络的迭代中通过学习算法来修改自身的值,而与这些参数不同的是,超参数的值不是通过学习算法本身学习得来的,而是我们在程序一开始预先设定好的。出于两点原因我们会将一个参数设置为超参数:第一他非常难以优化;第二他必须作为一个超参数出现。为合理设置超参数,我们一般会选择多次优化试验的方法,通过在模型中尝试不同的超参数并得到模型在测试集上的准确率来判断模型的结果。

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值