Tensorflow案例5:CNN算法-Mnist手写数字识别

版权声明:未经本人同意不得转载! https://blog.csdn.net/yanpenggong/article/details/84680415

学习目标

  • 目标
    • 应用tf.nn.conv2d实现卷积计算
    • 应用tf.nn.relu实现激活函数计算
    • 应用tf.nn.max_pool实现池化层的计算
    • 应用卷积神经网路实现图像分类识别
  • 应用
    • CNN-Mnist手写数字识别

1、网络设计

我们自己定义一个卷积神经网络去做识别,这里定义的结构有些是通常大家都会采用的数量以及熟练整个网络计算流程。但是至于怎么定义结构是没办法确定的,也就是神经网络的黑盒子特性,如果想自己设计网络通常还是比较困难的,可以使用一些现有的网络结构如之前的GoogleNet、VGG等等

1.1 网络结构

1.2 具体参数

  • 第一层
    • 卷积:32个filter、大小5*5、strides=1、padding=“SAME”
    • 激活:Relu
    • 池化:大小2x2、strides2
  • 第一层
    • 卷积:64个filter、大小5*5、strides=1、padding=“SAME”
    • 激活:Relu
    • 池化:大小2x2、strides2
  • 全连接层

经过每一层图片数据大小的变化需要确定,Mnist输入的每批次若干图片数据大小为[None, 784],如果要进过卷积计算,需要变成[None, 28, 28, 1]

  • 第一层
    • 卷积:[None, 28, 28, 1]———>[None, 28, 28, 32]
      • 权重数量:[5, 5, 1 ,32]
      • 偏置数量:[32]
    • 激活:[None, 28, 28, 32]———>[None, 28, 28, 32]
    • 池化:[None, 28, 28, 32]———>[None, 14, 14, 32]
  • 第二层
    • 卷积:[None, 14, 14, 32]———>[None, 14, 14, 64]
      • 权重数量:[5, 5, 32 ,64]
      • 偏置数量:[64]
    • 激活:[None, 14, 14, 64]———>[None, 14, 14, 64]
    • 池化:[None, 14, 14, 64]———>[None, 7, 7, 64]
  • 全连接层
    • [None, 7, 7, 64]———>[None, 7 * 7 * 64]
    • 权重数量:[7 * 7 * 64, 10],由分类别数而定
    • 偏置数量:[10],由分类别数而定

2、案例:CNN识别Mnist手写数字

2.1 流程

1、准备数据

2、卷积、激活、池化(两层)

3、全连接层

4、计算损失、优化

5、计算准确率

2.2 代码

  • 网络结构实现

    def conv_model():
    “”"
    自定义的卷积网络结构
    :return: x, y_true, y_predict
    “”"
    # 1、准备数据占位符
    # x [None, 784] y_true [None, 10]
    with tf.variable_scope(“data”):

          x = tf.placeholder(tf.float32, [None, 784])
    
          y_true = tf.placeholder(tf.int32, [None, 10])
    
      # 2、卷积层一 32个filter, 大小5*5,strides=1, padding=“SAME”
    
      with tf.variable_scope("conv1"):
          # 随机初始化这一层卷积权重 [5, 5, 1, 32], 偏置[32]
          w_conv1 = weight_variables([5, 5, 1, 32])
    
          b_conv1 = bias_variables([32])
    
          # 首先进行卷积计算
          # x [None, 784]--->[None, 28, 28, 1]  x_conv1 -->[None, 28, 28, 32]
          x_conv1_reshape = tf.reshape(x, [-1, 28, 28, 1])
          # input-->4D
          x_conv1 = tf.nn.conv2d(x_conv1_reshape, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1
    
          # 进行激活函数计算
          #  x_relu1 -->[None, 28, 28, 32]
          x_relu1 = tf.nn.relu(x_conv1)
    
          # 进行池化层计算
          # 2*2, strides 2
          #  [None, 28, 28, 32]------>[None, 14, 14, 32]
          x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    
      # 3、卷积层二 64个filter, 大小5*5,strides=1,padding=“SAME”
      # 输入:[None, 14, 14, 32]
      with tf.variable_scope("conv2"):
          # 每个filter带32张5*5的观察权重,一共有64个filter去观察
          # 随机初始化这一层卷积权重 [5, 5, 32, 64], 偏置[64]
          w_conv2 = weight_variables([5, 5, 32, 64])
    
          b_conv2 = bias_variables([64])
    
          # 首先进行卷积计算
          # x [None, 14, 14, 32]  x_conv2 -->[None, 14, 14, 64]
          # input-->4D
          x_conv2 = tf.nn.conv2d(x_pool1, w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2
    
          # 进行激活函数计算
          #  x_relu1 -->[None, 28, 28, 32]
          x_relu2 = tf.nn.relu(x_conv2)
    
          # 进行池化层计算
          # 2*2, strides 2
          #  [None, 14, 14, 64]------>x_pool2[None, 7, 7, 64]
          x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    
      # 4、全连接层输出
      # 每个样本输出类别的个数10个结果
      # 输入:x_poll2 = [None, 7, 7, 64]
      # 矩阵运算: [None, 7 * 7 * 64] * [7 * 7 * 64, 10] +[10] = [None, 10]
      with tf.variable_scope("fc"):
          # 确定全连接层权重和偏置
          w_fc = weight_variables([7 * 7 * 64, 10])
    
          b_fc = bias_variables([10])
    
          # 对上一层的输出结果的形状进行处理成2维形状
          x_fc = tf.reshape(x_pool2, [-1, 7 * 7 * 64])
    
          # 进行全连接层运算
          y_predict = tf.matmul(x_fc, w_fc) + b_fc
    
      return x, y_true, y_predict
    
  • 损失计算优化、准确率计算

      # 1、准备数据API
      mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)
    
      # 2、定义模型,两个卷积层、一个全连接层
      x, y_true, y_predict = conv_model()
    
      # 3、softmax计算和损失计算
      with tf.variable_scope("softmax_loss"):
    
          # labels:真实值 [None, 10]  one_hot
          # logits:全脸层的输出[None,10]
          # 返回每个样本的损失组成的列表
          loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true,
                                                                        logits=y_predict))
      # 4、梯度下降损失优化
      with tf.variable_scope("optimizer"):
          # 学习率
          train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
          # train_op = tf.train.AdamOptimizer(0.1).minimize(loss)
    
      # 5、准确率计算
      with tf.variable_scope("accuracy"):
    
          equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
    
          accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
    
      # 初始化变量op
      init_op = tf.global_variables_initializer()
    
  • 会话运行

 # 会话运行
        with tf.Session() as sess:
    
            # 初始化变量
            sess.run(init_op)
    
            # 循环去训练模型
            for i in range(2000):
    
                # 获取数据,实时提供
                # 每步提供50个样本训练
                mnist_x, mnist_y = mnist.train.next_batch(50)
    
                sess.run(train_op, feed_dict={x:mnist_x, y_true: mnist_y})
    
                # 打印准确率大小
                print("第%d步训练的准确率为:--%f" % (i,
                                            sess.run(accuracy, feed_dict={x:mnist_x, y_true: mnist_y})
                                            ))

### 2.3 学习率过大问题

发现当我们设置0.1的学习率之后,准确率一直上不去,并且打印参数发现已经变为NaN,这个地方是不是与之前在做线性回归的时候似曾相识。对于卷积网络来说,更容易发生梯度爆炸现象,只能通过调节学习来避免。

完整代码

# -*- coding=utf-8 -*-
import os
# os.environ["TF_CPP_MIN_LOG_LEVEL"]='1' # 这是默认的显示等级,显示所有信息  
# os.environ["TF_CPP_MIN_LOG_LEVEL"]='2' # 只显示 warning 和 Error   
# os.environ["TF_CPP_MIN_LOG_LEVEL"]='3' # 只显示 Error

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from tensorflow.contrib.slim.python.slim.nets.inception_v3 import inception_v3_base  # TensorFlow网络模型API
# (补充)卷积网络其它用途
# 图像目标检测
# Yolo:GoogleNet+ bounding boxes
# SSD:VGG + region proposals


