神经网络学习(三)比较详细 卷积神经网络原理、手写字体识别(卷积网络实现)

之前写了一篇基于minist数据集(手写数字0-9)的全连接层神经网络,识别率(85%)并不高,这段时间学习了一些卷积神经网络的知识又实践了一把, 识别率(96%左右)确实上来了 ,下面把我的学习过程记录下来以便加深理解也希望可以帮到大家伙儿。

一、准备工作

定义定义两个函数weight_variables(shape)、 bias_variables(shape)用来初始化权重偏置

# 定一个初始化权重的函数
def weight_variables(shape):
    """
    初始化权重的函数
    :param shape:
    :return:
    """
    w = tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=1.0))
    return w
# 定义一个初始化偏置的函数
def bias_variables(shape):
    """
    初始化偏置的函数
    :param shape:
    :return:
    """
    b = tf.Variable(tf.constant(0.0, shape=shape))
    return b

二、自定义卷积模型

1. 老规矩先把位置占下

占位符这里就不赘述了、不明白的可以看上篇。

		x = tf.placeholder(tf.float32, [None, 784])

        y_true = tf.placeholder(tf.float32, [None, 10])

2. 确定第一卷积层Filter_1的数量跟形状

Filter是什么 是干啥的 下面我会在卷积层通过图片1详细说。
这里我指定Filter_1的形状为5×5×1×32,即长=5、 宽=5、 高=1、 个数=32。
ps:看到这部分人会有疑问,为什么Filter_1的形状是5×5×1×32?其实可以指定Filter_1的形状为任意形状,只要其长宽不超过数据集中图片的长跟宽。当然不要太小也别太大,不然训练效果就不好了。其实Filter的形状是卷积模型中可调整的参数之一,其形状直接影响训练效果。

3. 根据Filter形状确定第一卷积层的权重跟偏置

Filter_1的形状有了那么权重跟偏置的形状就确定下来了,我们直接调用上文写的初始化函数。

    # 初始化权重(这里要清楚 权重是跟Filter的形状挂钩的,Filter的形状是什么偏置的形状就是什么)
     w_conv1 = weight_variables([5, 5, 1, 32])  # 5行5列、1个通道、32个Filter

    # 初始化偏置(有几个Filter就有几个偏置bias)
     b_conv1 = bias_variables([32])

4.数据集的形状改变一下

[None,784]单位为张 即None张图片,一张图片的格式为28×28×1。
-1 在tensorflow中代表任意数或不确定数量。

 		# 对x(数据集)进行形状的改变。[None,784]=>[None,28,28,1]
        x_reshape = tf.reshape(x, [-1, 28, 28, 1])

5. 卷积层(convolution)(第一层)

图一动图中四列分别为
1、第一列三张表格代表一张彩色图的三个通道(RGB)
2、第二列为一个Filter
3、第三列为另一个Filter
4、第四列为卷积结果

对Filter的理解
1、可以把Filter看作 过滤网,此过滤网可以有好几层,其层数由数据集中图片的通道数决定。如图1 由第一列可以看到此图片有三个通道,那么第二三列Filter(过滤网)的层数均为3层。
2、一个卷积网络会有多个卷积层,每一卷积层都对应若干个Filter,我们这把这个若干个Filter称作Filter组或是“滤芯”。其实Filter的形状是卷积模型中可调整的参数之一,其形状直接影响训练效果。
3、如图1 每个Filter都有自己的偏置bias,第一个Filter的偏置为1,第二个Filter的偏置为0.

卷积操作流程
1、用Filter去扫描图像矩阵(图1第一列),这个扫描过程就是将Filter矩阵与扫描区域矩阵对应位置相乘然后相加。后再加上Filter对应的偏置bias,最后按行跟列排列,得到卷积的输出(如图中第四列)此过程即为卷积。

2、老规矩 在这还要给神经网络添加非线性功能,也就是添加激活函数。现在深度学习中用到的激活函数99%都是用ReLU。也就是将图1中第四列的值传到ReLU函数中作为ReLU函数的输入。至此卷积层搞定。

3、代码中的参数解析:①x_reshape:训练数据占位符,可以认为它就是我们输入的图片数据(x),因为在训练的过程中我们会把图像数据往这里面放。
②w_conv1:权重(w),我们可以认为这权重就是Filter。所以w的形状跟Filter的形状是一样的。
③strides:滑动步长,Filter在扫描图片矩阵的时候,其左右上下的滑动间隔。tensorflow里面要求strides的形状为1×4,其实在strides=[1, 1, 1, 1]这个数组中只有中间两个数值有用,第一个跟第四个数值都是默认的1。绝大部分Filter的滑动步长(strides)都是1。
④padding:填充标志,看到这里不知道小伙伴们有没有发现一个问题,图1中Filter的滑动步长为2,正好在横向扫描的时候扫描三次,若滑动步长为3,当Filter扫描两次后 我们发现后面还剩1列无法扫描(因为Filter大小为3×3,扫描两次后 图像矩阵中剩余未扫描的部分为3×1)。这时候我们有两种选择:一 剩余的一列我不要了(padding=”VALID“),二 在整个图像矩阵外面在添加两圈零(padding=“SAME”),来补充图像矩阵。
4、图中图像数据非常少,所以卷积后期输出矩阵的形状(行跟列的数量)都很好算,当图像数据非常大时 比如当图像矩阵为1920×1080时 通过人眼是看不出卷积操作后输出矩阵(图1第四行)的形状的。这里给出公式:
公式图像
其中W、H为元图像矩阵的行跟列,F为Filter的大小(比如一个3×3的Filter,其大小为3),P为Padding的值(即当在图像矩阵外圈添加一圈零)p需要手动计算:比如数据宽为28 Filter宽为5,28除5取余为3(当取余数为0时 p=0),p=5-3=2,也就是在补充2列0。(28-5+2*2)/1 +1 = 28,所以卷积后的输出为[None,28,28,32],其中32 为输出矩阵数量。
width跟height为卷积操作后输出矩阵的列数跟行数

# 卷积操作:[None,28,28,1]=>[None,28,28,32]
x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1)

卷积原理 图1

5. 池化层(Pooling)(第一层)

池化操作理解
池化操作跟卷积操作非常相似但池化操作简单的多。一般池化操作分三种(Max Pooling、Mean Pooling、Random Pooling)其中最大值池化Max Pooling、均值池化Mean Pooling最常用。池化操作相对比较简单这里不多赘述,可以参考图2、图3,也可以参考文章。

  # 池化操作(2*2、strides=1、个数=32),[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")

在这里插入图片描述
图2
池化操作
图3

6. 卷积层(convolution)(第二层)

原理与第一层卷积一样。其中Filter_2的 大小=5×5×64 , strides=1

#卷积层二  卷积:(filter_size=5*5*32、filter_number=64、strides=1) =>  激活(tf.n.relu)  =>  池化2*2 strides=2
 		# 随即初始化权重[5,5,32,64]
        w_conv2 = weight_variables([5, 5, 32, 64])

        # 随机初始化偏置
        b_conv2 = bias_variables([64])

        # 卷积([None,14,14,32]->[None,14,14,64])=>激活
        x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, filter=w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2)

7. 池化层(Pooling)(第二层)

同池化层第一层,这里就不再叨叨了。
接下来流程: 全连接层=>优化(梯度下降)=>预测=>保存模型=>利用训练好的模型进行训练。这一系列流程在上篇已经详细介绍
废话不多说直接上代码 ↓↓↓↓↓↓

三、源代码

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

FlAGS = tf.flags.FLAGS
tf.flags.DEFINE_integer("is_train", 0, "指定程序是预测还是训练")


def conv_fc():
    # 获取数据
    mnist = input_data.read_data_sets("./data/mnist/input_data/", one_hot=True)

    # 专门定义一个模型函数,得出输出
    x, y_true, y_predict, w_fc, b_fc = model()

    #  求所有样本的损失、然后求平均值
    with tf.variable_scope("soft_cross"):
        # 求平均交叉熵损失(先经过softmax得到概率,后交叉熵拿损失,后求平均损失)
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict))

    # 梯度下降优化损失
    with tf.variable_scope("optimizer"):
        train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(loss)

    # 计算准确率
    with tf.variable_scope("acc"):
        # 分别拿到预测样本跟目标样本每一行的最大下标 ,若下表相等 返回1 不相等返回0
        equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))

        # 准确率=算平均值 None个样本 [1,0,1,0,1,1,0,1,..............................]
        accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

    # 定一个初始化变量的op
    init_op = tf.global_variables_initializer()

    # 收集变量  (一维)单个数字值收集
    tf.summary.scalar("losses", loss)
    tf.summary.scalar("acc", accuracy)

    # 收集变量 高纬度变量收集
    tf.summary.histogram("weightes", w_fc)
    tf.summary.histogram("biases", b_fc)
    # 定义一个合并变量的操作operation
    merged = tf.summary.merge_all()

    # 定义一个saver用于保存模型
    saver = tf.train.Saver()
    correct = 0
    # 开启会话运行训练
    with tf.Session() as sess:
        filewriter = tf.summary.FileWriter("./temp/summary/test_conv/", graph=sess.graph)
        sess.run(init_op)
        if FlAGS.is_train == 1:
            # 循环进行训练
            for i in range(100000):
                # 取出真实存在的目标值和特征值
                mnist_x, mnist_y = mnist.train.next_batch(50)

                # 运行train_op训练
                sess.run(train_op, feed_dict={x: mnist_x, y_true: mnist_y})

                # 写入每一步训练的值
                summary = sess.run(merged, feed_dict={x: mnist_x, y_true: mnist_y})
                filewriter.add_summary(summary, i)
                print("训练地%d步,准确率为:%f" % (i, sess.run(accuracy, feed_dict={x: mnist_x, y_true: mnist_y})))

            # 保存模型
            saver.save(sess, "./temp/ckpt_conv/fc_model")
        else:

            saver.restore(sess, "./temp/ckpt_conv/fc_model")
            # 如果是0,则作出预测
            for i in range(100):
                # 每次测试一张图片
                x_test, y_test = mnist.test.next_batch(1)

                print("第%d张图片目标是%d,预测结果是:%d" % (
                    i,
                    tf.argmax(y_test, 1).eval(),
                    tf.argmax(sess.run(y_predict, feed_dict={x: x_test, y_true: y_test}), 1).eval()
                ))
                if (tf.argmax(y_test, 1).eval() == tf.argmax(sess.run(y_predict, feed_dict={x: x_test, y_true: y_test}),
                                                             1).eval()):
                    correct += 1
            print("100样本预测准确率:%f" % (correct / 100))
    return None


