图片 12

4用TensorFlow做Kaggle“手写识别”达到98%准确率-详解

前面我们曾有篇文章中提到过关于用tensorflow训练手写2828像素点的数字的识别,在那篇文章中我们把手写数字图像直接碾压成了一个784列的数据进行识别,但实际上,这个图像是2828长宽结构的,我们这次使用CNN卷积神经网络来进行识别。

这是一个TensorFlow的系列文章,本文是第三篇,在这个系列中,你讲了解到机器学习的一些基本概念、TensorFlow的使用,并能实际完成手写数字识别、图像分类、风格迁移等实战项目。

卷积神经网络我的理解是部分模仿了人眼的功能。
我们在看一个图像时不是一个像素点一个像素点去分辨的,我们的眼睛天然地具有大局观,我们看到某个图像时自动地会把其中的细节部分给聚合起来进行识别,相反,如果我们用个放大镜看到其中的各个像素点时反而不知道这是啥东西了。

文章将尽量用平实的语言描述、少用公式、多用代码截图,总之这将是一份很赞的入门指南。欢迎分享/关注。

因此卷积神经网络就把每个像素点的图像进行一定程度上的模糊化,而怎么进行模糊化呢?它是通过选择一小片的区域范围,把这小片中的图像数据缩小其长宽,但增加其高度值。然后进行某种计算,最终达到有点类似模糊化图像的目的,但这个模糊化的图像中反而能够比较容易识别出相应的边界及形状。

图片 1

具体大家可以到网上搜索相关的理论知识,这里不细讲,只专注于如何在tensorflow中实现
CNN的功能。

上一期,我们用Tensorflow实现了Kaggle的手写识别项目,但准确率比较低,只有92%,这次我们打算把识别的准确率提升到98%以上。

之前在tensorflow分类-【老鱼学tensorflow】中已经用一般的神经网络进行过手写数字的识别,我们在那个程序的基础上来进行,那篇文章的地址为:http://www.cnblogs.com/dreampursuer/p/8026866.html

为什么不是上次说的提升到99%以上呢?因为92%到98%是比较容易的,而再从98%到99%是要费不少功夫的,一篇文章难以承载这么多内容,所以将会分成两篇文章,首先是从92%到98%,下一次是从98%到99%。

import tensorflow as tf

# 准备数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)

def add_layer(inputs, in_size, out_size, activation_function=None):
    """
    添加层
    :param inputs: 输入数据
    :param in_size: 输入数据的列数
    :param out_size: 输出数据的列数
    :param activation_function: 激励函数
    :return:
    """

    # 定义权重,初始时使用随机变量,可以简单理解为在进行梯度下降时的随机初始点,这个随机初始点要比0值好,因为如果是0值的话,反复计算就一直是固定在0中,导致可能下降不到其它位置去。
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    # 偏置shape为1行out_size列
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 建立神经网络线性公式:inputs * Weights + biases,我们大脑中的神经元的传递基本上也是类似这样的线性公式,这里的权重就是每个神经元传递某信号的强弱系数,偏置值是指这个神经元的原先所拥有的电位高低值
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        # 如果没有设置激活函数,则直接就把当前信号原封不动地传递出去
        outputs = Wx_plus_b
    else:
        # 如果设置了激活函数,则会由此激活函数来对信号进行传递或抑制
        outputs = activation_function(Wx_plus_b)
    return outputs

# 定义输入数据
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那个one hot结构的数据

# 定义层,输入为xs,其有28*28列,输出为10列one hot结构的数据,激励函数为softmax,对于one hot类型的数据,一般激励函数就使用softmax
prediction = add_layer(xs, 28*28, 10, activation_function=tf.nn.softmax)

# 定义loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)


def computer_accuracy(v_xs, v_ys):
    """
    计算准确度
    :param v_xs:
    :param v_ys:
    :return:
    """
    # predication是从外部获得的变量
    global prediction
    # 根据小批量输入的值计算预测值
    y_pre = sess.run(prediction, feed_dict={xs:v_xs})
    correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys})
    return result

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys})
    if i % 50 == 0:
        # 每隔50条打印一下预测的准确率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

不要小看提升1%,越往后面,难度就越大。如果我们做到99%准确率,在Kaggle的手写识别这个项目上,也就进入了前25%了,可以说入门了。

添加必要的函数

一、回顾上期

上期我们学习了梯度下降、神经网络、损失函数、交叉熵等概念,然后用42000张图片数据训练了一个简单的神经网络,准确度92%。可以说,这只是一个Hello
World。

图片 2

Hello World

生成权重变量

