基于TensorFlow实现卷积神经网络 3

来源:互联网 发布:建站影响以后的优化吗 编辑:程序博客网 时间:2024/05/18 00:07

TensorFlow实现CNN

搭建TensorFlow环境

一、实验介绍

1.1 实验内容

上节实验我们使用 TensorFlow 实现了一个简单的神经网络,本节实验我们将使用自己的图片数据,学习如何使用 TensorFlow 实现一个卷积神经网络(Convolutional Neural Networks, CNN)。

关于 CNN 的理论基础请学习 861 基于卷积神经网络实现图片风格的迁移. 卷积神经网络详解章节

1.2 实验知识点

  • 什么是卷积神经网络
  • TensorFlow 实现 CNN

1.3 实验环境

  • python2.7
  • Tensorflow 0.8
  • Xfce终端

1.4 先修课程

  • 861 基于卷积神经网络实现图片风格的迁移卷积神经网络详解章节

二、 卷积神经网络

有了神经网络,我们就可以对输入的信息进行特征提取,但是图像包含的信息量巨大,一般的神经网络并不能准确的提取图像的特征,这时 卷积神经网络(Convolutional Neural Networks, CNN) 就是计算机处理图像的助推器,有了它,计算机理解图像就会更准确.

注意:CNN 并不是只用来处理图片,也可以用于处理文本及语音数据等具有部分层级特征的信息。

卷积神经网络包含输入层、隐藏层和输出层,隐藏层又包含 卷积层(conv)池化层(pooling)

下图展示了,卷积层处理图像的基本过程:

此处输入图片的描述

从图上我们可以看出,立方体不断的增加厚度,这是因为图像输入到卷积神经网络后通过 卷积核 来不断的提取特征,每提取一个特征就会增加一个feature map。那么为什么厚度增加了但是却越来越瘦了呢,这是因为 pooling层 的作用,pooling层本质是下采样,通常采用的是最大值pooling和平均值pooling,因为参数太多会导致计算越来越复杂,所以通过pooling 来稀疏参数,使我们的网络不至于太复杂。

现在我们对卷积神经网络已经有了大概的了解,下节我们将通过代码来实现一个简单的卷积神经网络。

三、实现 CNN

3.1 基本流程

通过搭建卷积神经网络来实现sklearn库中的手写数字识别,搭建的卷积神经网络结构如下图所示:

此处输入图片的描述

3.2 开始搭建

3.2.1 准备

打开终端

#激活TensorFlow环境$ cd /home/shiyanlou/tensorflow$ source bin/activate

下载Python中的机器学习包 sklearn

sudo apt-get install python-sklearn

我们将卷积神经网络写在 myCNN.py

$ sudo gedit myCNN.py

如果你需要添加注释,请在文件头部加入

# -*- coding: utf-8 -*-

导入 TensorFlow 和 Numpy:

import tensorflow as tfimport numpy as np

3.2.2 数据预处理

from sklearn.datasets import load_digits #sklearn 为我们提供的手写数字数据集#数据预处理digits = load_digits()X_data = digits.data.astype(np.float32)Y_data = digits.target.astype(np.float32).reshape(-1,1)print X_data.shapeprint Y_data.shape

在终端运行 myCNN.py

$ python myCNN.py

输出为

#输入和输出数据格式(1797, 64)(1797, 1)

继续编辑 myCNN.py

#数据的标准化(normalization)是将数据按比例缩放,#使之落入一个小的特定区间。这样去除数据的单位限制,#将其转化为无量纲的纯数值,便于不同单位或量级的指标能够进行比较和加权。from sklearn.preprocessing import MinMaxScalerscaler = MinMaxScaler()X_data = scaler.fit_transform(X_data)print X_datafrom sklearn.preprocessing import OneHotEncoderY = OneHotEncoder().fit_transform(Y_data).todense() #one-hot编码print Y

在终端运行 myCNN.py

$ python myCNN.py

输出为