def model():
    """
    自定义的ziding卷积模型
    :return:
    """
    # 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.float32, [None, 10])
    # 2、卷积层一  卷积:(filter_size=5*5*1、filter_number=32、strides=1) =>  激活(tf.n.relu)  =>  池化(2*2、strides=1、个数=32)
    with tf.variable_scope("conv1"):
        # 初始化权重(这里要清楚 权重是跟Filter的形状挂钩的,Filter的形状是什么偏置的形状就是什么)
        w_conv1 = weight_variables([5, 5, 1, 32])  # 5行5列、1个通道、32个Filter

        # 初始化偏置(有几个Filter就有几个偏置bias)
        b_conv1 = bias_variables([32])

        # 对x(数据集)进行形状的改变。[None,784]=>[None,28,28,1]
        x_reshape = tf.reshape(x, [-1, 28, 28, 1])

        # 卷积操作:[None,28,28,1]=>[None,28,28,32]
        x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1)

        # 池化操作(2*2、strides=1、个数=32),[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、卷积层二  卷积:(filter_size=5*5*32、filter_number=64、strides=1) =>  激活(tf.n.relu)  =>  池化2*2 strides=2
    with tf.variable_scope("conv2"):
        # 随即初始化权重[5,5,32,64]
        w_conv2 = weight_variables([5, 5, 32, 64])

        # 随机初始化偏置
        b_conv2 = bias_variables([64])

        # 卷积([None,14,14,32]->[None,14,14,64])=>激活
        x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, filter=w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2)

        # 池化操作2*2 strides=2,[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")

    # 4、全连接层[None,7,7,64]=>[None,7*7*64]*[7*7*64,10]+[10] = [None,10]
    with tf.variable_scope("conv2"):
        # 随机初始化权重和偏执
        w_fc = weight_variables([7 * 7 * 64, 10])
        b_fc = bias_variables([10])

        # 修改形状 x_pool2[None,7,7,64]=>[None,7*7*64]
        x_fc_reshape = tf.reshape(x_pool2, [-1, 7 * 7 * 64])

        # 进行矩阵预算得出每个样本的10个结果
        y_predict = tf.matmul(x_fc_reshape, w_fc) + b_fc

    return x, y_true, y_predict, w_fc, b_fc


# 定一个初始化权重的函数
def weight_variables(shape):
    """
    初始化权重的函数
    :param shape:
    :return:
    """
    w = tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=1.0))
    return w


# 定义一个初始化偏置的函数
def bias_variables(shape):
    """
    初始化偏置的函数
    :param shape:
    :return:
    """
    b = tf.Variable(tf.constant(0.0, shape=shape))
    return b


if __name__ == "__main__":
    conv_fc()

四、效果一览

1、我这边训练练了10W次 用时15分钟
在这里插入图片描述
2、训练时电脑的运行状态。

在这里插入图片描述
3、用了1000张图片测试模型的可靠程度。如下图 准确率为94.9%
在这里插入图片描述

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于CNN卷积神经网络TensorFlow的中文手写文字识别是一种使用深度学习技术解决中文手写文字识别问题的方法。中文手写文字的复杂性和多样性使得传统的机器学习方法难以有效识别,而CNN的卷积和池化层可以有效地提取特征并保留空间结构信息,从而提高识别准确率。 首先,我们需要构建一个合适的CNN模型。模型可以包含多个卷积层、池化层和全连接层,每个层都具有特定的功能。卷积层用于提取图像的局部特征,而池化层用于减小特征图的尺寸并保持重要信息。全连接层将提取的特征与标签进行映射,以实现识别功能。 然后,我们需要准备大规模的中文手写文字数据集,包含不同风格和字体的文字样本。这些数据集可以用于训练模型和评估模型的准确性。在训练过程中,我们可以使用反向传播算法和梯度下降法来优化模型的参数,使其能够更好地适应手写文字识别任务。 最后,我们可以使用TensorFlow作为开发框架来实现CNN模型。TensorFlow提供了丰富的API和工具,可以方便地构建、训练和评估深度学习模型。通过将中文手写文字图像输入到训练好的模型中,我们可以获得相应的识别结果。 总之,基于CNN卷积神经网络TensorFlow的中文手写文字识别是一种有效的方法。通过合理构建模型、准备大规模数据集和使用TensorFlow进行开发,我们可以实现准确、高效的中文手写文字识别系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值