MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片。
官网
每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。训练数据集的图片是 mnist.train.images ,训练数据集的标签是 mnist.train.labels。
每一张图片包含28X28个像素点。我们可以用一个数字数组来表示这张图片:
我们把这个数组展开成一个向量,长度是 28x28 = 784。因此,在MNIST训练数据集中,mnist.train.images 是一个形状为 [60000, 784] 的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。
相对应的MNIST数据集的标签是介于0到9的数字,用来描述给定图片里表示的数字。标签数据是”one-hot vectors”。 一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。所以在此教程中,数字n将表示成一个只有在第n维度(从0开始)数字为1的10维向量。比如,标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。因此, mnist.train.labels 是一个 [60000, 10] 的数字矩阵。
标签值
为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片像素值进行加权求和。如果这个像素具有很强的证据说明这张图片不属于该类,那么相应的权值为负数,相反如果这个像素拥有有利的证据支持这张图片属于这个类,那么权值是正数。
我们也需要加入一个额外的偏置量(bias),因为输入往往会带有一些无关的干扰量。因此对于给定的输入图片 x 它代表的是数字 i 的证据可以表示为
其中 代表权重,代表数字 i 类的偏置量,j 代表给定图片 x 的像素索引用于像素求和。然后用softmax函数可以把这些证据转换成概率 y:
这里的softmax可以看成是一个激励(activation)函数或者链接(link)函数,把我们定义的线性函数的输出转换成我们想要的格式,也就是关于10个数字类的概率分布。因此,给定一张图片,它对于每一个数字的吻合度可以被softmax函数转换成为一个概率值。softmax函数可以定义为:
对于输入的xs加权求和,再分别加上一个偏置量,最后再输入到softmax函数中:
如果把它写成一个等式,我们可以得到:
我们也可以用向量表示这个计算过程:用矩阵乘法和向量相加。这有助于提高计算效率。
一个非常常见的,非常漂亮的成本函数是“交叉熵”(cross-entropy)。它的定义如下:
y 是我们预测的概率分布, y’ 是实际的分布(我们输入的one-hot vector)。比较粗糙的理解是,交叉熵是用来衡量我们的预测用于描述真相的低效性。
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('mnist', one_hot=True) #加载数据
x = tf.placeholder(tf.float32, [None, 784]) #输入节点,None表示不确定
W = tf.Variable(tf.zeros([784, 10])) #权值矩阵,784*10
b = tf.Variable(tf.zeros([10])) #偏置,1*10
y = tf.matmul(x, W) + b #BP网络输出
y_ = tf.placeholder(tf.float32, [None, 10]) #真实标签值
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
先计算每个y的对数,然后把 y_ 的每一个元素和log(y) 的对应元素相乘。最后,用 tf.reduce_mean 计算张量的所有元素的总和然后取平均。
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
TensorFlow用梯度下降算法(gradient descent algorithm)以0.5的学习速率最小化交叉熵。TensorFlow在这里实际上所做的是,它会在后台给描述你的计算的那张图里面增加一系列新的计算操作单元用于实现反向传播算法和梯度下降算法。然后,它返回给你的只是一个单一的操作,当运行这个操作时,它用梯度下降算法训练你的模型,微调你的变量,不断减少成本。
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100) #每次加载100个数据
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) #
该循环的每个步骤中,我们都会随机抓取训练数据中的100个批处理数据点,然后我们用这些数据点作为参数替换之前的占位符来运行train_step。
使用一小部分的随机数据来进行训练被称为随机训练(stochastic training)- 在这里更确切的说是随机梯度下降训练。在理想情况下,我们希望用我们所有的数据来进行每一步的训练,因为这能给我们更好的训练结果,但显然这需要很大的计算开销。所以,每一次训练我们可以使用不同的数据子集,这样做既可以减少计算开销,又可以最大化地学习到数据集的总体特性。
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
tf.argmax(y,1)返回的是模型对于任一输入x预测到的标签值,而 tf.argmax(y_,1) 代表正确的标签,我们可以用 tf.equal 来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75。
error = sess.run(accuracy,feed_dict={x: mnist.test.images, y_: mnist.test.labels})
最后,我们计算所学习到的模型在测试数据集上面的正确率。mnist.test.images是测试数据的输入值,mnist.test.labels的标签值。
#!/usr/bin/python
# coding=utf-8
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
tf.reset_default_graph() #构建图表
mnist = input_data.read_data_sets('mnist', one_hot=True) #加载数据
def BP():
error = 0
x = tf.placeholder(tf.float32, [None, 784]) #输入节点,None表示不确定
W = tf.Variable(tf.zeros([784, 10])) #权值矩阵,784*10
b = tf.Variable(tf.zeros([10])) #偏置,1*10
y = tf.matmul(x, W) + b #BP网络输出
y_ = tf.placeholder(tf.float32, [None, 10]) #真实标签值
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
init = tf.initialize_all_variables()
with tf.Session() as sess: #创建sess
sess.run(init) #执行一次运算
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
error = sess.run(accuracy,feed_dict={x: mnist.test.images, y_: mnist.test.labels})
return error
error = BP()
print "正确率 "+ str(error*100)+" %"
图像处理中,往往把图像表示为像素的向量,比如一个 1000×1000 的图像,可以表示为一个10^6 的向量,就如同在手写字神经网络中,输入层为28×28 = 784 维的向量。如果隐含层的节点个数与输入层一样,即也是10^6 时,那么输入层到隐含层的参数数据为 10^6×10^6 =10^12,参数的个数太多了,要想在正常的时间内训练完,基本是不可能的。所以要想处理1000×1000的图像分类,就得首先想办法减少参数的个数,也就是只基于深度神经网络(DNN)已经很难训练或者有没有更加优秀的算法可以专门处理这种图像分类呢? 这就是卷积神经网络,convolutional neural network ,简称为 CNN 。
下图为三层神经网络识别手写数字的网络结构:
一个典型的神经网络的结构是全连接的,即某一层的某个节点与上一层的每个节点相连,且每个节点各自使用一套参数,这样的结构就是经典的全连接结构。在全连接的网络中,假如k层有n个节点,k+1层有m个节点,则一共有n*m个连接;每个连接都有一个参数,外加每个k+1层节点有一个bias,则共有n*m + m个训练参数,所以全连接的层的连接数、参数数量的数量级约为O(n^2)。全连接的网络的结构如下图:
卷积神经网络采用局部连接和参数共享的方式连接网络。对于一个卷积神经网络,假如该网络的第k层有n个节点,k+1层为卷积层且有m个节点,则k+1层的每个节点只与k层的部分节点相连,此处假设只与k层的i个节点相连(局部连接);另外k+1层的每个节点的连接共享相同的参数、相同的bias(参数共享)。这样该卷积神经网络的第k、k+1层间共有m*i个连接、i+1个参数。由于i小于n且为常数,所以卷积层的连接数、参数数量的数量级约为O(n),远小于全连接的O(n^2)的数量级。卷积神经网络的部分连接的结构如下图:
部分连接且卷积层各节点的输入节点有重叠的网络
部分连接且卷积层各节点的输入节点无重叠的网络
我们的图片通过卷积操作,再通过池化操作后,如果我们再添加层,图片会越变越小。这个时候我们就会引出“Zero Padding”(补零),它可以帮助我们保证每次经过卷积或池化输出后图片的大小不变。如果原始图片大小为4*4,采用3*3的卷积核,那么我们可以加入1的zero padding。得到下图:
通常情况下,我们希望图片做完卷积操作后保持图片大小不变,所以我们一般会选择尺寸为3*3的卷积核和1的zero padding,或者5*5的卷积核与2的zero padding,这样通过计算后,可以保留图片的原始尺寸。那么加入zero padding后的feature_map尺寸 =( width + 2 * padding_size - filter_size )/stride + 1。
单核做卷积操作
在卷积操作中,涉及到一种特殊的操作,叫做求内积,它是两个同型矩阵对应的元素相乘,然后求和。具体说来,如下,
A和B做内积后,得到一个数:1*1 + 0*0 + 1*1 + 0*0 + 1*1 + 0*0 + 1*1 + 0*0 + 1*1 = 5,这就是两个矩阵求内积得到的结果。
对9*9矩阵进行卷积
卷积核
filter = [1 0 1
0 1 0
1 0 1 ]
最后的卷积结果如下图所示:
多核做卷积操作
如果用多个卷积核进行卷积操作,应该是怎样的呢,为了表达方便,分享一个多核卷积操作的动画演示,图中的输入为 7 × 7 × 3,可以看到还做了一层零填充(Zero-padding),这是CNN中另一个重要的超参数,用到了两个过滤核:w0和w1,这在CNN中称为深度(Depth),是CNN三个超参数介绍的最后一个,分别用两个过滤核w0,和w1做了一次卷积操作,对应的得到两个卷积结果。
以上这些就是CNN的卷积部分,它应用了两种技术:
局部连接
权值共享
减少了权重参数,具体这个操作为卷积操作,这个操作有3个重要的超参数:
步长(Stride)
零填充(Zero-padding)
深度(Depth)
Pooling层能起到降低上一层输入的特征的维数的作用,但是同时能保持其最重要的信息,Pooling操作分多种:最大池化,平均池化,求和池化等。
以最大池化为例,池化一般在ReLU操作之后,首先定义一个相邻区域,然后求出这个区域的最大值,再选定一个步长,依次遍历完图像,如下图所示:
Pooling的好处
很明显就是减少参数
Pooling就有平移不变性((translation invariant)
如图feature map是12x12大小的图片,Pooling区域为6x6,所以池化后得到的feature map为2x2,假设白色像素值为1,灰色像素值为0,若采用max pooling之后,左上角窗口值为1
将图像右移一个像素,左上角窗口值仍然为1
将图像缩放之后,左上角窗口值仍然为1
Pooling的方法中average方法对背景保留更好,max对纹理提取更好
深度学习可以进行多次卷积、池化操作。
在每次卷积操作之后一般都会经过一个非线性层,也是激活层
现在一般选择是ReLu,层次越深,相对于其他的函数效果较好,还有Sigmod,tanh函数等
sigmod和tanh都存在饱和的问题,如上图所示,当x轴上的值较大时,对应的梯度几乎为0,若是利用BP反向传播算法, 可能造成梯度消失的情况,也就学不到东西了
ReLU函数对CNN的实际意义
可以看出CNN中的卷积操作是线性操作,对应元素相乘,然后再求和,又知道在现实世界中,大部分的数据都是非线性的,所以有必要引入一个非线性的激活函数,下面通过ReLU操作看下对图片的影响:
原图像
左图为对原图像卷积后的图像,右图为ReLU后的图像,对于单通道图上取值范围为0-255,其中255为白色,0是黑色,因为卷积后的结果有可能为负值,所以经过ReLU后,所有的值为非负值,不再出现有问题的负值。
dropout是一种防止模型过拟合的技术,这项技术也很简单,但是很实用。它的基本思想是在训练的时候随机的dropout(丢弃)一些神经元的激活,这样可以让模型更鲁棒,因为它不会太依赖某些局部的特征(因为局部特征有可能被丢弃)
上图a是标准的一个全连接的神经网络,b是对a应用了dropout的结果,它会以一定的概率(dropout probability)随机的丢弃掉一些神经元。
将多次卷积和池化后的图像展开进行全连接,如下图所示。
将卷积操作,ReLU操作,Pooling操作结合起来,我们可以得到如下深度网络,可以看出共进行了2次卷积和池化,完成对输入的图像的特征提取,接下来就是全连接层,经过全连接层,会使用前一层提取的所有主要特征,使用一般的均值方差作为损失函数,在输出层可以使用softmax分类器完成多分类任务。
上面的是一个多层的卷积神经网络结构,在实际应用中,我们可以在两层全连接层中添加一个Dropout层来防止模型过拟合。
权重和偏置初始化函数
def weight_variable(shape): #权重初始化函数
initial = tf.truncated_normal(shape, stddev=0.1) # # 使用truncated_normal进行初始化
return tf.Variable(initial)
def bias_variable(shape): #偏置初始化函数
initial = tf.constant(0.1, shape=shape) ## 偏置定义为常量
return tf.Variable(initial)
卷积函数
strides[0]和strides[3]的两个1是默认值,中间两个1代表padding时在x方向运动1步,y方向运动1步
padding=’SAME’代表经过卷积之后的输出图像和原图像大小一样
def conv2d(x, W): #x是图片的所有参数,W是此卷积层的权重
#卷积使用1步长(stride size),0边距(padding size)的模板,保证输出和输入是同一个大小
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
池化函数
ksize指定池化核函数的大小
根据池化核函数的大小定义strides的大小
#池化函数
def max_pool_2x2(x):
#池化的核函数大小为2x2,因此ksize=[1,2,2,1],步长为2,因此strides=[1,2,2,1]
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')
加载mnist数据和定义placeholder
mnist = input_data.read_data_sets('mnist', one_hot=True) #加载数据
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
keep_prob = tf.placeholder(tf.float32)
#我们把x变成一个4d向量,其第2、第3维对应图片的宽、高,最后一维代表图片的颜色通道数
x_image = tf.reshape(x, [-1, 28, 28, 1])
第一层卷积和池化
with tf.name_scope('conv1'): #第一层卷积层
W_conv1 = weight_variable([5, 5, 1, 32])# 卷积核定义为5x5,1是输入的通道数目,32是输出的通道数目
b_conv1 = bias_variable([32]) # 每个输出通道对应一个偏置
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 卷积运算,并使用ReLu激活函数激活
with tf.name_scope('pool1'): #第一层池化层
h_pool1 = max_pool_2x2(h_conv1) # pooling操作
第二层卷积和池化
with tf.name_scope('conv2'): #第二层卷积层
W_conv2 = weight_variable([5, 5, 32, 64]) # 卷积核还是5x5,32个输入通道,64个输出通道
b_conv2 = bias_variable([64]) # 与输出通道一致
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # 卷积运算,并使用ReLu激活函数激活
with tf.name_scope('pool2'): #第二层池化层
h_pool2 = max_pool_2x2(h_conv2) # pooling操作
全连接第一层
with tf.name_scope('fc1'): #图片尺寸减小到7x7,我们加入一个有1024个神经元的全连接层,用于处理整个图片
W_fc1 = weight_variable([7 * 7 * 64, 1024]) # 下面就是定义一般神经网络的操作了,继续扩大为1024
b_fc1 = bias_variable([1024]) # 对应的偏置
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) # 将最后操作的数据展开
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) #把池化层输出的张量reshape成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU
dropout防止过拟合
with tf.name_scope('dropout'): # 为了减少过拟合,我们在输出层之前加入dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
最后一层全连接预测
输出
with tf.name_scope('fc2'): #输出
W_fc2 = weight_variable([1024, 10]) # 最后一层权重初始化
b_fc2 = bias_variable([10]) # 对应偏置
y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
softmax分类器
with tf.name_scope('loss'):
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y_conv) #返回交叉熵误差
梯度下降优化
cross_entropy = tf.reduce_mean(cross_entropy) #求平均值误差
with tf.name_scope('adam_optimizer'):
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) # 调用梯度下降
计算准确率
with tf.name_scope('accuracy'): #计算准确率
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
correct_prediction = tf.cast(correct_prediction, tf.float32)
accuracy = tf.reduce_mean(correct_prediction)
迭代过程
with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
for i in range(101):
batch = mnist.train.next_batch(100) #使用SGD,每次选取100个数据训练
if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
print "第"+ str(i)+"次迭代,训练集正确率:"+str(int(train_accuracy*100))+"%"
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
测试数据(分段加载,虚拟机内存不足)
a = 200 #测试集大小为10000
b = 50
sum = 0
for i in range(a):
testSet = mnist.test.next_batch(b)
c = accuracy.eval(feed_dict={x: testSet[0], y_: testSet[1], keep_prob: 1.0})
sum += c * b
print "测试集正确率 " +str(sum / (b * a)*100)+ " %"
#!/usr/bin/python
# coding=utf-8
import tensorflow as tf
import numpy as np
import time
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
import tempfile
import argparse
tf.reset_default_graph() #构建图表
mnist = input_data.read_data_sets('mnist', one_hot=True) #加载数据
#权重初始化函数
def weight_variable(shape): # 使用truncated_normal进行初始化
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
#偏置初始化函数
def bias_variable(shape): # 偏置定义为常量
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W): #x是图片的所有参数,W是此卷积层的权重
#卷积使用1步长(stride size),0边距(padding size)的模板,保证输出和输入是同一个大小
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
#池化函数
def max_pool_2x2(x):
#池化的核函数大小为2x2,因此ksize=[1,2,2,1],步长为2,因此strides=[1,2,2,1]
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')
def Bulit_CNN(x):
with tf.name_scope('reshape'):
#我们把x变成一个4d向量,其第2、第3维对应图片的宽、高,最后一维代表图片的颜色通道数
x_image = tf.reshape(x, [-1, 28, 28, 1])
with tf.name_scope('conv1'): #第一层卷积层
W_conv1 = weight_variable([5, 5, 1, 32])# 卷积核定义为5x5,1是输入的通道数目,32是输出的通道数目
b_conv1 = bias_variable([32]) # 每个输出通道对应一个偏置
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 卷积运算,并使用ReLu激活函数激活
with tf.name_scope('pool1'): #第一层池化层
h_pool1 = max_pool_2x2(h_conv1) # pooling操作
with tf.name_scope('conv2'): #第二层卷积层
W_conv2 = weight_variable([5, 5, 32, 64]) # 卷积核还是5x5,32个输入通道,64个输出通道
b_conv2 = bias_variable([64]) # 与输出通道一致
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # 卷积运算,并使用ReLu激活函数激活
with tf.name_scope('pool2'): #第二层池化层
h_pool2 = max_pool_2x2(h_conv2) # pooling操作
with tf.name_scope('fc1'): #图片尺寸减小到7x7,我们加入一个有1024个神经元的全连接层,用于处理整个图片
W_fc1 = weight_variable([7 * 7 * 64, 1024]) # 下面就是定义一般神经网络的操作了,继续扩大为1024
b_fc1 = bias_variable([1024]) # 对应的偏置
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) # 将最后操作的数据展开
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) #把池化层输出的张量reshape成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU
with tf.name_scope('dropout'): # 为了减少过拟合,我们在输出层之前加入dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
with tf.name_scope('fc2'): #输出
W_fc2 = weight_variable([1024, 10]) # 最后一层权重初始化
b_fc2 = bias_variable([10]) # 对应偏置
y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
return y_conv, keep_prob
def CNN(y_conv, keep_prob,x,y_):
error = 0
with tf.name_scope('loss'):
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y_conv) #返回交叉熵误差
cross_entropy = tf.reduce_mean(cross_entropy) #求平均值
with tf.name_scope('adam_optimizer'):
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) # 调用梯度下降
with tf.name_scope('accuracy'): #计算准确率
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
correct_prediction = tf.cast(correct_prediction, tf.float32)
accuracy = tf.reduce_mean(correct_prediction)
with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
for i in range(101):
batch = mnist.train.next_batch(100) #使用SGD,每次选取100个数据训练
if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0}) # dropout值定义为0.5
print "第"+ str(i)+"次迭代,训练集正确率:"+str(int(train_accuracy*100))+"%"
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
#测试集正确率
a = 200
b = 50
sum = 0
for i in range(a):
testSet = mnist.test.next_batch(b)
c = accuracy.eval(feed_dict={x: testSet[0], y_: testSet[1], keep_prob: 1.0})
sum += c * b
print "测试集正确率 " +str(sum / (b * a)*100)+ " %"
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
y_conv, keep_prob = Bulit_CNN(x)
start = time.clock()
CNN(y_conv, keep_prob,x,y_)
end = time.clock()
print "时间耗费 "+str(int((end-start)/60))+ " 分"
卷积神经网络的正确率应该是99%,但是我这里只跑出来了97%(正常情况)。虽然卷积神经网络的正确率非常高,但是时间代价太大了。我们发现,对于卷积神经网络来说,随着迭代次数的增多,正确率有可能会降低。因此我们可以适当的减少迭代次数。