您的位置:首页 > 理论基础 > 计算机网络

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

2017-08-28 16:15 531 查看

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 tf
import 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.shape
print Y_data.shape

在终端运行
myCNN.py


$ python myCNN.py

输出为

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

继续编辑
myCNN.py


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

from sklearn.preprocessing import OneHotEncoder
Y = 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为8

def 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_b2
print 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-3
BN_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_out
relu_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模型


五、课后习题

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

六、参考链接

TensorFlow 英文官方网站
TensorFlow 官方GitHub仓库
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: