1. 程式人生 > >TensorFlow學習(九):各種卷積網路

TensorFlow學習(九):各種卷積網路

更新:

2017.5.13
刪除了一些不常用的內容,可以通過查文件獲得
詳細解釋了conv2d的引數內容和執行過程

2017.12.26
增加了空洞卷積(dilated Conv)的相關內容

一.基本卷積操作(convolution)

基本卷積操作部分主要是講常見的在影象上面的卷積運算(conv2d).
也就是tf.nn.conv2d()這個函式,在開始使用這個函式之前,首先要知道這代表是一個卷積運算,然後要知道,這個函式對於輸入的格式有一些前提條件.如果瞭解了卷積的原理,這些輸入的格式條件看上去還是很自然的,要是對於卷積的原理不瞭解,就會很不解.所以還是先把原理的基礎打好,再來理解函式的使用.
卷積還有很多其他的函式,這裡就不全部都講了,可以看官方那個文件來理解.

Ⅰ.二維卷積(最常用)

tf.nn.conv2d(input,filter,strides,padding,use_cudnn_on_gpu=None,data_format=None,name=None)

作用:
對於給定的輸入和濾波器tensor(注意,這裡輸入的濾波器引數都是4維的)計算2維的卷積,其中輸入的形狀是[batch, in_height, in_width, in_channels],濾波器/卷積核的形狀是[filter_height, filter_width, in_channels, out_channels]

引數:
input:

Tensor. 輸入,元素必須是下面的型別: half, float32, float64.形狀為[batch, in_height, in_width, in_channels]詳細來說:

batch就是一個batch的size,比如你同時丟100個樣本什麼的
in_height是樣本的高
in_width就是樣本的寬度
in_channels就是樣本的通道數

filter: Tensor. 和輸入的型別需要是一樣的。形狀為[filter_height, filter_width, in_channels, out_channels]

filter_height你可以形象的理解為一個滑動視窗的高度,
filter_width就可以是一個滑動視窗的寬度,
in_channels表示作用的輸入的通道數(深度),所以你會發現他和input裡面的最後一個引數in_channels應該是相同的!>>out_channels表示輸出的feature map的深度,其實這裡本質就是filter的個數。

strides: int型別的一維列表,長度為4,表示對於輸入各個維度上面滑動的步長,一般來說,平時使用的[1,x_stride,y_stride,1],更加一般的,維度的順序由下面的data_format引數決定.
padding: 兩個選項 “SAME”或者 “VALID”. SAME表示會有zero padding,再步長為1時候,能夠得到一個和原圖寬高尺寸一樣的輸出.VALID則採用丟棄或者不zero padding的方式。
use_cudnn_on_gpu: 可選,預設為True
data_format: 有兩個選項,”NHWC”(預設), “NCHW”。指明輸入資料和輸出資料的格式,對於”NHWC”,資料儲存的格式[batch, in_height, in_width, in_channels] 對於”NCHW”, 資料儲存順序為: [batch, in_channels, in_height, in_width].
name: 可選,操作名稱

要是這裡看不懂的話,這裡舉一個例子來講一下這個函式的功能。這裡我們用tensorflow實現那個很出名的動圖
這裡寫圖片描述

程式碼:

import numpy as np
import tensorflow as tf

x=np.array([[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],
            [[0,0,0],[0,1,2],[1,1,0],[1,1,2],[2,2,0],[2,0,2],[0,0,0]],
            [[0,0,0],[0,0,0],[1,2,0],[1,1,1],[0,1,2],[0,2,1],[0,0,0]],
            [[0,0,0],[1,1,1],[1,2,0],[0,0,2],[1,0,2],[0,2,1],[0,0,0]],
            [[0,0,0],[1,0,2],[0,2,0],[1,1,2],[1,2,0],[1,1,0],[0,0,0]],
            [[0,0,0],[0,2,0],[2,0,0],[0,1,1],[1,2,1],[0,0,2],[0,0,0]],
            [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]])

#print("x[:,:,0]:\n",x[:,:,0])
#print("x[:,:,1]:\n",x[:,:,1])
#print("x[:,:,2]:\n",x[:,:,2])

W=np.array([[[[1,-1,0],[1,0,1],[-1,-1,0]],
             [[-1,0,1],[0,0,0],[1,-1,1]],
             [[-1,1,0],[-1,-1,-1],[0,0,1]]],

            [[[-1,1,-1],[-1,-1,0],[0,0,1]],
             [[-1,-1,1],[1,0,0],[0,-1,1]],
             [[-1,-1,0],[1,0,-1],[0,0,0]]]])

'''
#reshap to 4-D
#print(x.shape)

#print(x.shape)
#print(x[0][:,:,0])
'''

#this
x=np.reshape(a=x,newshape=(1,7,7,3))

#w

'''
print(W.shape)
print(W[0][:,:,0])
print(W[0][:,:,1])
print(W[0][:,:,2])
print(W[1][:,:,0])
print(W[1][:,:,1])
print(W[1][:,:,2])
'''

#this can make the W'th format match the fuction's
W=np.transpose(W,axes=(1,2,3,0))
print(W.shape)

#
b=np.array([1,0])
#define graph
graph=tf.Graph()
with graph.as_default():
   input=tf.constant(value=x,dtype=tf.float32,name="input")
   filter=tf.constant(value=W,dtype=tf.float32,name="filter")
   bias=tf.constant(value=b,dtype=tf.float32,name="bias")
   result=tf.nn.conv2d(input=input,filter=filter,strides=[1,2,2,1],padding="VALID",name="conv")+bias

with tf.Session(graph=graph) as sess:
    r=sess.run(result)
    print(r.shape)
    print(r[0][:,:,0])
    print(r[0][:,:,1])

結果:
這裡寫圖片描述

是不是和圖上的結果是一樣的?現在來詳細的講一下這段程式碼.

載入什麼包就不講了,首先是定義了一個輸入:
這裡寫圖片描述
這個就是圖中最左邊的x了對吧,也就是說,這個就是我們的輸入了.這裡應該很好理解.

然後就是定義濾波器(“滑動視窗”)
這裡寫圖片描述
這個就是影象w的復現,照著做就是了,也是很簡單的.

接下來就開始要注意的.
一開始定義的x的形狀是什麼呢?是7x7x3(高x寬x通道), 但是卷積操作這個函式需要的形狀是4-D的,少了一維,少的那個維度就是batch,也就是樣本數量.很簡單,改變一下形狀就行了.x=np.reshape(a=x,newshape=(1,7,7,3))於是就把原來的形狀變為了1x7x7x3,這樣就滿足了函式的要求了.

然後就是w,w表示的是濾波器的引數,這個我們知道.但是我們定義w的時候,你看形狀可以看出來是2x3x3x3(濾波器數量x高x寬x輸入的通道),為什麼這麼寫,是因為這麼寫是能夠想到的最自然的寫法了,很清晰.動圖上面也是寫麼給出的.w[0]表示第一個濾波器,w[1]表示第二個濾波器……
但是!這樣的格式不符合函式的要求,看前面函式引數的要求裡面,這裡濾波器的引數要是[filter_height, filter_width, in_channels, out_channels] 也就是說是(高x寬x輸入通道x濾波器數量),這時候就用矩陣的轉秩就行了,把第幾個維度轉到另外的維度
W=np.transpose(W,axes=(1,2,3,0))這句話之後,w就變成了函式要求的樣子.把一開始的第0維轉到了最後一維.

到這裡,你其實可以根據理論算出來,這個輸入和濾波器進行卷積之後的輸入的形狀.為1x3x3x2(樣本數量x高x寬x通道數),你會發現,這裡的樣本數量是不變的,輸入樣本的數量是多少,這裡還是多少(廢話),中間的高和寬需要自己計算一下(不知道怎麼算的可以看一下開頭給出的博文).最後一個通道數其實就是卷積核的數量.要是理解的理論,這些還是很好理解的.