# 定义一个是否训练/预测的标志
tf.app.flags.DEFINE_integer("is_train", 1, "训练or预测")
# 训练步数
tf.app.flags.DEFINE_integer("train_step", 0, "训练模型的步数")
# 定义模型的路径
tf.app.flags.DEFINE_string("model_dir", " ", "模型保存的路径+模型名字")
FLAGS = tf.app.flags.FLAGS

# 此模型终端的运行方法
# python3 day03_cnn_Mnist.py --is_train=1 --train_step=2000  # 训练
# python3 day03_cnn_Mnist.py --is_train=0  # 预测

# tensorboard 终端查看
# tensorboard --logdir="./temp/summary/"

# 定义两个专门初始化权重和偏置的函数
def weight_variables(shape):
    w = tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=0.01))
    return w


def bias_variables(shape):
    b = tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=0.01))
    return b


def cnn_model():
    """
    自定义CNN 卷积模型
    1) tf.nn.conv2d(input, filter, strides=, padding=, name=None)
        计算给定4-D input和filter张量的2维卷积
        input:给定的输入张量,具有[batch,heigth,width,channel],类型为float32,64
        filter:指定过滤器的权重数量,[filter_height, filter_width, in_channels, out_channels]
        strides:strides = [1, stride, stride, 1],步长
        padding:“SAME”, “VALID”,使用的填充算法的类型,使用“SAME”。其中”VALID”表示滑动超出部分舍弃,“SAME”表示填充,使得变化后height,width一样大

    2) tf.nn.max_pool(value, ksize=, strides=, padding=,name=None)
        输入上执行最大池数
        value:4-D Tensor形状[batch, height, width, channels]
        channel:并不是原始图片的通道数,而是多少filter观察
        ksize:池化窗口大小,[1, ksize, ksize, 1]
        strides:步长大小,[1,strides,strides,1]
        padding:“SAME”, “VALID”,使用的填充算法的类型,默认使用“SAME”

    第一层
    卷积:32个filter、大小 5*5、strides=1、padding="SAME"
    激活:RELU
    池化:大小 2*2 、strides=2
    第二层
    卷积:64个filter、大小 5*5、strides=1、padding="SAME"
    激活:RELU
    池化:大小 2*2 、strides=2
    全连接层:[7*7*64, 10] [10]
    :return:
    """
    # 1、准备数据的占位符,便于后面卷积计算
    # x:[None, 784], y_true=[None, 10]
    with tf.variable_scope("x_data"):
        x = tf.placeholder(dtype=tf.float32, shape=[None, 784], name="x")
        y_true = tf.placeholder(dtype=tf.float32, shape=[None, 10], name="y_true")

    # 2、第一层
    # 卷积:32个filter、大小 5*5、strides=1、padding="SAME"
    # 激活:RELU
    # 池化:大小 2 * 2 、strides = 2
    with tf.variable_scope("conv_1"):
        # 准备权重和偏置
        # 权重数量:[5, 5, 1, 32]
        # 偏置:[32]
        w_conv1 = weight_variables(shape=[5, 5, 1, 32])
        b_conv1 = bias_variables(shape=[32])

        # 特征状况变成4维,用于卷积运算
        x_reshape = tf.reshape(x, [-1, 28, 28, 1])

        # 进行卷积、激活函数运算
        # [None, 28, 28, 1] --> [None, 28, 28, 32]
        # [None, 28, 28, 32] --> [None, 28, 28, 32]
        conv1 = tf.nn.conv2d(x_reshape,
                             w_conv1,
                             strides=[1, 1, 1, 1],
                             padding="SAME",
                             name="conv1") + b_conv1
        x_relu1 = tf.nn.relu(conv1, name="relu1")

        # 进行池化层
        # [None, 28, 28, 32] --> [None, 14, 14, 32]
        x_pool1 = tf.nn.max_pool(x_relu1,
                                 ksize=[1, 2, 2, 1],
                                 strides=[1, 2, 2, 1],
                                 padding="SAME",
                                 name="pool1")

    # 3、第二层
    # 卷积:64个filter、大小 5*5、strides=1、padding="SAME"
    # 激活:RELU
    # 池化:大小 2*2 、strides=2
    with tf.variable_scope("conv_2"):
        # 准备权重和偏置
        # 权重数量:[5, 5, 32, 64]
        # 偏置:[64]
        w_conv2 = weight_variables(shape=[5, 5, 32, 64])
        b_conv2 = bias_variables(shape=[64])

        # 进行卷积、激活运算
        # 卷积:[None, 14, 14, 32] --> [None, 14, 14, 64]
        # 激活:[None, 14, 14, 64] --> [None, 14, 14, 64]
        conv2 = tf.nn.conv2d(x_pool1,
                             w_conv2,
                             strides=[1, 1, 1, 1],
                             padding="SAME",
                             name="conv2") + b_conv2
        x_relu2 = tf.nn.relu(conv2, name="relu2")

        # 进行池化运算
        # 池化:[None, 14, 14, 64]  --> [None, 7, 7, 64]
        x_pool2 = tf.nn.max_pool(x_relu2,
                                 ksize=[1, 2, 2, 1],
                                 strides=[1, 2, 2, 1],
                                 padding="SAME",
                                 name="pool2")

    # 4、全连接层
    # 全连接层:[7*7*64, 10] [10]
    with tf.variable_scope("fc"):
        # 初始化权重,偏置
        w_fc = weight_variables(shape=[7 * 7 * 64, 10])
        b_fc = bias_variables(shape=[10])

        # 矩阵运算转换为二维
        x_fc_reshape = tf.reshape(x_pool2, shape=[-1, 7 * 7 * 64])

        # 全连接层矩阵运算
        y_predict = tf.matmul(x_fc_reshape, w_fc) + b_fc

    return x, y_true, y_predict, w_conv1, w_conv2, b_conv1, b_conv2


