1. 程式人生 > >深度學習之四:卷積神經網路基礎

深度學習之四:卷積神經網路基礎

計算機視覺在深度學習的幫助下取得了令人驚歎的進展,其中發揮重要作用的是卷積神經網路。本節總結了卷積神經的原理與實現方法。

1 卷積神經網路

1.1 計算機視覺與深度學習

計算機視覺要解決的問題是如何讓機器理解現實世界的現象。目前主要處理的問題如影象分類,目標檢測,風格變換等。

對於小型圖片,如MNIST的圖片處理,影象大小為64*64,使用全連線神經網路還可以處理。如果影象較大,如1000*1000*3時,單個輸入層就需要3M的節點,全連線情況下,單層的權重就可能多達數十億的待訓練引數。這將導致神經網路無法進行有效訓練。因此業界提出卷積神經網路解決引數過多的問題。

先看一個卷積神經網路的結構,在卷積神經網路中包括三種類型的層:卷積層,池化層和全連線層。下面幾節介紹卷積神經網路的基本概念。
此處輸入圖片的描述

1.2 過濾器

在卷積神經網路中,過濾器也稱為卷積核,通常是3*3或5*5的小型矩陣(先忽略每個點可能有多個通道)。過濾器的作用是和輸入影象的各個區域逐次進行卷積。

所謂卷積運算是計算兩個矩陣的對應元素(Element-wise)乘積的和。卷積本質是兩個向量的內積,體現了兩個向量的距離。

下圖展示了過濾器在輸入影象上的卷積計算的過程:
輸入影象與過濾器的卷積計算過程

因此過濾器可以捕獲影象中與之類似模式的影象區域。例如下圖展示了一個過濾器的數值矩陣與圖形。
此處輸入圖片的描述

當使用這個過濾器在影象上移動並計算卷積時,不同的區域會有不同的卷積結果。
此處輸入圖片的描述

在類似於的模式下過濾器與影象區域的卷積會產生較大的值。
此處輸入圖片的描述
不相似的區域則產生較小的值。
此處輸入圖片的描述

此外在一層上捕獲的模式可以組合為更復雜的模式並輸入到下一層,從而使得下一層能識別更復雜的影象模式。

訓練CNN在一定意義上是在訓練每個卷積層的過濾器,讓其組合對特定的模式有高的啟用,以達到CNN網路的分類/檢測等目的。

1.3 Padding

對於一個shape為[n,n]的影象,若過濾器的 shape為[f,f],在卷積時每次移動1步,則生成的矩陣shape為[nf+1,nf+1]。這樣如果經過多層的卷積後,輸出會越來越小。另一方面影象邊界的點的特徵不能被過濾器充分捕獲。

通過對原始影象四個邊界進行padding,解決上面提到的兩個問題。術語Same填充,會保證輸出矩陣與輸入矩陣大小相同,因此其需要對輸入影象邊界填充

f12個象素。另一個術語Valid填充表示不填充。

通過下圖體會Padding對輸出的影響
此處輸入圖片的描述

下面的程式碼展示了使用0填充,向X邊界填充pad個畫素的方法

def zero_pad(X, pad):
    X_pad = np.pad(X, ((0, 0), (pad, pad), (pad, pad), (0, 0)), 'constant', constant_values=0)

    return X_pad

1.4 卷積步長

另一個影響輸出大小的因素是卷積步長(Stride)。
對於一個shape為[n,n]的影象,若過濾器的 shape為[f,f],卷積步長為s,邊界填充p個象素,則生成的矩陣shape為[n+2pfs+1,n+2pfs+1]

1.5 卷積層記法說明

對於第l層的卷積層,nc[l]為該層的卷積核數,其輸入矩陣的維度為

[nH[l1],nW[l1],nc[l1]]

輸出矩陣的維度以channel-last展示為[nH[l],nW[l],nc[l]],各維度值如下:
nH[l]=nH[l1]+2p[l]f[l]s[l]+1
nW[l]=nW[l1]+2p[l]f[l]s[l]+1

下面的程式碼展示了影象卷積計算的方法

def conv_single_step(a_slice_prev, W, b):
    """
    Apply one filter defined by parameters W on a single slice (a_slice_prev) of the output activation
    of the previous layer.

    Arguments:
    a_slice_prev -- slice of input data of shape (f, f, n_C_prev)
    W -- Weight parameters contained in a window - matrix of shape (f, f, n_C_prev)
    b -- Bias parameters contained in a window - matrix of shape (1, 1, 1)

    Returns:
    Z -- a scalar value, result of convolving the sliding window (W, b) on a slice x of the input data
    """
    s = np.multiply(a_slice_prev, W) + b
    Z = np.sum(s)
    return Z


def conv_forward(A_prev, W, b, hparameters):
    """
    Implements the forward propagation for a convolution function

    Arguments:
    A_prev -- output activations of the previous layer, numpy array of shape (m, n_H_prev, n_W_prev, n_C_prev)
    W -- Weights, numpy array of shape (f, f, n_C_prev, n_C)
    b -- Biases, numpy array of shape (1, 1, 1, n_C)
    hparameters -- python dictionary containing "stride" and "pad"

    Returns:
    Z -- conv output, numpy array of shape (m, n_H, n_W, n_C)
    cache -- cache of values needed for the conv_backward() function
    """

    (m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape

    (f, f, n_C_prev, n_C) = W.shape

    stride = hparameters['stride']
    pad = hparameters['pad']

    n_H = int((n_H_prev - f + 2 * pad) / stride) + 1
    n_W = int((n_W_prev - f + 2 * pad) / stride) + 1

    # Initialize the output volume Z with zeros.
    Z = np.zeros((m, n_H, n_W, n_C))

    # Create A_prev_pad by padding A_prev
    A_prev_pad = zero_pad(A_prev, pad)

    for i in range(m):  # loop over the batch of training examples
        a_prev_pad = A_prev_pad[i]  # Select ith training example's padded activation
        for h in range(n_H):  
            for w in range(n_W): 
                for c in range(n_C):  # loop over channels (= #filters) of the output volume
                    # Find the corners of the current "slice"
                    vert_start = h * stride
                    vert_end = vert_start + f
                    horiz_start = w * stride
                    horiz_end = horiz_start + f
                    # Use the corners to define the (3D) slice of a_prev_pad (See Hint above the cell). 
                    a_slice_prev = a_prev_pad[vert_start:vert_end, horiz_start:horiz_end, :]
                    # Convolve the (3D) slice with the correct filter W and bias b, to get back one output neuron.
                    Z[i, h, w, c] = conv_single_step(a_slice_prev, W[..., c], b[..., c])

    assert (Z.shape == (m, n_H, n_W, n_C))

    cache = (A_prev, W, b, hparameters)

    return Z, cache

1.6 池化層

有兩種型別的池化層,一種是最大值池化,一種是均值池化。
下圖體現了最大池化的作用,其過濾器大小為2,步幅為2:
此處輸入圖片的描述
均值池化與最大池化不同的是計算相應區域的均值,而不是取最大值。
對池化層各維度值如下: