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)
。
下圖展示了,卷積層處理圖像的基本過程:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLi02dvw1ZuBnL4gTMwgDM5MDMxATNxAXbhR3cl1Wa0BDNzMDZpJWYsFjM4ADN0QWa11CduVWb1N2bk9CXl1mL49mYx5yYvRWL0V3biFWLn5WaoRXeuFWLuR2Lc9CX6MHc0RHaiojIsJye.jpg)
從圖上我們可以看出,立方體不斷的增加厚度,這是因為圖像輸入到卷積神經網絡後通過
卷積核
來不斷的提取特征,每提取一個特征就會增加一個
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
如果你需要添加注釋,請在檔案頭部加入
導入 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(,)
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(,,,)
batch_size = # 使用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
實作
layer
輸入層
tf_X = tf.placeholder(tf.float32,[None,,,])
tf_Y = tf.placeholder(tf.float32,[None,])
卷積層 conv1 + 激活層
conv_filter_w1 = tf.Variable(tf.random_normal([, , , ]))
conv_filter_b1 = tf.Variable(tf.random_normal([]))
relu_feature_maps1 = tf.nn.relu(\
tf.nn.conv2d(tf_X, conv_filter_w1,strides=[, , , ], 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=[,,,],strides=[,,,],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, [, , ], keep_dims=True)
shift = tf.Variable(tf.zeros([]))
scale = tf.Variable(tf.ones([]))
epsilon =
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, [-, **])
全連接配接層
fc_w1 = tf.Variable(tf.random_normal([**,]))
fc_b1 = tf.Variable(tf.random_normal([]))
fc_out1 = tf.nn.relu(tf.matmul(max_pool2_flat, fc_w1) + fc_b1)
輸出層
out_w1 = tf.Variable(tf.random_normal([,]))
out_b1 = tf.Variable(tf.random_normal([]))
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,,)))
# Adam優化算法:是一個尋找全局最優點的優化算法,引入了二次方梯度校正。
# 相比于基礎SGD算法,1.不容易陷于局部優點。2.速度更快
train_step = tf.train.AdamOptimizer().minimize(loss)
y_pred = tf.arg_max(pred,)
bool_pred = tf.equal(tf.arg_max(tf_Y,),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個周期
for batch_xs,batch_ys in generatebatch(X,Y,Y.shape[],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倉庫