MNIST数字识别问题

MNIST是手写体识别数据集,是大多数深度学习教程的入门资料。本文结合这个数据集,利用TensorFlow搭建神经网络。介绍了变量重用和名称空间的问题。
以下的代码以及内容,基本上来自Tensorflow:实战Google深度学习框架

1 MNIST介绍

MNIST是一个手写体数字识别数据集,它包含了60000张图片作为训练数据,10000张图片作为测试数据。每张图片代表0-9中的其中一个数字,图片大小为28x28,下图显示了一张数字图片以及其数字矩阵在这里插入图片描述

以下代码给出了利用TensorFlow处理MNIST数据集的例子程序

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#载入MNIST数据,如果路径下没有已经下载好的数据,则会自动下载
mnist = input_data.read_data_sets("/path/to/MNIST_data")

#程序会自动分好train,validation,test的数据集
print("data size = %d" %(mnist.train.num_examples))
print("validation size = %d" %(mnist.validation.num_examples))
print("test size = %d" %(mnist.test.num_examples))

#打印其中一个样本的矩阵,样本是一个长度为784的数组
#标签是0-9的其中一个数
print("train data "+ str(mnist.train.images[0]))
print("train data label " + str(mnist.train.labels[0]))

#为了方便使用随机梯度下降,函数含定义了next_batch函数,可以从
#数据集中读取一部分数据用于训练
batch_size = 100
xs,ys = mnist.train.next_batch(batch_size)
print("x shape = "+str(xs.shape))
print("y shape = "+str(ys.shape))

2 TensorFlow训练神经网络

以下给出一个利用TensorFlow搭建一个只有1个隐藏层的神经网络来识别MNIST数据集的数字。其中使用了指数衰减的学习率和L2正则化避免过拟合。以下是完整程序。

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

INPUT_NODE = 784     # 输入节点
OUTPUT_NODE = 10     # 输出节点
LAYER1_NODE = 500    # 隐藏层节点数       
                              
BATCH_SIZE = 100     # 每次batch打包的样本个数        

# 模型相关的参数
LEARNING_RATE_BASE = 0.8   #初始化学习率   
LEARNING_RATE_DECAY = 0.99 #学习率衰减率   
REGULARAZTION_RATE = 0.0001 #正则化的lambda  
TRAINING_STEPS = 5000 #总训练次数

#正向传播算法
#函数参数为输入矢量、连接到隐藏层和输出层的w1 b1, w2 b2,返回输出层结果
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
    layer1 = tf.nn.relu(tf.matmul(input_tensor,weights1)+biases1)
    return tf.matmul(layer1,weights2)+biases2


