最近一直在思考是出國還是找工作的事,和三年前一樣又到了做決定的時候。真的很心累,撿起了我的MatConvNet,重新看一了一下牛津派的編程藝術。只有沉浸式的工作,才能安靜下來吧。
1. vl_nncov - CNN的卷積操作
Y = VL_NNCONV(X, F, B)計算圖像X與濾波器組F/偏置B之間的卷積操作。如果B是一個空矩陣,就是沒有偏置參與;如果F是一個空矩陣,圖像並不進行卷積操作,但是仍然會執行添加偏置B、下采樣stride、以及邊緣補充padding。
- X 圖像是一個四維數據結構 = H x W x C x N。其中 (H,W) 是一個圖像通道的寬度和高度。 C 是特徵通道的數量, N是Batchsize中圖像的數量。
- F 濾波器組是一個四維數據結構 = FW x FH x FC x K。其中 (FH,FW) 是濾波器的高度和寬度, K是濾波器組中濾波器的個數. FC 是每個濾波器接收到特徵圖的數量,FC必須與圖像X中的特徵通道的數量相匹配。 Alternatively, FC can
- divide* the C; in this case, filters are assumed to form G=C/FC
- groups* of equal size (where G must divide K). Each group of filters works on a consecutive subset of feature channels of the input array X.
[DX, DF, DB] = VL_NNCONV(X, F, B, DY) 計算導數。DX, DF, DB, DY和 X, F, B, and Y具有相同的維度。 特別地,如果B是空矩陣,那麼DB也是空矩陣。
VL_NNCONV() 實現了一個特殊的全連接模式。
關於VL_NNCONV的可選項參數問題,主要指跨步卷積、邊緣填充、卷積核膨脹選項:
VL_NNCONV(..., 'option', value, ...) :
-
跨步卷積和默認值 = stride
[1] 可以看成是降採樣步驟,stride=1表示沒有降採樣. 如果設置stride=2,意味着橫向和縱向都進行1/2採樣操作;如果設置stride=[2,4], 意味着水平進行了1/2的降採樣,垂直進行了1/4的降採樣。 -
邊緣填充和默認值 = Pad
[0]爲輸入圖像進行邊緣填充。該步驟在卷積操作之前執行,填充的值都是0。[TOP BOTTOM LEFT RIGHT]指爲上下左右填充不同數目的0. 如果指定 pad=1,意味着上下左右都填充單位爲1的0佔位。大多數情況下我是反對邊緣填充的。 -
卷積核膨脹及默認值 = Dilate
[1] 將卷積核進行膨脹運算,膨脹之後0佔位,具體如下所示。通過設置 dilate = [dilate_y, dilate_x]對卷積核不同方向進行膨脹。大多數情況下我也反對卷積核膨脹運算,雖然說大尺寸卷積核有着更好的感知域,但是14年YZ的研究表明,卷積核越小,深度模型擬合得越好。
[1 3] if dilate = 2; [1 0 3]
[2 4] [0 0 0]
[2 0 4]
卷積之後圖像尺寸的計算公式爲:
format for padding and stride:
YH = floor((H + (PADTOP+PADBOTTOM) - FH)/STRIDEY) + 1
YW = floor((W + (PADLEFT+PADRIGHT) - FW)/STRIDEX) + 1
format for padding , stride, and dilate:
YH = floor((H + (PADTOP+PADBOTTOM) - FH*(DILATEY-1) -1)/STRIDEY) + 1
YW = floor((W + (PADLEFT+PADRIGHT) - FW*(DILATEX-1) -1)/STRIDEX) + 1
2. vl_nnReLU - CNN的線性整流單元
Y = VL_NNRELU(X) 對數據X應用線性整流單元函數。X可以是人任意大小。
DZDX = VL_NNRELU(X, DZDY) 計算塊投影到DZDY上的導數. DZDX,DZDY 與 X , Y 具有相同的維度。
VL_NNRELU(...,'OPT',VALUE,...) 具有以下選項:
泄露因子 = leak
[0] 設置泄露因子,他是一個非負數. 如果X大於等於0,那麼Y=X; 否則, Y=X*leak. 默認情況下,我們使用leak=0就夠了。
3. vl_nnpool - CNN的池化處理
Y = VL_NNPOOL(X, POOL) 對數據的所有通道施加池化操作。X是四維的數據結構 H x W x D x N,其中 (H,W) 是一層的寬度和高度,D是圖像深度,可以理解爲特徵通道數。N是圖像數目。
Y = VL_NNPOOL(X, [POOLY, POOLX]) 使用矩形窗進行池化,矩形窗的尺寸爲[pool_y, pool_x].
DZDX = VL_NNPOOL(X, POOL, DZDY)計算模塊在DZDY方向上的偏導數。 DZDX,DZDY和 X,Y各自維度對齊。
VL_NNPOOL(..., 'option', value, ...)具有下面的選項參數:
-
跨步池化 = stride
[1] 降採樣操作.可以設置爲同質的標量 stride = [2,2]; 也可以設置爲異質的矢量stride = [2,3]. -
邊緣填充 = pad
[0]:和卷積裏面的操作一樣的 -
池化方法 = method
['max']: 指定池化的方法。可以選擇最大池化 'method' = 'max'; 也可以選擇平局池化 ‘method’ = ‘avg’
4. vl_nnnormalize - CNN局部響應歸一化
LRN是一種提高深度學習準確度的技術方法。LRN一般是在激活、池化函數後的一種方法。 在ALexNet中,提出了LRN層,對局部神經元的活動創建競爭機制,使其中響應比較大對值變得相對更大,並抑制其他反饋較小的神經元,增強了模型的泛化能力。
在2012的Alexnet網絡中具體計算公式如下:
但是,這個是也很有爭議,2015年針對更深更大的圖像識別模型,有研究學者報道LRN並沒啥用...反而增加了內存的消耗,還浪費時間....
dagnn.LRN('param',[5 1 0.0001/5 0.75]) ;
5. vl_nnbnorm - CNN批歸一化
BatchNorm也是一種旨在提高深度學習精度的方法。與LRN相比,BatchNorm具有容易理解的數學意義。
Y = VL_NNBNORM(X,G,B) 對輸入數據X執行批歸一化操作。批歸一化定義如下:
Y(i,j,k,t) = G(k) * (X(i,j,k,t) - mu(k)) / sigma(k) + B(k)
% where:
mu(k) = mean_ijt X(i,j,k,t) % 均值
sigma2(k) = mean_ijt (X(i,j,k,t) - mu(k))^2 % 方差
sigma(k) = sqrt(sigma2(k) + EPSILON) % 標準差
G(k) 和B(k)分別相乘因子和相加因子,用於處理各通道中的尺度問題,實際中可以設置他們爲常數。
均值和方差通過所有的4D張量X逐漸累積。 常數EPSILON用於正則化sigma(k)和避免零除情況。
[DZDX,DZDG,DZDB] = VL_NNBNORM(X,G,B,DZDY) computes the derviatives of the block projected onto DZDY. DZDX, DZDG, DZDB and DZDY have the same dimensions as X, G, B, and Y respectivey.
VL_NNBNROM(..., 'Option', value)的可選項設置:
-
微量 = epsilon
[1e-4] 指定微量值小一點就好. -
動量匹配 = moments
[unspecified]:爲了匹配mu和sigma,這是一個很有用的技巧在測試階段,用於禁止批歸一化 -
CuDNN
[specified] 如果指定,使用CuDNN. 默認情況下,CuDNN都是開啓的 -
NoCuDNN
[not specified] 如果指定,批歸一化就不採用CuDNN.
具體的BatchNorm的原理細節需要單獨再弄一下。