往期TensorFlow教程系列延伸阅读:

TensorFlow(一)——基础图模型
TensorFlow(二)——逻辑回归





1. 导入基本库

这期讲解如何运用TensorFlow来建立一个卷积神经网络用于手写字识别。首先导入一些必备的库。

import tensorflow as tf


import tensorflow.examples.tutorials.mnist.input_data as input_data

from libs.utils import *

import matplotlib.pyplot as plt




2.新建权重函数和偏置函数

首先我们写两个通用函数,权重变量和偏置向量,传入的参数都是维度信息,并且都是使用高斯随机分布来产生初始值。

def weight_variable(shape):

    initial = tf.random_normal(shape, mean=0.0, stddev=0.01)

    return tf.Variable(initial)



def bias_variable(shape):

    initial = tf.random_normal(shape, mean=0.0, stddev=0.01)

    return tf.Variable(initial)




3.新建输入输出有关的占位符

这里我们使用的训练数据是MNIST手写字识别数据库,手写字识别数据库中的每张图片是28*28大小,然后手写字一共是十种类别,即1234567890,所以这里我们首先新建两个占位符,分别是输入数据x和标签数据y,其中None代表batch那个维度大小目前还无法确定。

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])




4.构造两层卷积神经网络

由于这里我们想运用卷积神经网络,因此首先需要把数据数据的维度变成4-D形式,即(batch_size,width,height,channel),所以这里width和height我们设置为28,channel为1,而batch_size设置为-1的意思是这个维度只用把总维度去除以width*height*channel即可得到。

x_tensor = tf.reshape(x, [-1, 28, 28, 1])



接下来我们需要设置卷积感知区域的大小以及卷积核的个数,这里我们设置感知区域的大小为3*3,卷积核的个数为16,这样就构建了权重矩阵。而偏置对于每个卷积核都是共享的,因此偏置是一个长度为n_filters_1的向量。

filter_size = 3

n_filters_1 = 16


W_conv1 = weight_variable([filter_size, filter_size, 1, n_filters_1])

b_conv1 = bias_variable([n_filters_1])



接下来我们可以构建第一个卷积层,我们知道,卷积层出了感知区域大小、卷积核个数两个属性以外,它还包括滑动步长、是否铺平等。所以,这里我们调用tf.nn.conv2d函数,输入数据是x_tensor,而卷积核就是权重矩阵,我们同时设置strides为2*2,即每次在height维度以及width维度滑动步长均为2,而padding设置为'same',即满足了卷积前与卷积后的数据维度一致。除此之外,我们还在卷积后加了偏置向量,并且最终通过了elu激活函数。

h_conv1 = tf.nn.elu(

    tf.nn.conv2d(input=x_tensor,

                 filter=W_conv1,

                 strides=[1, 2, 2, 1],

                 padding='SAME') +

    b_conv1)



为了使得识别准确率较高,我们在第一层卷积输出的基础上,再添一层卷积神经网络,同理,添加方法与第一层基本类似,只不过这里的输入由x_tensor改变为了h_conv1,并且卷积核和偏置向量都是不同的。

n_filters_2 = 16

W_conv2 = weight_variable([filter_size, filter_size, n_filters_1, n_filters_2])

b_conv2 = bias_variable([n_filters_2])

h_conv2 = tf.nn.elu(

    tf.nn.conv2d(input=h_conv1,

                 filter=W_conv2,

                 strides=[1, 2, 2, 1],

                 padding='SAME') +

    b_conv2)




5.添加全连接隐含层

接下来,我们将添加几个全连接层,第一个全连接层充当一种特征变换的作用,或者可以称为隐含层。在由卷积层过渡到隐含层之间,需要对卷积层的输出做一个维度变换,这里我们把维度变换成(batch_size,num_units)的形式。

h_conv2_flat = tf.reshape(h_conv2, [-1, 7 * 7 * n_filters_2])



创造一个全连接层,隐含层神经元个数为1024,然后通过一个非线性函数elu。

n_fc = 1024

W_fc1 = weight_variable([7 * 7 * n_filters_2, n_fc])

b_fc1 = bias_variable([n_fc])

h_fc1 = tf.nn.elu(tf.matmul(h_conv2_flat, W_fc1) + b_fc1)




6.添加Dropout防止过拟合

为了防止出现过拟合,我们设置一个神经元保留概率keep_prob,然后对输出层进行dropout操作。

keep_prob = tf.placeholder(tf.float32)

h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)




7.添加Softmax层

最后一层我们加上softmax操作,得到关于10个数字的分类概率。

W_fc2 = weight_variable([n_fc, 10])

b_fc2 = bias_variable([10])

y_pred = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)




8.获取目标函数

根据交叉熵公式得到误差函数,并且建立Adam优化器。

cross_entropy = -tf.reduce_sum(y * tf.log(y_pred))

optimizer = tf.train.AdamOptimizer().minimize(cross_entropy)




计算分类正确率。

correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y, 1))

accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))




9.新建会话并进行mini-batch训练

新建一个会话,并且初始化所有变量。

sess = tf.Session()

sess.run(tf.initialize_all_variables())




这里使用mini-batch来训练,设置mini-batch大小为100,设置迭代周期为5,注意这里每次run的是优化器。

batch_size = 100

n_epochs = 5

for epoch_i in range(n_epochs):

    for batch_i in range(mnist.train.num_examples // batch_size):

        batch_xs, batch_ys = mnist.train.next_batch(batch_size)

        sess.run(optimizer, feed_dict={

            x: batch_xs, y: batch_ys, keep_prob: 0.5})

    print(sess.run(accuracy,

                   feed_dict={

                       x: mnist.validation.images,

                       y: mnist.validation.labels,

                       keep_prob: 1.0

                   }))

(程序结果运行需要下载mnist手写字识别数据库)

 
 
 
 
来源:张泽旺 深度学习每日摘要
智造家