深度學習三:卷積神經網絡


卷積神經網絡

卷積神經網絡(Convolutional Neural Network,CNN)又叫卷積網絡(Convolutional Network),是一種專門用來處理具有類似網格結構的數據的神經網絡。卷積神經網絡一詞中的卷積是一種特殊的線性運算。卷積網絡是指那些至少在網絡的一層中使用卷積運算來代替一般的矩陣乘法的神經網絡。

卷積神經網絡的出現,極大的緩解了全連接神經網絡中存在的一個典型的問題:數據的波形被忽視了!眾所周知,全連接神經網絡在進行數據輸入的時候,需要將一個二維或者三維的數據展平為一維的數據。而我們知道在計算機中圖形是一個三維的數據,因為需要存儲一些類似 RGB 各個通道之間關聯性的空間信息,所以三維形狀中可能隱藏有值得提取的本質模式。而全連接展平會導致形狀的忽視。因此需要利用卷積神經網絡來保持形狀的不變。

典型應用場景:圖像識別、語音識別等。

典型結構如下圖所示:

為什么要使用卷積神經網絡?

對於計算機視覺來說,每一個圖像是由一個個像素點構成,每個像素點有三個通道,分別代表RGB三種顏色(不計算透明度),我們以手寫識別的數據集MNIST舉例,每個圖像的是一個長寬均為28,channel為1的單色圖像,如果使用全連接的網絡結構,即,網絡中的神經與相鄰層上的每個神經元均連接,那就意味着我們的網絡有\(28×28 =784\)個神經元(RGB3色的話還要3),hidden層如果使用了15個神經元,需要的參數個數(w和b)就有:\(28×28×15×10 + 15 + 10=117625\)個,這個數量級到現在為止也是一個很恐怖的數量級,一次反向傳播計算量都是巨大的,這還只是一個單色的28像素大小的圖片,如果我們使用更大的像素,計算量可想而知。

上面說到傳統的網絡需要大量的參數,但是這些參數是否重復了呢,例如,我們識別一個人,只要看到他的眼睛,鼻子,嘴,還有臉基本上就知道這個人是誰了,只是用這些局部的特征就能做做判斷了,並不需要所有的特征。 另外一點就是我們上面說的可以有效提取了輸入圖像的平移不變特征,就好像我們看到了這是個眼睛,這個眼鏡在左邊還是在右邊他都是眼睛,這就是平移不變性。 我們通過卷積的計算操作來提取圖像局部的特征,每一層都會計算出一些局部特征,這些局部特征再匯總到下一層,這樣一層一層的傳遞下去,特征由小變大,最后在通過這些局部的特征對圖片進行處理,這樣大大提高了計算效率,也提高了准確度。

神經網絡中的各層

  • 輸入層(Input Layer):主要是對原始的圖像數據進行預處理
  • 卷積層(Convolution Layers):可以看作是輸入樣本和卷積核的內積運算。從前一層提取移位不變特征。即當輸入數據是圖像的時候,卷積層會以3維數據的形式接收輸入數據,並同樣以3維數據的形式輸出至下一層。因此,在CNN中,可以(有可能)正確理解圖像等具有形狀的數據。注:卷積層的輸入和輸出數據通常又被稱為特征圖(Feature Map)。卷積層的輸入數據稱為輸入特征圖(Input Feature Map),輸出數據稱為輸出特征圖(Output Feature Map)。
  • 池化層(Pooling Layers):作用是減小卷積層產生的特征圖尺寸。將前一層的多個單元的激活組合為一個單元。池化是縮小高、長方向上的空間的運算,通常減小一半。
  • 全連接層(Fully Connected Layers):收集空間擴散信息
  • 輸出層(Output Layer):選擇類

卷積操作

卷積運算相當於圖像處理中的“濾波器運算”。卷積運算會對輸入數據應用濾波器(Filter)

假設,一個初始的圖像大小為 \(J*K\), 同時有 \(L\) 個管道,如下圖所示:

