使用tensorflow完成手写数字识别
mnist数据集介绍
MNIST 数据集已经是一个被”嚼烂”了的数据集, 很多教程都会对它”下手”, 几乎成为一个 “典范”. 不过有些人可能对它还不是很了解, 下面来介绍一下.
MNIST 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取, 它包含了四个部分:
Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)
Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)
Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)
MNIST 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据.
不妨新建一个文件夹 – mnist, 将数据集下载到 mnist 以后, 解压即可:
MNIST 数据集已经是一个被”嚼烂”了的数据集, 很多教程都会对它”下手”, 几乎成为一个 “典范”. 不过有些人可能对它还不是很了解, 下面来介绍一下.
MNIST 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取, 它包含了四个部分:
Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)
Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)
Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)
MNIST 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据.
不妨新建一个文件夹 – mnist, 将数据集下载到 mnist 以后, 解压即可:
训练数据集包含 60,000 个样本, 测试数据集包含 10,000 样本. 在 MNIST 数据集中的每张图片由 28 x 28 个像素点构成, 每个像素点用一个灰度值表示. 在这里, 我们将 28 x 28 的像素展开为一个一维的行向量, 这些行向量就是图片数组里的行(每行 784 个值, 或者说每行就是代表了一张图片). load_mnist 函数返回的第二个数组(labels) 包含了相应的目标变量, 也就是手写数字的类标签(整数 0-9).
tensorflow的安装
网上很多介绍,这里提供下参考地址:
《tensorflow安装-》
设计网络层
1. 初始化层
def __init__(self):
# 卷积输入数据是正方形的因为长方形会导致卷积核多卷一次或者少卷一次的操作
self.x = tf.placeholder(dtype=tf.float32,shape=[None,784]) # 28*28*1
self.y = tf.placeholder(dtype=tf.float32,shape=[None,10])
# 设置三层网络
self.w1 = tf.Variable(tf.truncated_normal(shape=[3,3,1,16],dtype=tf.float32,stddev=tf.sqrt(1/16))) # 14*14*16
self.b1 = tf.Variable(tf.zeros(shape=[16],dtype=tf.float32))
self.w2 = tf.Variable(tf.truncated_normal(shape=[3,3,16,128],dtype=tf.float32,stddev=tf.sqrt(1/128))) # 7*7*128
self.b2 = tf.Variable(tf.zeros(shape=[128],dtype=tf.float32))
# 这里要做形状转换 这里面要计算出结果数据形状
# 全连接层输入的数据格式是NV形式 nhwc n hwc->nv
self.w3 = tf.Variable(tf.truncated_normal(shape=[7*7*128,10],dtype=tf.float32,stddev=tf.sqrt(1/10)))
self.b3 = tf.Variable(tf.zeros(shape=[10],dtype=tf.float32))
2. 前向层forward
def forward(self):
# 在这里进行了,x,y的形状转化
x= tf.reshape(self.x,shape=[-1,28,28,1])
# same 填充默认使用1填充
# strides 参数第一位和第四位必须是1 只对卷积层的步长只对矩阵的长和宽有效。
y1 = tf.nn.relu(tf.layers.batch_normalization((tf.nn.conv2d(x,self.w1,strides=[1,1,1,1],padding="SAME"))+self.b1))
# 28*28*16
# print(y1.shape)
# 池化 的步长一定要大于1 后面项目不要用池化操作了会丢失数据,这里将池化换成卷积函数操作
con_y1 = tf.nn.relu(tf.layers.batch_normalization(tf.nn.conv2d(y1, self.conv_w1, strides=[1, 2, 2, 1], padding="VALID") + self.conv_b1)) #14*14*16
# print(con_y1.shape)
# pool_y1 = tf.nn.max_pool(y1,[1,2,2,1],[1,2,2,1],padding="SAME") #14*14*16
y2 = tf.nn.relu(tf.layers.batch_normalization(tf.nn.conv2d(con_y1,self.w2,strides=[1,1,1,1],padding="SAME")+self.b2))
# pool_y2 = tf.nn.max_pool(y2, [1, 2, 2, 1], [1, 2, 2, 1], padding="SAME") # 7*7*128
con_y2 = tf.nn.relu(tf.layers.batch_normalization(tf.nn.conv2d(y2, self.conv_w2, strides=[1, 2, 2, 1], padding="VALID") + self.conv_b2)) # 7*7*128
# print(con_y2)
# 形状转化
y2 = tf.reshape(con_y2, shape=[-1, 7 * 7 * 128])
self.y3 = tf.layers.batch_normalization(tf.matmul(y2, self.w3) + self.b3)
self.y_out = tf.nn.softmax(self.y3)
3. 后向层backward
def backward(self):
# 放置优化函数反向传播就可以
self.optimizer = tf.train.AdamOptimizer().minimize(self.error)
4. 损失函数loss
def loss(self):
self.error = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=self.y,logits=self.y3))
整体代码
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(".\MNIST_data",one_hot=True)
class CNN:
def __init__(self):
# 卷积输入数据是正方形的因为长方形会导致卷积核多卷一次或者少卷一次的操作
self.x = tf.placeholder(dtype=tf.float32,shape=[None,784]) # 28*28*1
self.y = tf.placeholder(dtype=tf.float32,shape=[None,10])
# 设置三层网络
self.w1 = tf.Variable(tf.truncated_normal(shape=[3,3,1,16],dtype=tf.float32,stddev=tf.sqrt(1/16))) # 14*14*16
self.b1 = tf.Variable(tf.zeros(shape=[16],dtype=tf.float32))
self.w2 = tf.Variable(tf.truncated_normal(shape=[3,3,16,128],dtype=tf.float32,stddev=tf.sqrt(1/128))) # 7*7*128
self.b2 = tf.Variable(tf.zeros(shape=[128],dtype=tf.float32))
# 这里要做形状转换 这里面要计算出结果数据形状
# 全连接层输入的数据格式是NV形式 nhwc n hwc->nv
self.w3 = tf.Variable(tf.truncated_normal(shape=[7*7*128,10],dtype=tf.float32,stddev=tf.sqrt(1/10)))
self.b3 = tf.Variable(tf.zeros(shape=[10],dtype=tf.float32))
#使用卷积替换池化层
self.conv_w1 = tf.Variable(tf.truncated_normal(shape=[1,1,16,16],dtype=tf.float32,stddev=tf.sqrt(1/16)))
self.conv_b1 = tf.Variable(tf.zeros(shape=[16],dtype=tf.float32))
self.conv_w2 = tf.Variable(tf.truncated_normal(shape=[1, 1, 128, 128], dtype=tf.float32, stddev=tf.sqrt(1 / 128)))
self.conv_b2 = tf.Variable(tf.zeros(shape=[128], dtype=tf.float32))
def forward(self):
# 在这里进行了,x,y的形状转化
x= tf.reshape(self.x,shape=[-1,28,28,1])
# same 填充默认使用1填充
# strides 参数第一位和第四位必须是1 只对卷积层的步长只对矩阵的长和宽有效。
y1 = tf.nn.relu(tf.layers.batch_normalization((tf.nn.conv2d(x,self.w1,strides=[1,1,1,1],padding="SAME"))+self.b1))
# 28*28*16
# print(y1.shape)
# 池化 的步长一定要大于1 后面项目不要用池化操作了会丢失数据,这里将池化换成卷积函数操作
con_y1 = tf.nn.relu(tf.layers.batch_normalization(tf.nn.conv2d(y1, self.conv_w1, strides=[1, 2, 2, 1], padding="VALID") + self.conv_b1)) #14*14*16
# print(con_y1.shape)
# pool_y1 = tf.nn.max_pool(y1,[1,2,2,1],[1,2,2,1],padding="SAME") #14*14*16
y2 = tf.nn.relu(tf.layers.batch_normalization(tf.nn.conv2d(con_y1,self.w2,strides=[1,1,1,1],padding="SAME")+self.b2))
# pool_y2 = tf.nn.max_pool(y2, [1, 2, 2, 1], [1, 2, 2, 1], padding="SAME") # 7*7*128
con_y2 = tf.nn.relu(tf.layers.batch_normalization(tf.nn.conv2d(y2, self.conv_w2, strides=[1, 2, 2, 1], padding="VALID") + self.conv_b2)) # 7*7*128
# print(con_y2)
# 形状转化
y2 = tf.reshape(con_y2, shape=[-1, 7 * 7 * 128])
self.y3 = tf.layers.batch_normalization(tf.matmul(y2, self.w3) + self.b3)
self.y_out = tf.nn.softmax(self.y3)
def loss(self):
self.error = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=self.y,logits=self.y3))
def backward(self):
# 放置优化函数反向传播就可以
self.optimizer = tf.train.AdamOptimizer().minimize(self.error)
if __name__ == '__main__':
cnn = CNN()
cnn.forward()
cnn.loss()
cnn.backward()
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for i in range(500000):
xs, ys = mnist.train.next_batch(100)
error,_ = sess.run(fetches=[cnn.error,cnn.optimizer],feed_dict={cnn.x:xs,cnn.y:ys})
if i%10 ==0:
xss,yss = mnist.validation.next_batch(10)
_error, output = sess.run([cnn.error, cnn.y_out], feed_dict={cnn.x: xss, cnn.y: yss})
print("error:", error, "_error", _error)
print("label:", np.argmax(yss[0]), "ouput", np.argmax(output[0]))
结果展示
error: 2.7920783 _error 2.3815117
label: 4 ouput 2
error: 1.6094059 _error 1.4065416
label: 5 ouput 1
error: 0.952906 _error 0.68214786
label: 8 ouput 4
error: 0.7417401 _error 0.50132793
label: 3 ouput 3
error: 0.5962105 _error 1.6322844
label: 1 ouput 1
error: 0.4186454 _error 0.8143649
label: 3 ouput 3
error: 0.48242718 _error 0.5043499
label: 6 ouput 6
error: 0.24547787 _error 0.06660037
label: 1 ouput 1
error: 0.45564872 _error 0.58576214
label: 8 ouput 8
error: 0.24093382 _error 0.12508802
label: 6 ouput 6
error: 0.15008664 _error 0.09691824
label: 1 ouput 1
error: 0.2426682 _error 0.033808738
label: 1 ouput 1
error: 0.38572145 _error 0.5644743
label: 6 ouput 6
error: 0.2806958 _error 0.054441046
label: 7 ouput 7
error: 0.23656942 _error 0.13716781
label: 3 ouput 3
error: 0.2282756 _error 0.08847963
label: 8 ouput 8
error: 0.37294194 _error 0.3430846
label: 8 ouput 8
error: 0.2200708 _error 0.015551163
label: 4 ouput 4
error: 0.38941205 _error 0.3061425
label: 6 ouput 6
error: 0.2381813 _error 0.30495685
label: 9 ouput 9
error: 0.19712932 _error 0.68359375
label: 8 ouput 9
error: 0.30451572 _error 0.31347287
label: 3 ouput 3
error: 0.07452514 _error 0.11504559
label: 8 ouput 8
error: 0.21752807 _error 0.012744324
label: 5 ouput 5
error: 0.23550516 _error 0.043388452
label: 7 ouput 7
error: 0.05256593 _error 0.2218481
label: 8 ouput 8
error: 0.14572689 _error 0.1335406
label: 5 ouput 9
error: 0.2032167 _error 0.053598754
label: 1 ouput 1
error: 0.15906087 _error 0.116603434
label: 5 ouput 5
error: 0.19653736 _error 0.4751224
label: 2 ouput 2
error: 0.23811238 _error 0.33534256
label: 4 ouput 4
error: 0.10221318 _error 0.058767788
label: 7 ouput 7
error: 0.24909215 _error 0.063688755
label: 9 ouput 9
error: 0.15639704 _error 0.039930798
label: 7 ouput 7
error: 0.21030435 _error 0.10319646
label: 3 ouput 3
error: 0.17436792 _error 0.6377451
label: 3 ouput 3
error: 0.06956818 _error 0.070775226
label: 4 ouput 4
error: 0.08807094 _error 0.32317868
label: 6 ouput 6
error: 0.17427593 _error 0.24434297
label: 9 ouput 9
error: 0.123306654 _error 0.27709654
label: 1 ouput 1
error: 0.087782934 _error 0.068826094
label: 6 ouput 6
error: 0.0695146 _error 0.010479583
label: 1 ouput 1
error: 0.06429311 _error 0.10896905
label: 3 ouput 3
error: 0.22038728 _error 0.0046242476
label: 1 ouput 1
error: 0.1277237 _error 0.22414172
label: 7 ouput 4
error: 0.15473348 _error 0.3529214
label: 9 ouput 9
error: 0.16050461 _error 0.27860898
label: 0 ouput 0
error: 0.19431192 _error 0.12932254
label: 2 ouput 0
error: 0.08871374 _error 0.11567821
label: 0 ouput 0
error: 0.08047363 _error 0.008385212
label: 7 ouput 7
error: 0.16944367 _error 0.01338021
label: 6 ouput 6
error: 0.06996132 _error 0.01271003
label: 6 ouput 6
总结
再学习深度学习神经网络这是第一个必学的demo,demo虽然简单,但是里面涉及的东西蛮多的,
里面之前设计网络的时候,使用池化层减小计算量,但考虑了下,池化层会丢失数据,这里有一个特别好的方式去替换他,就是使用1 * 1 的卷积核对输出层再做一次卷积运算,设置的步长为2这样和池化效果一样,验证效果还是比较池化操作还是较好的,这里不用卷积神经网络而使用全连接方式做变换的话,网络层的参数量和计算量是非常庞大的,卷积就是为了减少计算量和参数量的。因为卷积层神经网络是权重共享的,局部感知,训练时间短,精度高等优势。