所以對於偏置的形狀應當怎麼設定呢?很簡單,卷積核有n個,偏置的形狀就是(n,).因為一個卷積核對應一個偏置,很容易就算出來了.比如這裡的偏置b定為[1,0],因為有兩個卷積核.

再講一下卷積操作.
這裡寫圖片描述
其他的簡單就不說了,卷積那裡,strides=[1,2,2,1],中間的兩個表示一次橫向移動和縱向移動的大小.在動圖裡面是2,那麼這裡設為2就行了.
padding=''VALID''表示不需要”加圈”

然後,大家應該對於這個卷積操作的函式會使用了.

Ⅱ.空洞卷積

空洞卷積的核心思想就是”空洞卷積相當於更大的填零標準卷積產生的效果”.所以空洞卷積能夠通過一個小的卷積達到更大的卷積能夠達到的視野.
空洞卷積可以參考之前的一些東西.
atrous_conv2d(value,filters,rate,padding,name=None)
引數:

value:float型別的4-DTensor 。它需要是預設的“NHWC”格式,形狀是[batch, in_height, in_width, in_channels]
filters:和value 型別想同的4-DTensor,形狀為[filter_height, filter_width, in_channels, out_channels]filtersin_channels尺寸必須和value 的相匹配。也其實就說說,value的第四維要和filter的第三維是一樣的. 空洞卷積相當於上取樣濾波器高度為filter_height + (filter_height - 1) * (rate - 1),寬度為filter_width + (filter_width - 1) * (rate - 1) 的標準卷積,通過插入(rate-1)個0來產生.
rate:要求是一個int型的正數,正常的卷積操作應該會有stride(即卷積核的滑動步長),但是空洞卷積是沒有stride引數的,這一點尤其要注意。取而代之,它使用了新的rate引數,那麼rate引數有什麼用呢?它定義為我們在輸入影象上卷積時的取樣間隔,你可以理解為卷積核當中穿插了(rate-1)數量的“0”,把原來的卷積核插出了很多“洞洞”,這樣做卷積時就相當於對原影象的取樣間隔變大了。具體怎麼插得,可以看後面更加詳細的描述。此時我們很容易得出rate=1時,就沒有0插入,此時這個函式就變成了普通卷積。
padding:一個字串,’VALID’或者’SAME’。填充演算法。
name:可選,返回張量的名稱。
這裡要注意的是,他沒有stride的引數,是因為stride被固定為1了,不能夠改動.

返回

填充方式為“VALID”時,返回[batch,height-2*(filter_width-1),width-2*(filter_height-1),out_channels]的Tensor
填充方式為“SAME”時,返回[batch, height, width, out_channels]的Tensor

二.池化操作(Pooling)

池化操作的思想算是比較簡單的了.在上面博文的原理中也講了.在這裡,池化操作只詳細講一個平均池化的函式,因為其他的大概的思路功能引數都差不多,所以可以類比使用.
官方文件:Neural Network

列表:
tf.nn.avg_pool
tf.nn.max_pool
tf.nn.max_pool_with_argmax
tf.nn.avg_pool3d
tf.nn.max_pool3d
tf.nn.fractional_avg_pool
tf.nn.fractional_max_pool
tf.nn.pool

tf.nn.avg_pool(value, ksize, strides, padding, data_format=’NHWC’, name=None)

作用:
在input上面執行average pooling
引數:
value: 一個4維tensor,形狀為[batch, height, width, channels] 元素型別可以是 float32, float64, qint8, quint8, or qint32.
ksize: 整形列表,長度 >= 4. 表示視窗在輸入的每個維度上面的尺寸.一般在二維的影象的情況下,都是[1,高,寬,1]
strides: 整形列表,長度 >= 4. 表示視窗滑動在輸入tensor上面每個維度滑動的的步長.和卷積操作是一樣的.
padding: 兩種模式 ‘VALID’ 或者 ‘SAME’.
data_format: 兩種模式 ‘NHWC’ 和 ‘NCHW’
name: 可選,操作名

到這裡,基本你的卷積操作就講完了,這些操作加上一些其他的基本操作,就可以組成更大更加複雜的卷積網路了.