MNIST手写数字识别模型精度测试

MINST数据库是由Yann提供的手写数字数据库文件,其官方下载地址http://yann.lecun.com/exdb/mnist/

数据库的里的图像都是28*28大小的灰度图像,每个像素的是一个八位字节(0~255)

这个数据库主要包含了60000张的训练图像和10000张的测试图像,主要是下面的四个文件

其中:

  训练集(包括5000的验证集):

    Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
    Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)

  测试集:

    Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)
    Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)

我们学习的第一门程序设计语言可能都是从Hello World入手的,在人工智能下的tensorflow框架,也就是说人工智能的Hello World可能就是MNIST数据集识别,下面附上我的代码:

前向传播(mnist_forward.py):

import tensorflow as tf
import tensorflow.contrib as contrib

INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500

def get_weight(shape, regularizer):
    # tf.truncated_normal()从截断的正态分布中输出随机值, shape表示生成张量的维度,stddev是标准差
    w = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
    # 将每个变量的正则化损失加入集合losses中
    if regularizer != None:
        tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(regularizer)(w))
    return w

def get_bias(shape):
    b = tf.Variable(tf.zeros(shape))
    return b

def forward(x, regularizer):
    w1 = get_weight([INPUT_NODE, LAYER1_NODE], regularizer)
    b1 = get_bias([LAYER1_NODE])
    # tf.nn.relu()为激活函数,当x<0时,y=0; 当x>=0时,y=x
    y1 = tf.nn.relu(tf.matmul(x, w1) + b1)

    w2 = get_weight([LAYER1_NODE, OUTPUT_NODE], regularizer)
    b2 = get_bias([OUTPUT_NODE])
    y = tf.matmul(y1, w2) + b2
    return y


简单说明:因为数据库的里的图像都是28*28大小的灰度图像,所以28*28=784,将这个矩阵变成一维向量便成为了一个784维的一维向量作为神经网络的输入节点INPUT_NODE,因为手写的数字的范围是0-9,有10个数字,所以输出节点有10个,因此OUTPUT_NODE=10,LAYER1_NODE便是隐藏节点,节点个数有500个,作为计算。

PS:我们需要获取权重和输出x,通过激活函数tf.nn.relu()进行以上运算,搭建出整个计算图,并返回。

反向传播(mnist_backward.py):

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

BATCH_SIZE = 200
LEARNING_RATE_BASE = 0.1     # 初始学习率
LEARNING_RATE_DECAY = 0.99   # 学习衰减率
REGULARIZER = 0.0001         # 正则率
STEPS = 50000                # 训练轮数
MOVING_AVERAGE_DECAY = 0.99  # 滑动平均衰减率
MODEL_SAVE_PATH = "./model/"# 模型保存路径
MODEL_NAME = "mnist_model"  # 模型名称

def backward(mnist):
    x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])
    y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])
    # 正向传输推算出模型
    y = mnist_forward.forward(x, REGULARIZER)
    global_step = tf.Variable(0, trainable=False)

    # 下面算出包括正则化的损失函数
    # logits为神经网络输出层的输出
    # 传入的label为一个一维的vector,长度等于batch_size,每一个值的取值区间必须是[0,num_classes),其实每一个值就是代表了batch中对应样本的类别
    # tf.argmax(vector, 1):返回的是vector中的最大值的索引号
    ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
    # 矩阵中所有元素求平均值
    cem = tf.reduce_mean(ce)
    # tf.get_collection(‘losses’):返回名称为losses的列表
    # tf.add_n(list):将列表元素相加并返回
    loss = cem + 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
    )


    # 定义训练过程
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)


    # 定义滑动平均
    # tf.train.ExponentialMovingAverage()这个函数用于更新参数,就是采用滑动平均的方法更新参数
    # MOVING_AVERAGE_DECAY是衰减率,用于控制模型的更新速度,设置为接近1的值比较合理
    ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    # apply()方法添加了训练变量的影子副本
    # 返回值:ExponentialMovingAverage对象,通过对象调用apply方法可以通过滑动平均模型来更新参数。
    # tf.trainable_variables返回的是需要训练的变量列表
    # tf.all_variables返回的是所有变量的列表
    ema_op = ema.apply(tf.trainable_variables())
    with tf.control_dependencies([train_step, ema_op]):
        # 里面的内容需要在将train_step、ema_op执行完后才能执行
        # tf.no_op()表示执行完 train_step, ema_op 操作之后什么都不做
        train_op = tf.no_op(name='train')

    saver = tf.train.Saver()

    with tf.Session() as sess:
        # 初始化所有变量
        init_op = tf.global_variables_initializer()
        sess.run(init_op)

        for i in range(STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            # xs= (200, 784)
            # ys= (200, 10)
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
            if i % 1000 == 0:
                print("After %d training step(s) , loss on training batch is %g." % (step, loss_value))
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)


def main():
    mnist = input_data.read_data_sets("../MNIST-data", one_hot=True)
    backward(mnist)

if __name__ == '__main__':
    main()
    # BATCH_SIZE = 200
    # mnist = input_data.read_data_sets("../MNIST-data", one_hot=True)
    # xs, ys = mnist.train.next_batch(BATCH_SIZE)
    # print("xs=", xs.shape)
    # print("ys=", ys.shape)




简单说明:该反向传播将通过滑动平均求 loss 的方法进行参数更新,应该在模型上运用滑动平均能够使整个模型更加健壮。特别注意,x为输出,y_为已知的正确答案,y为通过神经网络输出后的结果,最后将对比y和y_的值来计算模型的精度。

PS:采用反向传播函数,在Tensorflow中复现一张默认的计算图进行计算,时间间隔为5s,利用影子变量,我们对其之前保存的状态没1000轮输出一次,观察结果。

测试结果(我进行了以下对比实验):

case1:

Steps=50000, regularize=0.0001, learning_rate_base=0.1, learning_rate_decay=0.99

                 

Steps=50000, regularize=0.0001, learning_rate_base=0.2, learning_rate_decay=0.99

观察结果:在其他参数不变的情况下,将之前的学习率0.1提高到0.2,在50000轮训练之后的,loss损失函数有了明显降低,说明精度在不断提高,说明适当提高模型的初始学习率可以提高模型精度。

Steps=50000, regularize=0.0001, learning_rate_base=0.2, learning_rate_decay=0.99

以上参数保持不变,我们以上采用的是SGD(梯度下降函数)来更新的参数,并且在提高学习率的同时,loss也在减少,由此我想到了或许采用一下ADAM算法或许会更好,因为ADAM的速度更快,于是我将SGD换成了ADAM,没想到是这个结果.....

思考半天之后我发现或许是我的参数设置不对头,或许该改改参数,熟悉ADAM算法的人应该一看就知道哪里出问题了,应为ADAM需要更小的学习率,于是我将模型的参数改为:

Steps=50000, regularize=0.0001, learning_rate_base=0.001, learning_rate_decay=0.99

结果如下:

天呐,不敢想象仅仅通过了前1000次计算就达到了用SGD50000次左右的结果让人难以执行,,,,

仅仅在2000次就达到了有史以来的最优值

最终结果:

在43000次左右达到了目前为止的全局最优

发布了25 篇原创文章 · 获赞 8 · 访问量 4488

猜你喜欢

转载自blog.csdn.net/weixin_42414405/article/details/91468346