#标准化后的数据[[ 0.      0.      0.3125 ...,  0.      0.      0.    ] [ 0.      0.      0.     ...,  0.625   0.      0.    ] [ 0.      0.      0.     ...,  1.      0.5625  0.    ] ...,  [ 0.      0.      0.0625 ...,  0.375   0.      0.    ] [ 0.      0.      0.125  ...,  0.75    0.      0.    ] [ 0.      0.      0.625  ...,  0.75    0.0625  0.    ]]#独热编码[[ 1.  0.  0. ...,  0.  0.  0.] [ 0.  1.  0. ...,  0.  0.  0.] [ 0.  0.  1. ...,  0.  0.  0.] ...,  [ 0.  0.  0. ...,  0.  1.  0.] [ 0.  0.  0. ...,  0.  0.  1.] [ 0.  0.  0. ...,  0.  1.  0.]]

继续编辑 myCNN.py

# 转换为图片的格式 (batch,height,width,channels)X = X_data.reshape(-1,8,8,1)batch_size = 8 # 使用MBGD算法,设定batch_size为8def generatebatch(X,Y,n_examples, batch_size):    for batch_i in range(n_examples // batch_size):        start = batch_i*batch_size        end = start + batch_size        batch_xs = X[start:end]        batch_ys = Y[start:end]        yield batch_xs, batch_ys # 生成每一个batch

清除默认图的堆栈,并设置全局图为默认图

tf.reset_default_graph()

3.2.3 layer 实现

输入层

tf_X = tf.placeholder(tf.float32,[None,8,8,1])tf_Y = tf.placeholder(tf.float32,[None,10])

卷积层 conv1 + 激活层

conv_filter_w1 = tf.Variable(tf.random_normal([3, 3, 1, 10]))conv_filter_b1 =  tf.Variable(tf.random_normal([10]))relu_feature_maps1 = tf.nn.relu(\                tf.nn.conv2d(tf_X, conv_filter_w1,strides=[1, 1, 1, 1], padding='SAME') + conv_filter_b1)'''参数说明:- data_format:表示输入的格式,有两种分别为:“NHWC”和“NCHW”,默认为“NHWC”- input:输入是一个4维格式的(图像)数据,数据的 shape 由 data_format 决定:当 data_format 为“NHWC”输入数据的shape表示为[batch, in_height, in_width, in_channels],分别表示训练时一个batch的图片数量、图片高度、 图片宽度、 图像通道数。当 data_format 为“NHWC”输入数据的shape表示为[batch, in_channels, in_height, in_width]- filter:卷积核是一个4维格式的数据:shape表示为:[height,width,in_channels, out_channels],分别表示卷积核的高、宽、深度(与输入的in_channels应相同)、输出 feature map的个数(即卷积核的个数)。- strides:表示步长:一个长度为4的一维列表,每个元素跟data_format互相对应,表示在data_format每一维上的移动步长。当输入的默认格式为:“NHWC”,则 strides = [batch , in_height , in_width, in_channels]。其中 batch 和 in_channels 要求一定为1,即只能在一个样本的一个通道上的特征图上进行移动,in_height , in_width表示卷积核在特征图的高度和宽度上移动的布长,即 strideheight 和 stridewidth 。-padding:表示填充方式:“SAME”表示采用填充的方式,简单地理解为以0填充边缘,当stride为1时,输入和输出的维度相同;“VALID”表示采用不填充的方式,多余地进行丢弃。具体公式:“SAME”: output_spatial_shape[i]=⌈(input_spatial_shape[i] / strides[i])⌉“VALID”: output_spatial_shape[i]=⌈((input_spatial_shape[i]−(spatial_filter_shape[i]−1)/strides[i])⌉'''

池化层

max_pool1 = tf.nn.max_pool(relu_feature_maps1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')'''- value:表示池化的输入:一个4维格式的数据,数据的 shape 由 data_format 决定,默认情况下shape 为[batch, height, width, channels]其他参数与 tf.nn.cov2d 类型- ksize:表示池化窗口的大小:一个长度为4的一维列表,一般为[1, height, width, 1],因不想在batch和channels上做池化,则将其值设为1。'''print max_pool1

卷积层 conv2

conv_filter_w2 = tf.Variable(tf.random_normal([3, 3, 10, 5]))conv_filter_b2 =  tf.Variable(tf.random_normal([5]))conv_out2 = tf.nn.conv2d(relu_feature_maps1, conv_filter_w2,strides=[1, 2, 2, 1], padding='SAME') + conv_filter_b2print conv_out2

BN归一化层+激活层

batch_mean, batch_var = tf.nn.moments(conv_out2, [0, 1, 2], keep_dims=True)shift = tf.Variable(tf.zeros([5]))scale = tf.Variable(tf.ones([5]))epsilon = 1e-3BN_out = tf.nn.batch_normalization(conv_out2, batch_mean, batch_var, shift, scale, epsilon)'''参数说明:- mean 和 variance 通过 tf.nn.moments 来进行计算: batch_mean, batch_var = tf.nn.moments(x, axes = [0, 1, 2], keep_dims=True),注意axes的输入。对于以feature map 为维度的全局归一化,若feature map 的shape 为[batch, height, width, depth],则将axes赋值为[0, 1, 2]- x 为输入的feature map 四维数据,offset、scale为一维Tensor数据,shape 等于 feature map 的深度depth。'''print BN_outrelu_BN_maps2 = tf.nn.relu(BN_out)

池化层

max_pool2 = tf.nn.max_pool(relu_BN_maps2,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')print max_pool2

将特征图进行展开

max_pool2_flat = tf.reshape(max_pool2, [-1, 2*2*5])

全连接层

fc_w1 = tf.Variable(tf.random_normal([2*2*5,50]))fc_b1 =  tf.Variable(tf.random_normal([50]))fc_out1 = tf.nn.relu(tf.matmul(max_pool2_flat, fc_w1) + fc_b1)

输出层

out_w1 = tf.Variable(tf.random_normal([50,10]))out_b1 = tf.Variable(tf.random_normal([10]))pred = tf.nn.softmax(tf.matmul(fc_out1,out_w1)+out_b1)

3.3.4 开始训练

loss = -tf.reduce_mean(tf_Y*tf.log(tf.clip_by_value(pred,1e-11,1.0)))# Adam优化算法:是一个寻找全局最优点的优化算法,引入了二次方梯度校正。# 相比于基础SGD算法,1.不容易陷于局部优点。2.速度更快train_step = tf.train.AdamOptimizer(1e-3).minimize(loss)y_pred = tf.arg_max(pred,1)bool_pred = tf.equal(tf.arg_max(tf_Y,1),y_pred)accuracy = tf.reduce_mean(tf.cast(bool_pred,tf.float32)) # 准确率with tf.Session() as sess:    sess.run(tf.initialize_all_variables())    for epoch in range(100): # 迭代100个周期        for batch_xs,batch_ys in generatebatch(X,Y,Y.shape[0],batch_size): # 每个周期进行MBGD算法            sess.run(train_step,feed_dict={tf_X:batch_xs,tf_Y:batch_ys})        res = sess.run(accuracy,feed_dict={tf_X:X,tf_Y:Y})        print (epoch,res)//打印每次迭代的准确率    res_ypred = y_pred.eval(feed_dict={tf_X:X,tf_Y:Y}).flatten() # 只能预测一批样本,不能预测一个样本    print res_ypred

保存 myCNN.py,重新回到终端

$ python myCNN.py

如果你的程序开始训练了,那么恭喜你搭建了一个简单的 CNN。

完整代码获取:

$ cd /home/shiyanlou/tensorflow$ wget http://labfile.oss.aliyuncs.com/courses/893/myCNN.py

四、实验总结

至此,本次实验结束,我们对所进行的工作进行总结:

  • 首先,我们学习了TensorFlow 的基本用法,亲自动手搭建了 TensorFlow 0.8 的环境
  • 第二次实验,我们通过搭建神经网络学习如何使用 TensorFlow
  • 最后我们搭建了卷积神经网络

CNN是我们学习深度学习的基础之一,它非常的强大,与高科技息息相关,只有充分的理解卷积神经网络,才能在深入深度学习的过程中披荆斩棘。后续课程我们将介绍另一个经典的网络模型RNN与LSTM模型

五、课后习题

  1. 请你完成训练完成之后训练模型的保存。
  2. [理解归一化层]我们在第100次个batch size 迭代时,准确率就快速接近收敛了,这得归功于 Batch Normalization 的作用!如果模型应用于单个样本,请你观察预测效果,是否发现我们会得到相反的预测效果,请思考原因。

六、参考链接

  1. TensorFlow 英文官方网站
  2. TensorFlow 官方GitHub仓库