欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

经典卷积网络模型LeNet-5模型来解决MNIST数字识别问题(主要解决验证集正确率低的问题)

程序员文章站 2022-04-30 20:12:48
...

LeNet-5模型不是重点,重点是我当时遇到的问题,不知道你遇到了没?是不是发现你训练的正确率跟书本上或者正常情况下的相差甚远,尤其是在验证集上的正确率我当时才0.1,而我参考的那本书(《TensorFlow实战Google深度学习框架》)上的正确率是0.99!

解决办法:当时网上查找原因,下面这篇博客https://blog.csdn.net/wangdong2017/article/details/90176323说的很详细。但是没能解决。我的解决办法是调学习率,简单粗暴的办法。将mnist_train.py中的基础学习率修改为LEARNING_RATE_BASE = 0.01就OK啦。

这个模型很老了,我这里直接上代码吧!

mnist_inference.py中的代码:

import tensorflow as tf

#定义神经网络的相关参数
INPUT_NODE = 784
OUTPUT_NODE = 10

IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10

#第一层卷积层的尺寸和深度
CONV1_DEEP = 32
CONV1_SIZE = 5
#第二层卷积层的深度和尺寸
CONV2_DEEP = 64
CONV2_SIZE = 5
#全连接层的节点个数
FC_SIZE = 512

# def get_weight_variable(shape,regularizer):
#     weights = tf.get_variable('weights',shape,
#                               initializer=tf.truncated_normal_initializer(stddev=0.1))
#     if regularizer != None:
#         tf.add_to_collection('losses',regularizer(weights))
#     return weights

#定义卷积神经网络的前向传播过程,这里新添加了一个参数train用于区分训练过程和测试过程
def inference(input_tensor,train,regularizer):
    #声明第一层卷积层的变量并实现前向传播的过程。
    with tf.variable_scope('layer1_conv1'):
        conv1_weights = tf.get_variable(
            "weight",[CONV1_SIZE,CONV1_SIZE,NUM_CHANNELS,CONV1_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1)
        )
        conv1_biases = tf.get_variable(
            'biases',[CONV1_DEEP],initializer=tf.constant_initializer(0.0))
        #使用边长为5,深度为32的过滤器,过滤器移动步长为1,使用全零填充
        conv1 = tf.nn.conv2d(
            input_tensor,conv1_weights,strides=[1,1,1,1],padding="SAME")
        reul1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))

    #类似的声明第二层池化层的前向传播过程。
    #选用最大池化层,池化过滤器的边长为2,全零填充,步长为2。
    with tf.name_scope('layer2-pool1'):
       pool1 = tf.nn.max_pool(
           reul1,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")

    #声明第三层卷积层的变量并实现前向传播过程。
    with tf.variable_scope('layer3-conv2'):
        conv2_weights = tf.get_variable(
            'weight',[CONV2_SIZE,CONV2_SIZE,CONV1_DEEP,CONV2_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable(
            'bias',[CONV2_DEEP],
            initializer=tf.constant_initializer(0.0))
        #使用边长为5,深度为64的过滤器,过滤器移动步长为1,使用全零填充
        conv2 = tf.nn.conv2d(pool1,conv2_weights,strides=[1,1,1,1],padding='SAME')
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2,conv2_biases))

    #实现第四层 池化层的前向传播过程。这一层和第二层的结构一样
    with tf.name_scope('layer4-pool2'):
        pool2 = tf.nn.max_pool(relu2,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

    #第5层为全连接层,将第四层的池化层的输出转化为第5层的输入格式。
    #第四层是7*7*64的矩阵 第5层是输入格式为向量,需要将第四层的矩阵拉成向量
    pool_shape = pool2.get_shape().as_list()
    #pool_shape[0]为一个batch中数据的个数
    nodes = pool_shape[1]*pool_shape[2]*pool_shape[3]
    #通过tf.reshape函数将第四层变成一个batch的向量
    reshaped = tf.reshape(pool2,[pool_shape[0],nodes])

    #声明第5层全连接层的变量并实现前向传播的过程
    with tf.variable_scope('layer5-fc1'):
        fc1_weights = tf.get_variable(
            'weight',[nodes,FC_SIZE],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        #只有全连接层的权值需要加入正则化
        if regularizer != None:
            tf.add_to_collection('losses',regularizer(fc1_weights))
        fc1_biases = tf.get_variable(
            'bias',[FC_SIZE],initializer=tf.constant_initializer(0.1))

        fc1 = tf.nn.relu(tf.matmul(reshaped,fc1_weights) + fc1_biases)
        if train:
            fc1 = tf.nn.dropout(fc1,0.5)

    #声明第六层全连接层的变量并实现前向传播的过程
    with tf.variable_scope('layer6-fc2'):
        fc2_weights = tf.get_variable(
            'weight', [FC_SIZE, NUM_LABELS],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        # 只有全连接层的权值需要加入正则化
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc2_weights))
        fc2_biases = tf.get_variable(
            'bias', [NUM_LABELS], initializer=tf.constant_initializer(0.1))

        logit = tf.matmul(fc1,fc2_weights)+fc2_biases

    #返回第六层的输出
    return logit

mnist_train.py中的代码:

import tensorflow as tf
import numpy as np
import os
from tensorflow.examples.tutorials.mnist import input_data

#加载mnist_inference.py中定义的常量和前向传播的函数
import mnist_inference

#配置神经网络的参数
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 30000
MOVING_AVERAGE_DECAY = 0.99

#模型保存的路径和文件名
MODEL_SAVE_PATH = 'model'
MODEL_NAME = 'model.ckpt'

def train(mnist):
    #将处理的输入数据的计算都放在名字为input的命名空间下
    with tf.name_scope('input'):
        #定义输入输出placeholder
        x = tf.placeholder(tf.float32,
                           [BATCH_SIZE,
                            mnist_inference.IMAGE_SIZE,
                            mnist_inference.IMAGE_SIZE,
                            mnist_inference.NUM_CHANNELS
                           ],
                           name='x-input')
        y_ = tf.placeholder(tf.float32,[None,mnist_inference.OUTPUT_NODE],name='y-input')

    regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)#L2正则化
    #直接使用mnsit_inference中定义的前向传播过程
    y = mnist_inference.inference(x,True,regularizer)
    global_step = tf.Variable(0,trainable=False)

    #定义损失函数、学习率、滑动平均操作及训练过程
    #将处理滑动平均相关的计算都放在moving_average的命名空间下
    with tf.name_scope('moving_average'):
        variable_averages = tf.train.ExponentialMovingAverage(
            MOVING_AVERAGE_DECAY,global_step)
        variable_averages_op = variable_averages.apply(tf.trainable_variables())
    #将计算损失函数相关的计算放在名为loss_function的命名空间下
    with tf.name_scope('loss_function'):
        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)
        loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
    #将剩下的放在'train_step'
    with tf.name_scope('train_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')

    #初始化TensorFlow持久化层
    saver = tf.train.Saver()
    with tf.Session() as sess:
        tf.global_variables_initializer().run()

        for i in range(TRAINING_STEPS):
            xs,ys = mnist.train.next_batch(BATCH_SIZE)
            reshaped_xs = np.reshape(xs,(BATCH_SIZE,
                                         mnist_inference.IMAGE_SIZE,
                                         mnist_inference.IMAGE_SIZE,
                                         mnist_inference.NUM_CHANNELS
                                         ))
            _,loss_value,step = sess.run([train_op,loss,global_step],
                                         feed_dict={x:reshaped_xs,y_:ys})
            #每1000轮保存一次模型
            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)
    #将当前的计算图输出到TensorBoard日志文件
    writer = tf.summary.FileWriter('log',tf.get_default_graph())
    writer.close()

