1. 程式人生 > >機器學習筆記(十二):TensorFlow實現四(影象識別與卷積神經網路)

機器學習筆記(十二):TensorFlow實現四(影象識別與卷積神經網路)

1 - 卷積神經網路常用結構

1.1 - 卷積層

我們先來介紹卷積層的結構以及其前向傳播的演算法。

一個卷積層模組,包含以下幾個子模組:

  • 使用0擴充邊界(padding)
  • 卷積視窗過濾器(filter)
  • 前向卷積
  • 反向卷積(可選)
    2018042521470222.png

1.1.2 - 邊界填充

邊界填充將會在影象邊界周圍新增值為0的畫素點,如下圖所示:
20180425213940990.png

使用0填充邊界有以下好處:

  • 卷積了上一層之後的CONV層,沒有縮小高度和寬度,這對建立更深的網路非常重要,否則在更深層時,高度/寬度會縮小。一個重要的例子是“same”卷積,自重高度/寬度在卷積完一層之後會被完全保留。

  • 它可以幫助我們在影象邊界保留更多資訊,在沒有填充的情況下,卷積過程中影象邊緣的極少數值會受到過濾器的影響從而導致資訊丟失。

1.1.3 - 卷積視窗過濾器

在這裡,我們要實現第一步卷積,我們要使用一個過濾器來卷積輸入的資料。先來看看下面這個gif :
20180425214517222.gif
在目前比較成熟的卷積神經網路中,一般過濾器使用3X3或者5X5的矩陣

1.3.4 - 卷積神經網路 - 前向傳播

在前向傳播的過程中,我們將使用多種過濾器對輸入的資料進行卷積操作,每個過濾器會產生一個2D的矩陣,我們可以把它們堆疊起來,於是這些2D的卷積矩陣就變成了高維的矩陣。

我們需要實現一個函式以實現對啟用值進行卷積,我們需要在啟用值矩陣AprevA_{prev}上使用過濾器WW進行卷積,該函式的輸入時前一層的啟用輸出AprevA_{prev}

,FF個過濾器,其權重矩陣為WW、偏置矩陣為bb,每個過濾器只有一個偏置,最後,我們需要一個包含了步長ss和填充pp的字典的超引數。

小提示:

  • 如果我要在矩陣A_prev(shape = (5,5,3))的左上角選擇一個2x2的矩陣進行切片操作,那麼可以這樣做:
    a_slice_prev = a_prev[0:2,0:2,:]

  • 如果我想要自定義切片,我們可以這麼做:先定義要切片的位置,vert_start、vert_end、horiz_start、horiz_end,它們的位置我們看一下下面的圖就明白了。
    20180425214940264.png

我們還是說一下輸出維度的計算公式吧:

nH=nHpre

vf+2×padstride+1n_H = \left \lfloor \frac{n_{Hprev}-f+2\times pad}{stride} \right \rfloor+1

nW=nWprevf+2×padstride+1n_W = \left \lfloor \frac{n_{Wprev}-f+2\times pad}{stride} \right \rfloor+1

nC=n_C=過濾器數量

1.2 - 卷積層的TenforFlow實現

TensorFlow對卷積神經網路提供了非常好的支援,下面的程式實現了一個卷積層的前向傳播過程。

#通過tf.get_variable的方式建立過濾器的權重變數和偏置項變數。
#因為卷積層的引數值和過濾器的尺寸、深度以及當前層節點矩陣的深度有關,所以這裡宣告的引數變數
#是一個四維矩陣,前面兩個維度代表了過濾器的尺寸,第三個維度表示當前層的深度,第四個維度表示過濾器的深度

filter_weight = tf.get_variable(
    'weights', [5,5,3,16],
    initializer=tf.truncated_normal_initializer(stddev=0.1))
#和卷積層的權重類似,當前層矩陣上不同位置的偏置項也是共享的,所以總共有一下層深度個不同的偏置項。
#本例程式碼中16位過濾器的深度,也是神經網路中下一層節點矩陣的深度。
biases = tf.get_variable(
    'biases',[16],initializer=tf.constant_initializer(0.1))
#tf.nn.conv2d 提供了一個非常方便的函式來實現卷積層前向傳播的演算法,這個函式的第一個輸入為當前層的節點矩陣
#注意這個矩陣是一個四維矩陣,後面三個維度對應一個節點矩陣,第一維對應一個輸入batch.比如在輸入層,input[0,:,:,:]
#表示第一張圖片,input[1,:,:,:]表示第二張圖片,以此類推。tf.nn.conv2d第二個引數提供了卷積層的權重,第三個引數為
#不同維度上的步長,雖然第三個引數提供的是一個長度為4的陣列,但是第一維和最後一維數字要求一定是1,這是因為卷積層
#的步長只對矩陣的長和寬有效,最後一個引數是填充(padding)的方法,TensorFlow中提供SAME或者VALID兩種選擇。其中SAME
#表示全新增0填充,“VALID”表示不新增
conv = tf.nn.conv2d(
    input,filter_weight,strides=[1,1,1,1],padding='SAME')
#tf.nn.bias_add提供了一個方便的函式給每一個節點加上偏置項。注意這裡不能直接使用加法,因為矩陣上不同位置上的節點都需要
#加上同樣的偏置項。
bias = tf.nn.bias_add(conv,biases)
#將計算結果通過Relu啟用函式完成去線性化
actived_conv = tf.nn.relu(bias)

1.2 - 池化層

池化層會減少輸入的寬度和高度,這樣它會較少計算量的同時也使特徵檢測器對其輸入中的位置更加穩定

  • 最大值池化層:在輸入矩陣中滑動一個大小為fxf_x的視窗,選取窗口裡的值中的最大值,然後作為輸出的一部分。

  • 均值池化層:在輸入矩陣中滑動一個大小為fxf_x的視窗,計算窗口裡的值中的平均值,然後這個均值作為輸出的一部分
    20180425215218806.png

池化層沒有用於反向傳播的引數,但是它們有像視窗的大小為f的超引數,它制定fxf視窗的高度和寬度,我們可以計算出最大值或平均值。

1.4.1 - 池化層的前向傳播

現在我們要在同一個函式中實現最大值池化層和均值池化層(最大值池化層應用的更多),和之前計算輸出維度一樣,池化層的計算也是一樣的。
nH=nHprevfstride+1n_H = \left \lfloor \frac{n_{Hprev}-f}{stride} \right \rfloor+1

nW=nWprevfstride+1n_W = \left \lfloor \frac{n_{Wprev}-f}{stride} \right \rfloor+1

nC=nCprevn_C=n_{Cprev}

1.3 - 池化層的TensorFlow實現

#tf.nn.max_pool實現了最大池化層的前向傳播過程,它的引數和tf.nn.conv2d函式類似。
#ksize提供了過濾器的尺寸,strides提供了步長資訊,padding提供了是否使用全0填充
pool = tf.nn.max_pool(
    actived_conv,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')

對比池化層和卷積層前向傳播在TensorFlow中的實現,可以發現函式的引數形式是相似的。在tf.nn.max_pool函式中,首先需要傳入當前層的節點矩陣,這個矩陣是一個四維矩陣,格式和tf.nn.conv2d函式中的第一個引數一致,第二個引數為過濾器的尺寸,雖然給出的是一個長度為4的一維陣列,但是這個陣列的第一個和最後一個數必須為1.這意味著池化層的過濾器是不可以跨不同輸入樣例或者節點矩陣深度的。

在實際應用中使用得最多的池化層過濾器尺寸為【1,2,2,1】或者【1,3,3,1】