学习卷积神经网络的第一课Tensorflow+CNN+MNIST

关键字:tensorflow、cnn、mnist、神经网络、卷积、验证码、识别
时间:2017年5月

目录

前言
CNN介绍
Tensorflow介绍
MNIST介绍
定义网络结构及参数
代码及说明
注意事项

前言

工作原因需要抓取一个网站的数据,但这个网站有验证码,很自然的想到了验证码识别。起初通过使用了一些常规的验证码识别的方法,但到了分割字符这一步是,发现非常困难。通过搜索发现了卷积神经网络可以很简单的完成识别工作,于是开始了卷积神经网络的学习。
继续向下阅读需要你对神经网络的基本概念有所认识。

CNN介绍

卷积神经网络(CNN),完全可以按字面去理解它,就是神经网络,并在网络中引入了卷积算法。CNN目前已经在图像识别上取得了广泛的应用。

Tensorflow介绍

Tensorflow是Google开源的一个神经网络框架,支持Python语言,是主流神经网络框架之一。

MNIST介绍

MNIST是一个非常好的分类算法研究的样本库。它由0~9的手写数字灰度图片构成,6万个训练样本,1万个测试样本。

定义网络结构及参数

这里采用了2层卷积和2层全连接构建网络结构。这是一个比较通用的层数,完全可以用于任意位数的验证码识别。但用于多位验证码图片识别时,需要调整输入层和输出层。

输入层(reshape) 输入节点: 784 输出节点: 28*28
隐层1(卷积) 输入节点: 28*28 输出节点: 32*14*14
隐层2(卷积) 输入节点: 32*14*14 输出节点: 64*7*7
隐层3(全连接) 输入节点: 64*7*7 输出节点: 1024
隐层4(全连接) 输入节点: 1024 输出节点: 10
输出层(Softmax) 输入节点: 10 输出节点: 10

代码及说明

tensor,是tensorflow的核心,tf.Variable()、tf.reshape()、tf.nn.relu()、tf.matmul()、tf.nn.softmax()、tf.reduce_mean()等,几乎所有函数的输入输出都是tensor,因此他们之间可以任意连接,构造网络。
reshape中的-1,对矩阵数据进行形状变换时,可以在一个维度上使用-1代替。例如:把一个[1,2,3,4,5,6,7,8,9,0]变成[[1,2,3,4,5],[6,7,8,9,0]],可以使用reshape(x,[2,5])或reshape(x,[-1,5])。

# -*- coding:utf-8 -*-
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

X = tf.placeholder(tf.float32, [None, 784]) # 图片分辨率为28x28,但样本是
Y = tf.placeholder(tf.float32, [None, 10])
# placeholder(),占位函数,session.run的参数feed_dict={X: , Y:}中的X和Y对应的就是它。

inputs = tf.reshape(X,[-1,28,28,1])
# 样本是n个长度为784的1维向量,把它转成n个高28x宽28x通道数1的矩阵

#
# 第一层,卷积层
#
l1_weights = tf.Variable(tf.truncated_normal([5,5,1,32], stddev=0.1))
l1_biases = tf.Variable(tf.constant(0.1, shape=[32]))
# 卷积核[5,5],输入1张28x28图片,输出32张28x28特征图片

l1 = tf.nn.conv2d(inputs, l1_weights, strides=[1,1,1,1], padding='SAME') + l1_biases
# strides=[1,1,1,1],所有方向步进为1进行卷积
# padding='SAME',输出大小和输入大小相同28x28

l1 = tf.nn.relu(l1)
# 激活函数,例如:relu([0.5,-0.3,-0.13,1.2,-0.7] = [0.5,0,0,1.2,0]

l1 = tf.nn.max_pool(l1, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')
# 池化,strides=[1,2,2,1]使得输出为14x14

#
# 第二层,卷积层,与第一层类似
#
l2_weights = tf.Variable(tf.truncated_normal([5,5,32,64], stddev=0.1))
l2_biases = tf.Variable(tf.constant(0.1, shape=[64]))
# 卷积核[5,5],输入32张14x14图片,输出64张14x14特征图片

l2 = tf.nn.conv2d(l1, l2_weights, strides=[1,1,1,1], padding='SAME') + l2_biases
l2 = tf.nn.relu(l2)
l2 = tf.nn.max_pool(l2, ksize=[1,2,2,1], strides[1,2,2,1], padding='SAME')
# 池化,strides=[1,2,2,1]使得输出为7x7

#
# 第三层,全连接层
#
l3_weights = tf.Variable(tf.truncated_normal([7*7*64, 1024], stddev=0.1))
l3_biases = tf.Variable(tf.constant(0.1, shape=[1024]))
# 7*7*64个输入,1024个输出

l3 = tf.reshape(l2, [-1,7*7*64])
# 把64张7x7的特征转换成1维进行输入给全连接

l3 = tf.matmul(l3, l3_weights) + l3_biases
l3 = tf.nn.relu(l3)

#
#第四层,全连接层
#
l4_weights = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1))
l4_biases = tf.Variable(tf.constant(0.1, shape=[10]))
# 1024个输入,10个输出

l4 = tf.matmul(l3, l4_weights) + l4_biases # y = W * x + b

outputs = tf.nn.softmax(l4)
# softmax([1,2,2]) =  [1/(1+2+2),2/(1+2+2),2/(1+2+2)] = [0.2,0.4,0.4]

cross_entropy = -tf.reduce_sum(Y*tf.log(outputs)) # 计算交叉熵
train_op = tf.train.AdamOptimizer(0.0001).minimize(cross_entropy)

correct_prediction = tf.equal(tf.argmax(outputs, axis=1), tf.argmax(Y, axis=1))
# tf.argmax找某个轴上的最大值的位置,理解起来比较困难,简单理解就是把[1,0,0,0,0,0,0,0,0,0]转成[0],[0,1,0,0,0,0,0,0,0,0]转成[1],以此类推。
# tf.equal比较两个矩阵相同位置的值,相同为True,否则为False,例如:[[0,1],[1,0]]与[[0,0],[1,1]]比较,结果为[[True,False],[True,False]]

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
# tf.cast()数据类型转换,True/False转成浮点1./0.

sess = tf.Session()
sess.run(tf.initialize_all_variables())

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
# 加载MNIST库,本地没有则从网络下载,下载完成后在当前目录有个MNIST_data/目录

for i in range(1000):
# 训练1000次
    batch_xs, batch_ys = mnist.train.next_batch(100)
    # 100个样本一批,进行1次训练
    sess.run(train_op, feed_dict={X:batch_xs, Y:batch_ys})
    if i%50==0:
        print "step %d, accuracy %f"%(i, sess.run(accuracy, feed_dict={X:batch_xs,Y:batch_ys}))
        # 每50次训练,输出准确率

print "test accuracy: ",sess.run(accuracy, feed_dict{X:mnist.test.images, Y:mnist.test.labels})
# 使用测试集计算准确率

注意事项

mnist样本库文件大小约10MB,但下载比较慢。
为了代码简单,没有加入tf.nn.dropout()、tf.train.Saver()等函数。tf.nn.dropout(),可防止过拟合。tf.train.Saver(),可以保存或回复模型,主要用于暂停训练和恢复训练,以及训练好的模型直接加载使用。