全連接網絡存的弊端
以MINST數字識別為例,創建一個4層全連接網絡層,輸入為28x28,中間三個隱藏層的節點數都是 256,輸出節點數是10。通過summary()函數打印出模型每一層的參數量,計算總的參數量超過34萬個網絡參數,單個權值保存為 float 類型的變量,網絡參數至少需要約 1.34MB 內存。訓練過程中占用內存超過2GB(未使用GPU),訓練時長超過5分鐘(個人筆記本,ADM R7系列)。
隨著圖片的尺寸增加,網絡層增加,訓練占用的內存、時長會暴增。
下面是幾種如何避免全連接網絡的參數量過大的缺陷的方法。
局部相關性
以2D圖片數據為例,在進入全連接層之前,需要將矩陣數據打平成 1D 向量,然后每個像素點與每個輸出節點兩兩相連。網絡層的每個輸出節點都與所有的輸入節點相連接,用于提取所有輸入節點的特征信息,這種稠密的連接方式是全連接層參數量大、計算代價高的根本原因。全連接層也稱為稠密連接層(Dense Layer)
可以分析輸入節點對輸出節點的重要性分布,僅考慮較重要的一部分輸入節點,而拋棄重要性較低的部分節點,這樣就可以簡化模型了。然而找出每個中間節點的重要性分布是件非常困難的事情。利用先驗知識,可以使用位置或距離作為重要性分布衡量標準的數據。
以2D 圖片數據為例,簡單地認為與當前像素歐式距離(Euclidean Distance)小于和等于k2\frac{k}{\sqrt{2}}2?k?,的像素點重要性較高,歐式距離大于k2\frac{k}{\sqrt{2}}2?k?到像素點重要性較低,那么這樣就很輕松地簡化了每個像素點的重要性分布問題。
如下圖,這個高寬為kkk的窗口稱為感受野(Receptive Field),它表征了每個像素對于中心像素的重要性分布情況,網格內的像素才會被考慮,網格外的像素對于中心像素會被簡單地忽略。
這種基于距離的重要性分布假設特性稱為局部相關性,它只關注和自己距離較近的部分節點,而忽略距離較遠的節點。
權值共享
每個輸出節點僅與感受野區域內k×kk \times kk×k輸入節點相連接,輸出層節點數為∥J∥\parallel J \parallel∥J∥,當前層的參數量為$k \times k \times \parallel J \parallel ,相對于全連接層的,相對于全連接層的,相對于全連接層的\parallel I \parallel \times\parallel J \parallel,考慮,考慮,考慮k一般取值較小如1、3、5等,一般取值較小如 1、3、5 等,一般取值較小如1、3、5等,k \times k \ll \parallel J \parallel$,因此成功地將參數量減少了很多。
權值共享就是說,給一張輸入圖片,用一個卷積核去掃這張圖,卷積核里面的數就叫權重,這張圖每個位置是被同樣的卷積核掃的,所以權重是一樣的,也就是共享。
通過權值共享的思想,對于每個輸出節點ojo_joj?,均使用相同的權值矩陣𝑾,那么無論輸出節點的數量∥J∥\parallel J \parallel∥J∥是多少,網絡層的參數量總是k×kk \times kk×k。
卷積運算
對于窗口k×kk \times kk×k 內的所有像素,采用權值相乘累加的方式提取特征信息,每個輸出節點提取對應感受野區域的特征信息。這種運算其實是信號處理領域的一種標準運算:離散卷積運算。
離散卷積運算流程:
- 卷積核即是行、列為kkk大小的權值矩陣𝑾,對應到特征圖上大小為kkk的窗口即為感受野,感受野與權值矩陣𝑾相乘累加,得到此位置的輸出值。
- 每次通過移動卷積核,并與圖片對應位置處的感受野像素相乘累加,得到此位置的輸出值。
- 通過權值共享,從左上方逐步向右、向下移動卷積核,提取每個位置上的像素特征,直至最右下方,完成卷積運算。
在2D卷積運算中,卷積核是一個函數g(m,n)g(m,n)g(m,n)(m,n表示長/寬),有時也叫 Filter、Weight 等。
卷積神經網絡
卷積神經網絡通過充分利用局部相關性和權值共享的思想,大大地減少了網絡的參數量,從而提高訓練效率,更容易實現超大規模的深層網絡。
單通道單核卷積
單通道輸入cin=1c_{in}=1cin?=1,如灰度圖片只有灰度值一個通道,單個卷積核cout=1c_{out}=1cout?=1的情況。以輸入XXX為5 × 5的矩陣,卷積核為3 × 3的矩陣為例,與卷積核同大小的感受野(輸入XXX上方的綠色方框)首先移動至輸入XXX最左上方,選中輸入XXX上3 × 3的感受野元素,與卷積核(圖片中間3 × 3方框)對應元素相乘:
∣1?10?1?2?212?2∣⊙∣?1121?130?1?2∣=∣?1?10?1260?24∣\begin{vmatrix}1&-1&0 \\ -1&-2&-2 \\ 1&2&-2 \end{vmatrix} \odot \begin{vmatrix}-1&1&2 \\ 1&-1&3 \\ 0&-1&-2 \end{vmatrix}=\begin{vmatrix}-1&-1&0 \\ -1&2&6 \\ 0&-2&4 \end{vmatrix}?1?11??1?22?0?2?2??⊙??110?1?1?1?23?2??=??1?10??12?2?064??
⊙\odot⊙符號表示哈達馬積,即矩陣的對應元素相乘,它與矩陣相乘符號@是矩陣的二種最為常見的運算形式。
運算后得到3 × 3的矩陣,這 9 個數值全部相加:?1?1+0?1+2+6+0?2+4=7-1-1+0-1+2+6+0-2+4 = 7?1?1+0?1+2+6+0?2+4=7
完成第一個感受野區域的特征提取后,感受野窗口向右移動一個步長單位(Strides,記為𝑠,默認為 1),按照同樣的計算方法,與卷積核對應元素相乘累加,得到輸出 10
按照上述方法,每次感受野向右移動𝑠 = 個步長單位,若超出輸入邊界,則向下移動𝑠 = 個步長單位,并回到行首,直到感受野移動至最右邊、最下方位置,最終輸出得到一個3 × 3的矩陣。
可以觀察到,卷積運算的輸出矩陣大小由卷積核的大小kkk,輸入XXX的高寬?/𝑤,移動步長𝑠,是否填充等因素共同決定。
多通道單核卷積
多通道輸入的卷積層更為常見,比如彩色的圖片包含了 R/G/B 三個通道,每個通道上面的像素值表示 R/G/B 色彩的強度。下面以 3 通道輸入、單個卷積核為例,將單通道輸入的卷積運算方法推廣到多通道的情況。
每個通道上面的感受野窗口同步落在對應通道上面的最左邊、最上方位置,每個通道上感受野區域元素與卷積核對應通道上面的矩陣相乘累加,分別得到三個通道上面的輸出 7、-11、-1 的中間變量,這些中間變量相加得到輸出-5,寫入對應位置。
隨后,感受野窗口同步在XXX的每個通道上向右移動𝑠 = 個步長單位,每個通道上面的感受野與卷積核對應通道上面的矩陣相乘累加,得到中間變量 10、20、20,全部相加得到輸出 50,寫入第一行、第二列元素位置。
以此方式同步移動感受野窗口,直至最右邊、最下方位置,此時全部完成輸入和卷積核的卷積運算,得到3 × 3的輸出矩陣
整個的計算示意圖如下
一般來說,一個卷積核只能完成某種邏輯的特征提取,當需要同時提取多種邏輯特征時,可以通過增加多個卷積核來得到多種特征,提高神經網絡的表達能力。
多通道,多卷積核
多通道輸入、多卷積核是卷積神經網絡中最為常見的形式。
以 3 通道輸入、2 個卷積核的卷積層為例。第一個卷積核與輸入XXX運算得到輸出OOO的第一個通道,第二個卷積核與輸入XXX運算得到輸出OOO的第二個通道,輸出的兩個通道拼接在一起形成了最終輸出OOO。每個卷積核的大小kkk、步長sss、填充設定等都是統一設置,這樣才能保證輸出的每個通道大小一致,從而滿足拼接的條件。
步長
對于信息密度較大的輸入,如物體數量很多的圖片,為了盡可能的少漏掉有用信息,在網絡設計的時候希望能夠較密集地布置感受野窗口;對于信息密度較小的輸入,比如全是海洋的圖片,可以適量的減少感受野窗口的數量。感受野密度的控制手段一般是通過移動步長(Strides)實現的。
步長是指感受野窗口每次移動的長度單位,對于 2D 輸入來說,分為沿xxx(向右)方向和yyy(向下)方向的移動長度。
通過設定步長𝑠,可以有效地控制信息密度的提取。當步長設計的較小時,感受野以較小幅度移動窗口,有利于提取到更多的特征信息,輸出張量的尺寸也更大;當步長設計的較大時,感受野以較大幅度移動窗口,有利于減少計算代價,過濾冗余信息,輸出張量的尺寸也更小。
填充
經過卷積運算后的輸出OOO的寬高一般會小于輸入XXX的寬高。在網絡模型設計時,有時希望輸出OOO與輸入XXX的寬高相同,從而方便網絡參數的設計,殘差鏈接等。
為了讓輸出OOO與輸入XXX的寬高相同,一般通過在原輸入XXX的高和寬維度上面進行填充(Padding)若干無效元素操作,得到增大的輸入X′X^\primeX′。通過精心設計填充單元的數量,在X′X^\primeX′上面進行卷積運算得到輸出OOO的高寬可以和原輸入XXX相等,甚至更大。
在 TensorFlow 中,在𝑠 = 1時(s表示步長),如果希望輸出OOO和輸入XXX高、寬相等,只需要簡單地設置參數 padding=”SAME" 即可使 TensorFlow 自動計算 padding 數量,非常方便。
空洞卷積
普通的卷積層為了減少網絡的參數量,卷積核的設計通常選擇較小的 × 和3 × 3感受野大小。小卷積核使得網絡提取特征時的感受野區域有限,但是增大感受野的區域又會增加網絡的參數量和計算代價,因此需要權衡設計。
空洞卷積(Dilated/Atrous Convolution)的提出較好地解決這個問題,空洞卷積在普通卷積的感受野上增加一個 Dilation Rate 參數用于控制感受野區域的采樣步長。當感受野的采樣步長 Dilation Rate 為 1 時,每個感受野采樣點之間的距離為1,此時的空洞卷積退化為普通的卷積;當 Dilation Rate 為 2 時,感受野每 2 個單元采樣一個點每個采樣格子之間的距離為 2……
池化層
在卷積層中,可以通過調節步長參數𝑠實現特征圖的高寬成倍縮小,從而降低了網絡的參數量。除了通過設置步長,還有一種專門的網絡層可以實現尺寸縮減功能,那就是池化層。
池化層同樣基于局部相關性的思想,通過從局部相關的一組元素中進行采樣或信息聚合,從而得到新的元素值。最大池化層(Max Pooling)從局部相關元素集中選取最大的一個元素值,平均池化層(Average Pooling)從局部相關元素集中計算平均值并返回。
以5×55 \times 55×5輸入XXX的最大池化為例,考慮感受野窗口大小為k=2k=2k=2,步長s=1s=1s=1的情況
綠色虛線方框代表第一個感受野的位置,感受野元素集合為{1,?1,?1,?2}\begin{Bmatrix} 1,-1,-1,-2 \end {Bmatrix}{1,?1,?1,?2?}
在最大池化采樣的方法下,通過 x′=max({1,?1,?1,?2})=1x^\prime = max(\begin{Bmatrix} 1,-1,-1,-2 \end {Bmatrix}) = 1x′=max({1,?1,?1,?2?})=1
計算出當前位置的輸出值為1,并寫入對應的位置
同樣的方法,循環往復,移動感受野窗口直到最下方、最右邊,獲得最大池化層的輸出,長寬為 × ,略小于輸入XXX的
由于池化層沒有需要學習的參數,計算簡單,并且可以有效減低特征圖的尺寸,非常適合圖片這種類型的數據,在計算機視覺相關任務中得到了廣泛的應用。
通過精心設計池化層感受野的高寬kkk和步長sss參數,可以實現各種降維運算。
BatchNorm層
卷積神經網絡的出現,網絡參數量大大減低,使得幾十層的深層網絡成為可能。然而,在殘差網絡出現之前,網絡的加深使得網絡訓練變得非常不穩定,會出現出現網絡長時間不更新甚至不收斂的現象,同時網絡對超參數比較敏感,超參數的微量擾動也會導致網絡的訓練軌跡完全改變。
BN (BatchNorm)層的提出,使得網絡的超參數的設定更加自由,比如更大的學習率、更隨意的網絡初始化等,同時網絡的收斂速度更快,性能也更好。
在 TensorFlow 中,通過 layers.BatchNormalization()類可以非常方便地實現 BN 層:
# 創建 BN 層
layer=layers.BatchNormalization()
與全連接層、卷積層不同,BN 層的訓練階段和測試階段的行為不同,需要通過設置training 標志位來區分訓練模式還是測試模式。
以 LeNet-5 的網絡模型為例,在卷積層后添加 BN 層:
# 網絡容器
network = Sequential([ layers.Conv2D(6,kernel_size=3,strides=1),# 插入 BN 層layers.BatchNormalization(),layers.MaxPooling2D(pool_size=2,strides=2),layers.ReLU(),layers.Conv2D(16,kernel_size=3,strides=1),# 插入 BN 層layers.BatchNormalization(),layers.MaxPooling2D(pool_size=2,strides=2),layers.ReLU(),layers.Flatten(),layers.Dense(120, activation='relu'),# 此處也可以插入 BN 層layers.Dense(84, activation='relu'),# 此處也可以插入 BN 層layers.Dense(10)
])
在訓練階段,需要設置網絡的參數 training=True 以區分 BN 層是訓練還是測試模型
with tf.GradientTape() as tape:x = tf.expand_dims(x,axis=3)out = network(x, training=True)
在測試階段,需要設置 training=False,避免 BN 層采用錯誤的行為
for x,y in db_test: # 遍歷測試集x = tf.expand_dims(x,axis=3)out = network(x, training=False)
深度殘差網絡
ResNet原理
ResNet 通過在卷積層的輸入和輸出之間添加 Skip Connection 實現層數回退機制,如下圖所示,輸入xxx通過兩個卷積層,得到特征變換后的輸出F(x)\mathcal{F}(x)F(x),與輸xxx進行對應元
素的相加運算,得到最終輸出H(x)\mathcal{H}(x)H(x):H(x)=x+F(x)\mathcal{H}(x) = x + \mathcal{F}(x)H(x)=x+F(x)
H(x)\mathcal{H}(x)H(x)叫作殘差模塊(Residual Block,簡稱 ResBlock)。由于被 Skip Connection 包圍的卷積神經網絡需要學習映射F(x)=H(x)?x\mathcal{F}(x)=\mathcal{H}(x) - xF(x)=H(x)?x,故稱為殘差網絡。
為了能夠滿足輸入xxx與卷積層的輸出F(x)\mathcal{F}(x)F(x)能夠相加運算,需要輸入xxx的 shape 與F(x)\mathcal{F}(x)F(x)的shape 完全一致。當出現 shape 不一致時,一般通過在 Skip Connection 上添加額外的卷積運算環節將輸入xxx變換到與F(x)\mathcal{F}(x)F(x)相同的 shape。
DenseNet
DenseNet是Skip Connection 比較流行方案之一,DenseNet 將前面所有層的特征圖信息通過 Skip Connection 與當前層輸出進行聚合,與 ResNet 的對應位置相加方式不同,DenseNet 采用在通道軸𝑐維度進行拼接操作,聚合特征信息。
如下圖所示,輸入X0X_0X0?通過H1H_1H1?卷積層得到輸出X1X_1X1?,X1X_1X1?與X0X_0X0?在通道軸上進行拼接,得到聚合后的特征張量,送入H2H_2H2?卷積層,得到輸出X2X_2X2?,同樣的方法,X2X_2X2?與前面所有層的特征信息 X1X_1X1?與X0X_0X0?進行聚合,再送入下一層。如此循環,直至最后一層的輸出X4X_4X4?和前面所有層的特征信息:{Xi}i=0,1,2,3\begin{Bmatrix}X_i\end{Bmatrix}_{i=0,1,2,3}{Xi??}i=0,1,2,3?進行聚合得到模塊的最終輸出。這樣一種基于 Skip Connection 稠密連接的模塊叫做 Dense Block。