def train(mnist):
    # 定义输入输出的placeholder
    x = tf.placeholder(dtype=tf.float32,shape=[None,784],name='x-input')
    y_= tf.placeholder(dtype=tf.float32,shape=[None,10],name='y-output')
	
	#初始化传输矩阵
    weight1 = tf.Variable(tf.truncated_normal([INPUT_NODE,LAYER1_NODE],stddev=0.1))
    biases1 = tf.Variable(tf.constant(0.1,shape=[LAYER1_NODE]))
    weight2 = tf.Variable(tf.truncated_normal([LAYER1_NODE,OUTPUT_NODE],stddev=0.1))
    biases2 = tf.Variable(tf.constant(0.1,shape=[OUTPUT_NODE]))
    
    # 计算不前向传播结果
    y = inference(x,None,weight1,biases1,weight2,biases2)
    
    # 定义训练轮数,用于学习率衰减
    global_step = tf.Variable(0,trainable=False)
    
    # 计算交叉熵及其平均值
    #tf.nn.sparse_softmax_cross_entropy_with_logits中
    #labels要求输入正确样本的下标
    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)
    
    # 损失函数的计算,添加正则化项
    regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
    regularization = regularizer(weight1)+regularizer(weight2)
    loss = cross_entropy_mean + regularization
    
    # 设置指数衰减的学习率。
    #设置为训练集过完一遍,学习率再下降
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE,
        LEARNING_RATE_DECAY,
        staircase=True)
        
    # 优化损失函数,利用Adam的话结果不收敛,原因暂时不明确
    train_step = tf.train.MomentumOptimizer(learning_rate,0.5).minimize(loss,global_step=global_step)
    
    # 计算正确率
    #correct_prodiction返回的是bool
    #tf.cast函数将bool值转化为tf.float值,用于计算正确率
    correct_prediction = tf.equal(tf.argmax(y_,1),tf.argmax(y,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
    
    # 初始化会话,并开始训练过程。
    # 每1000轮利用validation set进行一次正确率测试
    # 训练完成后利用test set进行正确率测试
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        validation_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):
            if i%1000==0:
                validation_acc = sess.run(accuracy,feed_dict=validation_feed)
                print("learning rate = " + str(sess.run(learning_rate)))
                print("i = %d,accuracy = %g" %(i,validation_acc))
            xs,ys=mnist.train.next_batch(BATCH_SIZE)
            sess.run(train_step,feed_dict={x:xs,y_:ys})
        
        test_acc = sess.run(accuracy,feed_dict=test_feed)
        print("test accuracy = %g" %(test_acc))

#主函数
def main(argv=None):
    mnist = input_data.read_data_sets("/path/to/data"
, one_hot=True)
    train(mnist)

if __name__=='__main__':
    main()

3 变量管理

在上方的例子中的inference函数中:

def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
    layer1 = tf.nn.relu(tf.matmul(input_tensor,weights1)+biases1)
    return tf.matmul(layer1,weights2)+biases2

需要传入神经网络里的所有权重参数进去,才能计算输出值。当网络的层数少的时候,这种方法问题不大,但是如果层数很多,就需要一个更好的方法来传输这些网络变量了。
TensorFlow提供了通过变量名称来创建或者获取变量,并且通过上下文管理器,使名称相同的变量能够在不同的区域独立存在。实现的机制主要是通过tf.get_variable 和tf.variable_scope。下边介绍一下。

当用tf.get_variable 来创建变量时,其效果跟tf.Variable 是基本等价的:

v = tf.get_variable("v", shape=[1],initializer=tf.constant_initializer(1.0))
v = tf.Variable(tf.constant(1.0,shape=[1],name='v'))

两者的区别在于,tf.get_variable中的变量名称是一个必填的参数,而tf.Variable的变量名称可选。在tf.get_variable中如果变量名称在之前已经声明过的话,会创建失败并报错,防止两个变量名称重复。
如果要用tf.get_variable来获取变量,需要上下文管理器tf.scope函数

with tf.variable_scope("foo"):
    v=tf.get_variable("v",shape=[1],initializer=tf.constant_initializer(1))

#以上代码在foo名称空间中创建了名字为v的变量
#因此在以下代码中尝试再次创建时会报错
with tf.variable_scope("foo"):
    v=tf.get_variable("v",shape=[1],initializer=tf.constant_initializer(1))

#在声明名称空间时,如果reuse声明为true,则可以获取已经声明的变量
with tf.variable_scope("foo",reuse=True):
    v=tf.get_variable("v",shape=[1],initializer=tf.constant_initializer(1))
 
 #但是如果尝试获取没被声明的变量时,则会报错
 #例如下方v变量在bar的名称空间中没被事先声明,因此报错
 with tf.variable_scope("bar",reuse=True):
    v=tf.get_variable("v",shape=[1],initializer=tf.constant_initializer(1))
 
 #名称空间还能够嵌套使用
 with tf.variable_scope("root"):
    print tf.get_variable_scope().reuse  #打印false
    
    with tf.variable_scope("foo", reuse=True):
        print tf.get_variable_scope().reuse #打印true
        
        with tf.variable_scope("bar"):
            print tf.get_variable_scope().reuse#打印False
            
    print tf.get_variable_scope().reuse #打印False

#tf.variable_scope()还有管理变量名称的功能,在不同的名称空间中,变量的名字是不同的

v1 = tf.get_variable("v", [1])
print v1.name #打印v:0  v为变量名称,:0表示这个变量是运算得出的第一个结果

with tf.variable_scope("foo",reuse=True):
    v2 = tf.get_variable("v", [1])#打印foo/v:0
print v2.name

with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v3 = tf.get_variable("v", [1])
        print v3.name  #打印foo/bar/v:0
        
v4 = tf.get_variable("v1", [1])
print v4.name #打印v1:0

#tf.get_variable还支持利用带名称空间的变量名字来获取变量
#以下输出都是True
with tf.variable_scope("",reuse=True):
    v5 = tf.get_variable("foo/bar/v", [1])
    print v5 == v3
    v6 = tf.get_variable("v1", [1])     
    print v6 == v4
    

通过上下文管理器,我们可以优化刚开始构建的神经网络,通过更改inference函数,可以令其更简洁。

def inference(input_tensor):
    with tf.variable_scope('layer1'):
        weights = tf.get_variable('weights',[INPUT_NODE,LAYER1_NODE],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias = tf.get_variable('bias',[LAYER1_NODE],initializer=tf.constant_initializer(0.0))
        layer1 = tf.nn.relu(tf.matmul(input_tensor,weights)+bias)
    
    with tf.variable_scope('layer2'):
        weights = tf.get_variable('weights',[LAYER1_NODE,OUTPUT_NODE],initializer=tf.truncated_normal_initializer(stddev=0.1))
        bias = tf.get_variable('bias',[OUTPUT_NODE],initializer=tf.constant_initializer(0.0))
        layer2 = tf.matmul(layer1,weights)+bias
    return layer2

#同时y的获得方式也要改变
y = inference(x)

现在神经网络的参数声明以及其运算过程都在函数内完成,即使网络变大,也能够保证程序可读性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值