我們應用一個 \(M*N\) 大小的濾波器。圖中綠色的格子就是一個 3*3 的濾波器。卷積運算的整個操作為:將各個位置上濾波器的元素和輸入的對應元素相乘,然后再求和(整個操作也被稱為乘積累加運算),然后,將這個結果保存到輸出的對應位置。

將計算過程公式化,公式如下:

\[Z_{j,k} = b + \sum_{m=0}^{M-1}\sum_{n=0}^{N-1}K_{m,n}V_{j+m,k+n} \]

其中 \(K_{m,n}\) 表示濾波器的 \(m,n\) 位置的數據,\(V_{j+m,k+n}\) 表示圖像的 \(j+m,k+n\) 位置的數據,\(b\) 是偏置項。

新生成的卷積層大小,用公式表示為:

\[(J+1-M) * (K+1-N) \]

將計算過程圖像化,如下圖所示:

其中,濾波器的參數就是權重(Weights),同時還有有個偏置項(Bias),這個偏置項會被加到濾波器最后的輸出上。

權重共享

我們可以根據上圖看到,對一層中的每一個 \(M×N\) 塊輸入應用相同的權值,計算卷積層中的下一個隱藏單元,這就是個權重共享(Weight Sharing)的概念。

填充

填充(Padding):在進行卷積操作之前,有時候要向周圍填入固定的數據,比如用數值 0 進行填充,也叫零填充(Zero Padding)。填充的寬度用字母 P 表示。

應用填充之后,卷積層的輸出大小將會和卷積之前的層一樣,如圖所示,其中粉色的 6 * 7 的格子是原始的圖像尺寸,我們在周圍加上一圈數值為 0 的格子,用白色表示:

以步幅 1 進行卷積,如下圖所示:

最終我們得到了一個大小為 6 * 7 的藍色的卷積層,和原始圖像的尺寸相同,如下圖所示:

這主要也是為了避免卷積過程中過一個典型的問題:如果每次進行卷積運算都會縮小空間,那么在某個時刻輸出大小就有可能變成 1,導致無法再應用卷積運算。為了避免這個情況的出現,就需要使用填充。

新的卷積層尺寸,公式為:

\[(J + 2P + 1 - M) * (K + 2P + 1 - N) \]

步幅

步幅(Stribe):應用濾波器間隔的位置稱為步幅。在上面的例子中,采用的步幅為 1。

我們可以將步幅改為 2,那么卷積過程就會變成下圖所示的:

可以看到應用濾波器的窗口的間隔變成了 2 個元素,如下圖所示:

最終,新的一層的尺寸變為一個 3 * 4 的層。用公式表示為(設步長為\(s\)):

\[(1+\frac{(J-M)}{s}) * (1+\frac{(K-N)}{s}) \]

至此,結合卷積、填充、步幅相關的公式,我們可以得出最完整的卷積層尺寸計算公式:

\[(1+\frac{(J + 2P -M)}{s}) * (1+\frac{(K + 2P -N)}{s}) \]

3 維數據的卷積運算

在一開始,雖然我們假設了一個初始的圖像大小為 \(J*K\), 同時有 \(L\) 個管道。但是我們討論的卷積操作一直只在單層上進行的,下面我們就討論一下在 3 維情況下的卷積運算。

當在通道方向上有多個特征圖的時候,會按照通道進行輸入數據濾波器的卷積運算,並將結果相加,從而得到輸出。計算步驟如下圖所示:

至此,我們的公式可以總結為:

\[Z^i_{j,k} = b^i + \sum_l\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}K^i_{m,n}V^l_{j+m,k+n} \]

其中 \(K^i_{m,n}\) 表示第 \(i\) 個濾波器的 \(m,n\) 位置的數據,\(V^l_{j+m,k+n}\) 表示圖像第 \(l\) 層通道的 \(j+m,k+n\) 位置的數據,\(b^i\) 是第 \(i\) 個偏置項。根據之前的權重共享的概念,我們可以知道,每一層通道是共用一個濾波器的相同權重的,同樣也共用相同的偏置。