def train():
    """
    卷积网络识别训练
    :return:
    """
    # 1、准备数据输入
    mnist = input_data.read_data_sets("../data/mnist/input_data/", one_hot=True)

    # 2、建立卷积网络模型
    # y_true: [None, 10]
    # y_predict: [None, 10]
    x, y_true, y_predict, w_conv1, w_conv2, b_conv1, b_conv2 = cnn_model()

    # 3、根据输出结果与真实结果建立softmax、交叉熵损失计算
    with tf.variable_scope("soft_cross"):
        # 先进行网络输出的值的概率计算softmax,再进行交叉熵损失计算
        all_loss = tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict, name="compute_loss")

        # 求出平均损失
        loss = tf.reduce_mean(all_loss)

    # 4、梯度下降优化
    with tf.variable_scope("GD"):
        train_op = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss=loss)
        # train_op = tf.train.AdamOptimizer(learning_rate=0.1).minimize(loss=loss)

    # 5、准确率计算
    with tf.variable_scope("accuracy"):
        equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
        accuracy = tf.reduce_mean(tf.cast(equal_list, dtype=tf.float32))

    # 6、tensorflowboard展示的数据
    # 1)收集要在tensorflowboard观察的张量值
    # 数值型  --> scalar 准确率, 损失值
    tf.summary.scalar("loss", loss)
    tf.summary.scalar("accuracy", accuracy)

    # 维度高的张量值
    tf.summary.histogram("w1", w_conv1)
    tf.summary.histogram("b1", b_conv1)
    tf.summary.histogram("w2", w_conv2)
    tf.summary.histogram("b2", b_conv2)

    # 2)合并变量
    merged = tf.summary.merge_all()

    # 7、创建保存模型的OP
    saver = tf.train.Saver()

    # 开启会话进行训练
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())

        # 创建tensorboard的events文件
        filte_writer = tf.summary.FileWriter("./temp/summary/", graph=sess.graph)

        # 9、加载本地模型继续训练或者拿来进行预测测试集
        checkoutpoint = tf.train.latest_checkpoint("./temp/cnn_model/")
        # 判断模型是否存在
        if checkoutpoint:
            saver.restore(sess, checkoutpoint)

        # 判断是训练还是预测
        if FLAGS.is_train == 1:
        # 循环步数去训练
            for i in range(2000):
                # 每次给50个样本
                mnist_x, mnist_y = mnist.train.next_batch(50)

                _, loss_run, accuracy_run,summary = sess.run([train_op, loss, accuracy, merged],
                                                     feed_dict={x: mnist_x, y_true: mnist_y})

                # 打印每步训练的效果
                print("第{}步的50个样本损失为:{:.6f}, 准确率为:{:.2%}".format(i, loss_run, accuracy_run))

                # 3) 写入运行的结果到文件当中
                filte_writer.add_summary(summary, i)

                # 每隔100步保存一次模型的参数
                if i % 100 == 0:
                    # saver.save(sess, FLAGS.model_dir)
                    saver.save(sess, save_path="./temp/cnn_model/cnn_model")
        else:
            # 进行预测
            # 导入模型
            # 加载模型,从模型当中找出与当前训练的模型代码当中(名字一样的OP操作),覆盖原来的值
            checkoutpoint = tf.train.latest_checkpoint("./temp/cnn_model/")
            # 判断模型是否存在
            if checkoutpoint:
                saver.restore(sess, checkoutpoint)

                # 预测100个样本
                N= 200
                a = 0
                for i in range(N):
                    image, label = mnist.test.next_batch(1)
                    # 直接运行网络的输出预测结果
                    print("第{0}样本,真实的图片数字为:{1}, 神经网络预测的数字为:{2}".format(
                    # print("sample:{0},image_true_num:{1}, NN_predict_num:{2}".format(
                        i,
                        tf.argmax(label, 1).eval(),
                        tf.argmax(sess.run(y_predict, feed_dict={x: image, y_true: label}), 1).eval())
                    )
                    if tf.argmax(label, 1).eval() == tf.argmax(sess.run(y_predict, feed_dict={x: image, y_true: label}), 1).eval():
                        a += 1
                test_accuracy = a/N

                print("测试正确率:{:.2%}".format(test_accuracy))
            else:
                print("模型不存在,checkoutpoint 请输出入正确的模型路径")

    return None