# 生成权重变量
def weight_variable(shape):
    # 产生一个随机变量
    init = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(init)

二、如何进行改进

首先,这次我们将使用卷积神经网络来进行图片识别。众所周知,卷积神经网络对于图片识别是非常有效的。

这里我打算这样来构建这个卷积神经网络:

卷积层1+池化层1+卷积层2+池化层2+全连接1+Dropout层+输出层

然而,什么是卷积神经网络?什么是卷积层、池化层、全连接层?Dropout又是什么鬼?

1 什么是卷积神经网络?

我们人看到一幅图像,眨眼之间就知道图像中有什么,图像中的主体在干什么。但计算机不同,计算机看到的每一副图像都是一个数字矩阵。那我们怎么让计算机从一个个数字矩阵中得到有用的信息呢,比如边缘,角点?更甚一点,怎么让计算机理解图像呢?

对图像进行卷积,就是接近目标的第一步。

图像在计算机里的表示可能是这样的:

图片 3

一张图片

对图像卷积,就是求卷积核作用在图像后,得到的图像对于该卷积核的累加数值。这些累加的数值可以代表这个图片的一些特征。

如果是针对猫进行识别,人可能知道猫头,猫尾巴等特征。CNN对图片进行处理后,也会学习到一些特征,它可能不知道猫头、猫尾巴这些特征,但也会识别出一些我们可能看不出来的特征,CNN通过这些学习到的特征去做判断。

2 什么是卷积层?

卷积层的作用是指对图片的矩阵进行卷积运算,得到一些数值,作为图片的某些特征

3 什么是池化层?

池化曾的作用是对上层的数据进行采样,也就是只留下一部分,这样的作用是可以缩小数据量和模糊特征。

4 什么是全连接层?

全连接层就是连在最后的分类器。前面卷积层和池化层进行处理后,得到了很多的特征,全连接层使用这些特征进行分类。比如识别数字,那就是对0~9的十个类别进行分类。

5 Dropout是什么?

Dropout层是为了防止CNN对训练样本过拟合,而导致处理新样本的时候效果不好,采取的丢弃部分激活参数的处理方式。

这里对这些概念的解释都是比较简单的,如果希望详细了解,可以看知乎的这个链接:

CNN卷积神经网络是什么?

定义bias变量

# 定义bias变量
def bias_variable(shape):
    # bias的初始值比权重值稍微简单点,直接用非0常量定义就可以
    init = tf.constant(0.1, shape=shape)
    return tf.Variable(init)

三、 代码实现

图片 4

图片 5

1 标签的处理

图片 6

2 把数据分为训练集和验证集

图片 7

3 定义处理数据的函数

图片 8

4 定义网络的结构

图片 9

5 定义各类参数

图片 10

6 进行训练

定义卷积神经网络层

# 定义卷积神经网络层
def conv2d(x, W):
    # strides:结构为[1, x方向上的步长,y方向上的步长, 1],这里x方向上的步长和y方向上的步长都设置为1
    # padding可选值有VALID和SAME,VALID方式会在边界处比原始图片小一点,而SAME方式会在边界处用0来补充,从而保持跟原始图相同的大小
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

四、生成结果

这里迭代20个周期:

图片 11

7 验证集上的准确度

然后我们使用这个模型对Kaggle的测试集进行预测,并生成cvs格式的结果

图片 12

8 生成结果

这里建议跑30轮以上,因为在验证集上有98.35%准确率,上传到Kaggle往往就只有百分之九十七点几的准确率了

祝大家好运~~~~

附带之前的三篇文章:

五分钟带你入门TensorFlow

零基础用TensorFlow玩转Kaggle的“手写识别”

这里剧透一下,下一篇文章将会使用Keras对模型进行重构和优化,使得模型准确率达到99%以上,在Kaggle进入前20%

想第一时间看到下一篇优化模型的文章?抓紧,喜欢和关注吧~~~

定义pooling

# 为了防止跨步太大丢失掉信息,我们会在中间建立一个pooling,使其跨度减小,但在pooling时跨度可以变大一点,这样在最后的图片生成时可以把大小减小下来但同时又尽可能保存了相关的信息
def max_pool_2x2(x):
    # strides结构依然为:[1, x方向上的步长,y方向上的步长, 1],这里x方向上的步长和y方向上的步长都设置为2,这样在pool时把图像的大小给减小了
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

编写主程序部分

定义输入数据