激活函數

因為卷積層的操作也是一個線性的操作,所以我們會像處理深度前饋網絡一樣利用激活函數來引入非線性。

池化

池化(Pooling)是縮小高、長方向上的空間的運算。通過減少卷積層之間的連接,降低運算復雜程度。和卷積操作類似的地方是,池化操作同樣要引入一個 Filter,通過不斷地滑動,使濾器覆蓋的區域進行合並,只保留一個值。

最典型的池化過程叫做最大池化(Max Pooling),如下圖所示,這是一個單層的池化:

圖中所示的步驟是,我們划定一個 \(2 * 2\) 的窗口,找出這個窗口中最大的那個值,作為下一層的數據,這個窗口以步幅 2 進行移動。一般來說,池化的窗口大小會和步幅設定成相同的值。

除了 Max Pooling 之外,還有 Average Pooling/Mean Pooling,即平均池化,是計算目標區域的平均值。在圖像識別領域中,主要使用 Max Pooling。

重疊池化

重疊池化(Overlapping Pooling)是 AlexNet 中的一個概念。Overlapping Pooling 的概念和 No-Overlapping Pooling的概念相對。

  • Overlapping Pooling:當步幅小於窗口寬度,會使池化窗口產生重疊區域。可以提升預測精度、緩解過擬合。
  • No-Overlapping Pooling:當步幅等於窗口寬度,池化窗口沒有重疊區域。如上圖的展示。又叫一般池化,General Pooling

池化層的特征

  • 沒有要學習的參數:池化層和卷積層不同,沒有要學習的參數。池化層只是從目標區域中取最大值或平均值。
  • 通道數不發生變化:經過池化運算,輸入數據和輸出數據的通道數不會發生變化。即計算是按照通道獨立進行的。
  • 對微小的位置變化具有魯棒性(健壯):輸入數據發生微小偏差時,池化仍會返回相同的結果。

全連接層

Full Connnected Layer,一般是作為最后的輸出層使用,目的就是輸出我們想要的結果,無論是分類,還是回歸。

在全連接層之間的所有層中,特征都是使用矩陣表示的,所以再傳入全連接層之前還需要對特征進行壓扁,將他這些特征變成一維的向量,如果要進行分類的話,就是用sofmax作為輸出,如果要是回歸的話就直接使用linear即可。

神經網絡的訓練方法

誤差更新方法

和深度前饋網絡相同,神經網絡中的參數訓練也采用誤差的反向傳播算法。我們知道在全連接層中的更新方法是根據鏈式法則從上到下的逐層更新參數。那么卷積層和池化層是如何更新的呢?就是將卷積層和池化層轉變成全連接層的形式來看。
如下圖所示是池化層的誤差傳播。我們假設紅色邊框區域中的最大值為 \(n_{12}\),那么誤差就只在 \(n_{12}\)\(n_{21}\) 之間傳播,即只有這一條連接權重 \(w_{21}\) 為 1, 其他紅色區域的連接權重為 0。對於藍色區域也是同樣的處理辦法。

如下圖所示是卷積層的誤差傳播。卷積層要看成只與特定單元相連接的全連接層。卷積核就相當於權重。卷積核的調整和深度前饋網絡相同,也是從上層的連接權重開始逐層調整。

參數的設定方法

在構建和訓練卷積神經網絡的過程中,有非常多的參數需要設置,其中一部分是神經網絡本身的參數,另一部分只與訓練有關的參數。
與神經網絡相關的主要參數如下:

  • 卷積層的卷積核大小、卷積核個數
  • 激活函數的種類
  • 池化方法的種類
  • 網絡的層結構(卷積層的個數和全連接層的個數)
  • Dropout 的概率
  • 有無預處理
  • 有無歸一化