def main(argv = None):
    mnsit = input_data.read_data_sets('mnsit_data',one_hot=True)
    train(mnsit)

if __name__ == '__main__':
    tf.app.run()

 mnist_eval.py中的代码:

import tensorflow as tf
import time
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

#加载mnist_inference.py中定义的常量和前向传播的函数
import mnist_inference
import mnist_train

#每10秒加载一次最新的模型,并在测试集上测试最新模型的正确率
EVAL_INTERVAL_SECS = 10

def evaluate(mnist):
    with tf.Graph().as_default() as g:
        #定义输入输出placeholder
        x = tf.placeholder(tf.float32,
                           [mnist.validation.images.shape[0],
                            mnist_inference.IMAGE_SIZE,
                            mnist_inference.IMAGE_SIZE,
                            mnist_inference.NUM_CHANNELS],
                           name='x-input')
        # x = tf.placeholder(
        #     tf.float32,[None,mnist_inference.INPUT_NODE],name='x-input'
        # )
        y_ = tf.placeholder(tf.float32,[None,mnist_inference.OUTPUT_NODE],name='y-input')

        xs = mnist.validation.images
        reshaped_xs = np.reshape(xs,[mnist.validation.images.shape[0],
            mnist_inference.IMAGE_SIZE,
            mnist_inference.IMAGE_SIZE,
            mnist_inference.NUM_CHANNELS])

        validate_feed = {x:reshaped_xs,
                         y_:mnist.validation.labels}

        #直接使用mnsit_inference中定义的前向传播过程
        y = mnist_inference.inference(x,None,None)
        #使用前向传播的结果计算正确率。如果需要对未知的样例进行分类,那么使用
        #tf.argmax(y,1)就可以得到输入样例的预测类别
        correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

        #定义损失函数、学习率、滑动平均操作及训练过程
        variable_averages = tf.train.ExponentialMovingAverage(
            mnist_train.MOVING_AVERAGE_DECAY)
        variables_to_restore = variable_averages.variables_to_restore()
        saver = tf.train.Saver(variables_to_restore)

        with tf.Session() as sess:
            ckpt = tf.train.get_checkpoint_state(mnist_train.MODEL_SAVE_PATH)
            if ckpt and ckpt.model_checkpoint_path:
                #加载模型
                saver.restore(sess,ckpt.model_checkpoint_path)
                #通过文件名得到当时迭代的轮数
                global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
                accuracy_score = sess.run(accuracy,feed_dict=validate_feed)

                print('After %s training step(s),validation accuracy= %g.'%(global_step,accuracy_score))
            else:
                print('No checkpoint file found')
                return

        #每隔10秒调用一次计算正确率以检测训练过程中正确率的变化
        # while True:
        #     with tf.Session() as sess:
        #         ckpt = tf.train.get_checkpoint_state(mnist_train.MODEL_SAVE_PATH)
        #         if ckpt and ckpt.model_checkpoint_path:
        #             #加载模型
        #             saver.restore(sess,ckpt.model_checkpoint_path)
        #             #通过文件名得到当时迭代的轮数
        #             global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
        #             accuracy_score = sess.run(accuracy,feed_dict=validate_feed)
        #
        #             print('After %s training step(s),validation accuracy= %g.'%(global_step,accuracy_score))
        #         else:
        #             print('No checkpoint file found')
        #             return
        #     time.sleep(EVAL_INTERVAL_SECS)

def main(argv = None):
    mnsit = input_data.read_data_sets('mnsit_data',one_hot=True)
    evaluate(mnsit)

if __name__ == '__main__':
    tf.app.run()
下面是我的文件结构:
经典卷积网络模型LeNet-5模型来解决MNIST数字识别问题(主要解决验证集正确率低的问题)
图1  文件结构