CNN网络细节及代码实现手写数字识别
CNN的大体结构在第二天的博客里面已经说了,下面我以手写数字识别为例,具体分析一下
tensorflow里面自带有一个mnist的手写数字识别的数据集,这个数据集是ubyte格式的所以不能直接打开看,可以使用如下代码转化为png格式的:
import numpy as np
import struct
from PIL import Image
import os
data_file = 'D:\\Jupyter\\MNIST_data\\train-images.idx3-ubyte'
# It's 47040016B, but we should set to 47040000B
data_file_size = 47040016
data_file_size = str(data_file_size - 16) + 'B'
data_buf = open(data_file, 'rb').read()
magic, numImages, numRows, numColumns = struct.unpack_from(
'>IIII', data_buf, 0)
datas = struct.unpack_from(
'>' + data_file_size, data_buf, struct.calcsize('>IIII'))
datas = np.array(datas).astype(np.uint8).reshape(
numImages, 1, numRows, numColumns)
label_file = 'D:\\Jupyter\\MNIST_data\\train-labels.idx1-ubyte'
# It's 60008B, but we should set to 60000B
label_file_size = 60008
label_file_size = str(label_file_size - 8) + 'B'
label_buf = open(label_file, 'rb').read()
magic, numLabels = struct.unpack_from('>II', label_buf, 0)
labels = struct.unpack_from(
'>' + label_file_size, label_buf, struct.calcsize('>II'))
labels = np.array(labels).astype(np.int64)
datas_root = 'mnist_train'
if not os.path.exists(datas_root):
os.mkdir(datas_root)
for i in range(10):
file_name = datas_root + os.sep + str(i)
if not os.path.exists(file_name):
os.mkdir(file_name)
for ii in range(numLabels):
img = Image.fromarray(datas[ii, 0, 0:28, 0:28])
label = labels[ii]
file_name = datas_root + os.sep + str(label) + os.sep + \
'mnist_train_' + str(ii) + '.png'
img.save(file_name)
28*28的图片
Step 0 导入Tensorflow
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
Step 1加载数据集MNIST
声明两个placeholder(占位符),用于存储神经网络的输入,输入包括image和label。这里加载的image是28*28也就是(784,)的shape。
<tf.Tensor ‘Placeholder_10:0’ shape=(?, 784) dtype=float32>
下面也使用到了One hot encoding原理非常简单
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
x = tf.placeholder(tf.float32,[None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
Step 2 定义weights 和 bias
其实我不太懂他这句话是什么意思,但是CS231n里面有说到一点,在初始化权值的时候我们一般都是产生服从正态分布的随机值,在这里用tf.truncated_normal()。如果权值一样的话,神经元的作用就是相同的,收敛速度就会变慢。
如下函数的定义:
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
从截断的正态分布中输出随机值。 shape表示生成张量的维度,mean是均值,stddev是标准差。这个函数产生正太分布,均值和标准差自己设定。这是一个截断的产生正太分布的函数,就是说产生正太分布的值如果与均值的差值大于两倍的标准差,那就重新生成。和一般的正太分布的产生随机数据比起来,这个函数产生的随机数与均值的差距不会超过两倍的标准差,但是一般的别的函数是可能的。
关于这个函数的详细说明可以看这个:
https://www.jianshu.com/p/e18fdc7b633a
#----Weight Initialization---#
#One should generally initialize weights with a small amount of noise for symmetry breaking, and to prevent 0 gradients通常应该用少量噪声初始化权重以进行对称性破坏,并防止0梯度
def weight_variable(shape):
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)
Step 3 定义卷积层和Maxpooling
同样,为了代码的整洁,将卷积层和maxpooling封装起来。padding=‘SAME’表示使用padding,不改变图片的大小。
卷积的操作就靠tensorflow里面的这个函数完成:
tf.nn.conv2d (input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
tf.nn.conv2d(x, W, strides=[1,1,1,1], padding=‘SAME’)
- 这里的x需要x 为 input 。要求为一个张量,shape为 [ batch, in_height, in_weight, in_channel ]其中batch为图片的数量,in_height 为图片高度,in_weight 为图片宽度,in_channel 为图片的通道数,灰度图该值为1,彩色图为3。
这里的W为filter。 - 卷积核,要求也是一个张量,shape为 [ filter_height, filter_weight, in_channel, out_channels ],其中 filter_height 为卷积核高度,filter_weight 为卷积核宽度,in_channel 是图像通道数 ,和 input 的 in_channel 要保持一致,out_channel 是卷积核数量。
- strides: 卷积时在图像每一维的步长,这是一个一维的向量,[ 1, strides, strides, 1],第一位和最后一位固定必须是1
- padding: string类型,值为“SAME” 和 “VALID”,表示的是卷积的形式,是否考虑边界。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑
#Convolution and Pooling
#Our convolutions uses a stride of one and are zero padded so that the output is the same size as the input.
#Our pooling is plain old max pooling over 2x2 blocks
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')
Step 4 reshape image数据
为了神经网络的layer可以使用image数据,我们要将其转化成4d的tensor: (Number, width, height, channels)因为上面说了,要是用tf.nn.conv2d这个函input 和 filter都必须是tensor
#To apply the layer, we first reshape x to a 4d tensor, with the second and third dimensions corresponding to image width and height,
#and the final dimension corresponding to the number of color channels.
x_image = tf.reshape(x, [-1,28,28,1])
# -1代表的含义是不用我们自己指定这一维的大小,函数会自动计算
下面我们就要开始搭建CNN结构了。
Step 5 搭建第一个卷积层
使用32个5x5的filter,然后通过maxpooling。
#----first convolution layer----#
#he convolution will compute 32 features for each 5x5 patch. Its weight tensor will have a shape of [5, 5, 1, 32].
#The first two dimensions are the patch size,
#the next is the number of input channels, and the last is the number of output channels.
W_conv1 = weight_variable([5,5,1,32])
#We will also have a bias vector with a component for each output channel.
b_conv1 = bias_variable([32])
#We then convolve x_image with the weight tensor, add the bias, apply the ReLU function, and finally max pool.
#The max_pool_2x2 method will reduce the image size to 14x14.
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
Step 6 第二层卷积
使用64个5x5的filter。
#----second convolution layer----#
#The second layer will have 64 features for each 5x5 patch and input size 32.
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
Step 7 构建全链接层
需要将上一层的输出,展开成1d的神经层。
#----fully connected layer----#
#Now that the image size has been reduced to 7x7, we add a fully-connected layer with 1024 neurons to allow processing on the entire image
W_fc1 = weight_variable([7*7*64, 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)
Step 8 添加Dropout
加入Dropout层,可以防止过拟合问题。注意,这里使用了另外一个placeholder,可以控制在训练和预测时是否使用Dropout。
#-----dropout------#
#To reduce overfitting, we will apply dropout before the readout layer.
#We create a placeholder for the probability that a neuron's output is kept during dropout.
#This allows us to turn dropout on during training, and turn it off during testing.
keep_prob = tf.placeholder(tf.float32)
h_fc1_dropout = tf.nn.dropout(h_fc1, keep_prob)
Step 9 输入层
没有什么特别的,就是输出一个线性结果。
#----read out layer----#
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.matmul(h_fc1_dropout, W_fc2) + b_fc2
Step 10 训练和评估
首先,需要指定一个cost function --cross_entropy,在输出层使用softmax。然后指定optimizer–adam。需要特别指出的是,一定要记得
tf.global_variables_initializer().run()初始化变量
在这里插入代码片