if __name__ == '__main__':
    train()

3、拓展-Tensorflow高级API实现结构

高级API可以更快的构建模型,但是对神经网络的运行流程了解清晰还是需要去使用底层API去构建模型,更好理解网络的原理

https://www.tensorflow.org/tutorials/layers

def cnn_model_fn(features, labels, mode):
  """Model function for CNN."""
  # Input Layer
  input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

  # Convolutional Layer #1
  conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=32,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

  # Pooling Layer #1
  pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

  # Convolutional Layer #2 and Pooling Layer #2
  conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=64,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)
  pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

  # 全连接层计算+dropout
  pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
  dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
  dropout = tf.layers.dropout(
      inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

  # 得出预测结果
  logits = tf.layers.dense(inputs=dropout, units=10)

  predictions = {
      # Generate predictions (for PREDICT and EVAL mode)
      "classes": tf.argmax(input=logits, axis=1),
      # Add `softmax_tensor` to the graph. It is used for PREDICT and by the
      # `logging_hook`.
      "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
  }

  if mode == tf.estimator.ModeKeys.PREDICT:
    return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

  # 计算损失
  loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

  # 配置train_op
  if mode == tf.estimator.ModeKeys.TRAIN:
    optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
    train_op = optimizer.minimize(
        loss=loss,
        global_step=tf.train.get_global_step())
    return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

  # 评估模型
  eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(
          labels=labels, predictions=predictions["classes"])}
  return tf.estimator.EstimatorSpec(
      mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

猜你喜欢

转载自blog.csdn.net/yanpenggong/article/details/84680415
今日推荐