與訓練有關的參數如下:

  • Mini-Batch的大小
  • 學習率
  • 迭代次數
  • 有無預訓練

參數的影響力對比

通常在構建和訓練神經網絡的時候,我們需要選擇這些參數的最優組合,但是因為參數的數量過多,往往我們只能根據經驗來選擇。但是實際上有的參數對神經網絡影響比較小,有的影響比較大。我們可以優先調整影響比較大的參數,再調整影響比較小的參數。假設我們有一個如下圖所示的神經網絡,應用 CIFAR-10 數據集進行訓練,觀察各個參數的影響。

參數的比較結果如下圖所示:

從圖中可以看出,卷積層的卷積核個數、激活函數的種類和輸入圖像的預處理對模型存在較大的影響,應該首先確定這些重要參數。其他的參數影響相對較小,只需要在設定好重要參數之后進行微調就可以。但是要注意,對於不同的數據集和問題,重要的參數情況可能有變化,需要根據實際問題和研究,這個例子只是參考作用。

典型的CNN

LeNet

原理

LeNet 是在 1998 年提出的進行手寫數字識別的網絡。具有連續的卷積層和池化層(准確的講是只抽取元素的子采樣層),最后經全連接層輸出結果。基本結構如下:

  • C1:第一個卷積層的 5×5 的窗口從原始的 32×32 的圖像中提取出 28×28 的特征數組。有6個卷積核,提取6種局部特征。
  • S2:然后再進行子抽樣,將其大小減半為14×14,降低網絡訓練參數及模型的過擬合程度。
  • C3:第二個卷積層使用另一個 5×5 的窗口提取一個 10×10 的特征數組,使用了16個卷積核。
  • S4:第二個子采樣層將其簡化為 5×5。
  • C5、F6:這些激活然后通過兩個全連接層進入對應數字 ‘0’ 到 ‘9’ 的10個輸出單元。

和現在的 CNN 相比,LeNet 有幾個不同點

  • 激活函數:LeNet中使用 Sigmoid 函數,而現在的 CNN 中主要使用 ReLU 函數。
  • 池化/子采樣:LeNet 中使用子采樣縮小中間數據的大小,而現在的 CNN 中 Max Pooling 是主流。

Pytorch 實現

import torch.nn as nn
class LeNet5(nn.Module):

     def __init__(self):
        super(LeNet5, self).__init__()
        # 1 input image channel, 6 output channels, 5x5 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120) # 這里論文上寫的是conv,官方教程用了線性層
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = LeNet5()
print(net)

AlexNet

原理

AlexNet 是一個引發深度學習熱潮的導火索。基本結構如下:

AlexNet 疊有多個卷積層和池化層,最后經由全連接層輸出:

  • 5 個卷積層和 3 個全連接層
  • 利用重疊步幅的最大池化層
  • 1000 個類的 Softmax
  • 兩個只在特定層進行交互的 GPU。上圖中就是利用了兩個 GPU,在第二個 Max Pooling 層進行交互,一直到最后進行輸出兩個 GPU 一直是在並行運算。

AlexNet 雖然在結構上和 LeNet 沒有很大的不同,但是也有一些細節上的差異

  • AlexNet 的激活函數使用的是 ReLU
  • 使用進行局部正規化的 LRN(Local Response Normalization)層
  • 使用 Dropout

Pytorch 實現

因為 AlexNet 是 Pytorch 官方實現的,所以直接從 torchvision 包中調用

    import torchvision
    model = torchvision.models.alexnet(pretrained=False) #我們不下載預訓練權重
    print(model)

各個神經網絡對比


這是一個准確率和計算量之間的對比。橫軸是數據量,縱軸是准確率。可以在一定程度上指導我們進行網絡的選擇!

面試常見問題

  • 神經網絡中的權重共享是什么?
  • \(1 * 1\) 卷積的作用
  • 卷積層和池化層有什么區別
  • LeNet-5 結構
  • AlexNet 結構


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM