天天看點

基于TensorFlow實作卷積神經網絡 3TensorFlow實作CNN搭建TensorFlow環境

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)

下圖展示了,卷積層處理圖像的基本過程:

基于TensorFlow實作卷積神經網絡 3TensorFlow實作CNN搭建TensorFlow環境

從圖上我們可以看出,立方體不斷的增加厚度,這是因為圖像輸入到卷積神經網絡後通過

卷積核

來不斷的提取特征,每提取一個特征就會增加一個

feature map

。那麼為什麼厚度增加了但是卻越來越瘦了呢,這是因為

pooling層

的作用,pooling層本質是下采樣,通常采用的是最大值pooling和平均值pooling,因為參數太多會導緻計算越來越複雜,是以通過

pooling

來稀疏參數,使我們的網絡不至于太複雜。

現在我們對卷積神經網絡已經有了大概的了解,下節我們将通過代碼來實作一個簡單的卷積神經網絡。

三、實作 CNN

3.1 基本流程

通過搭建卷積神經網絡來實作sklearn庫中的手寫數字識别,搭建的卷積神經網絡結構如下圖所示:

基于TensorFlow實作卷積神經網絡 3TensorFlow實作CNN搭建TensorFlow環境

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

實作

輸入層

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模型

五、課後習題

  1. 請你完成訓練完成之後訓練模型的儲存。
  2. [了解歸一化層]我們在第100次個batch size 疊代時,準确率就快速接近收斂了,這得歸功于

    Batch Normalization

    的作用!如果模型應用于單個樣本,請你觀察預測效果,是否發現我們會得到相反的預測效果,請思考原因。

六、參考連結

  1. TensorFlow 英文官方網站
  2. TensorFlow 官方GitHub倉庫

繼續閱讀