# 定义输入数据
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那个one hot结构的数据
keep_prob = tf.placeholder(tf.float32)
# 为了使用卷积神经网络,我们需要把原始的一维的数据变换成长宽表示的平面图的数据,把xs的形状变成[-1,28,28,1],-1代表先不考虑输入的图片例子多少这个维度,
# 后面的1是channel的数量,因为我们输入的图片是黑白的,因此channel是1,例如如果是RGB图像,那么channel就是3。
x_image = tf.reshape(xs, [-1, 28, 28, 1])

定义卷积层1

# 定义卷积层1,以5*5的面积进行扫描,因为黑白图片channel是1所以输入是1,输出是32个高度的值
W_conv1 = weight_variable([5, 5, 1, 32])
# bias的大小是32个长度,因此我们传入它的shape为[32]
b_conv1 = bias_variable([32])
# 定义好了Weight和bias,我们就可以定义卷积神经网络的第一个卷积层h_conv1=conv2d(x_image,W_conv1)+b_conv1,同时我们对h_conv1进行非线性处理,
# 也就是激活函数来处理喽,这里我们用的是tf.nn.relu(修正线性单元)来处理,要注意的是,因为采用了SAME的padding方式,输出图片的大小没有变化依然是28x28,
# 只是厚度变厚了,因此现在的输出大小就变成了28x28x32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# 最后我们再进行pooling的处理就ok啦,经过pooling的处理,输出大小就变为了14x14x32
h_pool1 = max_pool_2x2(h_conv1)

定义卷积层2

# 定义卷积层2
# 扫描的面积还是定义成5*5,输入大小为32,因为卷积层1中输出为32就被设置为这里的输入大小了。输出大小设定为64,也就是变得更高了
W_conv2 = weight_variable([5, 5, 32, 64])
# bias的大小是64个长度,因此我们传入它的shape为[64]
b_conv2 = bias_variable([64])
# 定义好了Weight和bias,我们就可以定义卷积神经网络的第二个卷积层h_conv2=conv2d(h_pool1,W_conv2)+b_conv2,同时我们对h_conv2进行非线性处理,
# 也就是激活函数来处理喽,这里我们用的是tf.nn.relu(修正线性单元)来处理,要注意的是,因为采用了SAME的padding方式,输出图片的大小没有变化依然是在第一层卷集层输出时的14*14,
# 只是厚度变厚了,因此现在的输出大小就变成了14x14x64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# 最后我们再进行pooling的处理就ok啦,经过pooling的处理,输出大小就变为了7x7x64
h_pool2 = max_pool_2x2(h_conv2)

定义神经网络全连接层1

# 定义神经网络全连接层1
# 其形状为h_pool2的输出形状7*7*64,输出为1024个神经元
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把h_pool2的输出的包含长宽平面的信息形状转换成一个维度的数据,相当于变平的操作:[n_samples, 7, 7, 64] => [n_samples, 7*7*64]
h_pool1_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool1_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

定义神经网络全连接层2

# 定义神经网络全连接层2
# 其输入为全连接层1的输出1024,输出为0-9数字的one hot格式,因此为10列
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

定义loss值

# 定义loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
# 对于比较庞大的系统可以用AdamOptimizer比较好一点
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

执行

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
    if i % 50 == 0:
        # 每隔50条打印一下预测的准确率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

全部代码

import tensorflow as tf

def add_layer(inputs, in_size, out_size, activation_function=None):
    """
    添加层
    :param inputs: 输入数据
    :param in_size: 输入数据的列数
    :param out_size: 输出数据的列数
    :param activation_function: 激励函数
    :return:
    """

    # 定义权重,初始时使用随机变量,可以简单理解为在进行梯度下降时的随机初始点,这个随机初始点要比0值好,因为如果是0值的话,反复计算就一直是固定在0中,导致可能下降不到其它位置去。
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    # 偏置shape为1行out_size列
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 建立神经网络线性公式:inputs * Weights + biases,我们大脑中的神经元的传递基本上也是类似这样的线性公式,这里的权重就是每个神经元传递某信号的强弱系数,偏置值是指这个神经元的原先所拥有的电位高低值
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        # 如果没有设置激活函数,则直接就把当前信号原封不动地传递出去
        outputs = Wx_plus_b
    else:
        # 如果设置了激活函数,则会由此激活函数来对信号进行传递或抑制
        outputs = activation_function(Wx_plus_b)
    return outputs

def computer_accuracy(v_xs, v_ys):
    """
    计算准确度
    :param v_xs:
    :param v_ys:
    :return:
    """
    # predication是从外部获得的变量
    global prediction
    # 根据小批量输入的值计算预测值
    y_pre = sess.run(prediction, feed_dict={xs:v_xs, keep_prob: 1})
    correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys, keep_prob: 1})
    return result


# 生成权重变量
def weight_variable(shape):
    # 产生一个随机变量
    init = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(init)

# 定义bias变量
def bias_variable(shape):
    # bias的初始值比权重值稍微简单点,直接用非0常量定义就可以
    init = tf.constant(0.1, shape=shape)
    return tf.Variable(init)

# 定义卷积神经网络层
def conv2d(x, W):
    # strides:结构为[1, x方向上的步长,y方向上的步长, 1],这里x方向上的步长和y方向上的步长都设置为1
    # padding可选值有VALID和SAME,VALID方式会在边界处比原始图片小一点,而SAME方式会在边界处用0来补充,从而保持跟原始图相同的大小
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

# 为了防止跨步太大丢失掉信息,我们会在中间建立一个pooling,使其跨度减小,但在pooling时跨度可以变大一点,这样在最后的图片生成时可以把大小减小下来但同时又尽可能保存了相关的信息
def max_pool_2x2(x):
    # strides结构依然为:[1, x方向上的步长,y方向上的步长, 1],这里x方向上的步长和y方向上的步长都设置为2,这样在pool时把图像的大小给减小了
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')


# 准备数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)


# 定义输入数据
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那个one hot结构的数据
keep_prob = tf.placeholder(tf.float32)
# 为了使用卷积神经网络,我们需要把原始的一维的数据变换成长宽表示的平面图的数据,把xs的形状变成[-1,28,28,1],-1代表先不考虑输入的图片例子多少这个维度,
# 后面的1是channel的数量,因为我们输入的图片是黑白的,因此channel是1,例如如果是RGB图像,那么channel就是3。
x_image = tf.reshape(xs, [-1, 28, 28, 1])

# 定义卷积层1,以5*5的面积进行扫描,因为黑白图片channel是1所以输入是1,输出是32个高度的值
W_conv1 = weight_variable([5, 5, 1, 32])
# bias的大小是32个长度,因此我们传入它的shape为[32]
b_conv1 = bias_variable([32])
# 定义好了Weight和bias,我们就可以定义卷积神经网络的第一个卷积层h_conv1=conv2d(x_image,W_conv1)+b_conv1,同时我们对h_conv1进行非线性处理,
# 也就是激活函数来处理喽,这里我们用的是tf.nn.relu(修正线性单元)来处理,要注意的是,因为采用了SAME的padding方式,输出图片的大小没有变化依然是28x28,
# 只是厚度变厚了,因此现在的输出大小就变成了28x28x32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# 最后我们再进行pooling的处理就ok啦,经过pooling的处理,输出大小就变为了14x14x32
h_pool1 = max_pool_2x2(h_conv1)

# 定义卷积层2
# 扫描的面积还是定义成5*5,输入大小为32,因为卷积层1中输出为32就被设置为这里的输入大小了。输出大小设定为64,也就是变得更高了
W_conv2 = weight_variable([5, 5, 32, 64])
# bias的大小是64个长度,因此我们传入它的shape为[64]
b_conv2 = bias_variable([64])
# 定义好了Weight和bias,我们就可以定义卷积神经网络的第二个卷积层h_conv2=conv2d(h_pool1,W_conv2)+b_conv2,同时我们对h_conv2进行非线性处理,
# 也就是激活函数来处理喽,这里我们用的是tf.nn.relu(修正线性单元)来处理,要注意的是,因为采用了SAME的padding方式,输出图片的大小没有变化依然是在第一层卷集层输出时的14*14,
# 只是厚度变厚了,因此现在的输出大小就变成了14x14x64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# 最后我们再进行pooling的处理就ok啦,经过pooling的处理,输出大小就变为了7x7x64
h_pool2 = max_pool_2x2(h_conv2)

# 定义神经网络全连接层1
# 其形状为h_pool2的输出形状7*7*64,输出为1024个神经元
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把h_pool2的输出的包含长宽平面的信息形状转换成一个维度的数据,相当于变平的操作:[n_samples, 7, 7, 64] => [n_samples, 7*7*64]
h_pool1_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool1_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 定义神经网络全连接层2
# 其输入为全连接层1的输出1024,输出为0-9数字的one hot格式,因此为10列
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

# 定义loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
# 对于比较庞大的系统可以用AdamOptimizer比较好一点
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
    if i % 50 == 0:
        # 每隔50条打印一下预测的准确率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

输出为:

0.0853
0.7785
0.8835
0.9084
0.9241
0.9316
0.9412
0.9463
0.9485
0.951
0.9561
0.9578
0.9599
0.9611
0.964
0.9644
0.966
0.9673
0.9687
0.9685

这次用卷积神经网络把结果提高了很多。