暑假第三天

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()初始化变量

在这里插入代码片
发布了29 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43343116/article/details/93768680