日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人工智能 > 卷积神经网络 >内容正文

卷积神经网络

深度学习入门之PyTorch学习笔记:卷积神经网络

發布時間:2024/10/8 卷积神经网络 121 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深度学习入门之PyTorch学习笔记:卷积神经网络 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

深度學習入門之PyTorch學習筆記

  • 緒論
  • 1 深度學習介紹
  • 2 深度學習框架
  • 3 多層全連接網絡
  • 4 卷積神經網絡
    • 4.1 主要任務及起源
    • 4.2 卷積神經網絡的原理和結構
      • 4.2.1 卷積層
        • 1.概述
        • 2.局部連接
        • 3.空間排列
        • 4.零填充的使用
        • 5.步長限制
        • 6.參數共享
        • 7.總結
      • 4.2.2 池化層
      • 4.2.3 全連接層
      • 4.2.4 卷積神經網絡的基本形式
        • 1.小濾波器的有效性
        • 2.網絡的尺寸
    • 4.3 PyTorch卷積模塊
      • 4.3.1 卷積層
      • 4.3.2 池化層
      • 4.3.3 提取層結構
      • 4.3.4 如何提取參數及自定義初始化
    • 4.4 卷積神經網絡案例分析
      • 4.4.1 LeNet
      • 4.4.2 AlexNet
      • 4.4.3 VGGNet
      • 4.4.4 GoogLeNet
      • 4.4.5 ResNet
    • 4.5 實現MNIST手寫數字分類
    • 4.6 圖像增強的方法
    • 4.7 實現cifar10分類
  • 參考資料

緒論

  • 深度學習如今已經稱為科技領域最炙手可熱的技術,幫助你入門深度學習。
  • 本文從機器學習與深度學習的基礎理論入手,從零開始學習PyTorch以及如何使用PyTorch搭建模型。
  • 學習機器學習中的線性回歸、Logistic回歸、深度學習的優化方法、多層全連接神經網絡、卷積神經網絡、循環神經網絡、以及生成對抗網絡,最后通過實戰了解深度學習前沿的研究成果。
  • 將理論與代碼結合,幫助更好的入門機器學習。

1 深度學習介紹

https://hulin.blog.csdn.net/article/details/107733777

2 深度學習框架

https://hulin.blog.csdn.net/article/details/107746239

3 多層全連接網絡

https://hulin.blog.csdn.net/article/details/107757088

4 卷積神經網絡

  • 圖像分類問題是計算機視覺中的一個核心問題,雖然問題描述很簡單,卻有著很廣泛的使用價值,很多獨立的計算機視覺任務如目標檢測、分割等,都可以簡化為圖像分類問題。
  • 卷積神經網絡于1998年由Yann Lecun提出。2012年,Alex憑借卷積神經網絡贏得了ImageNet挑戰賽,震驚了世界,如今卷積神經網絡已經成為計算機視覺領域最具影響力的一部分。
  • 本文將從計算機視覺的任務起源開始引入卷積神經網絡,介紹卷積神經網絡的原理和基礎,然后介紹PyTorch的卷積模塊,接著介紹現在應用最廣泛的幾個卷積神經網絡模型,然后用卷積神經網絡再次實現MNIST手寫數字分類,并介紹圖像增強技巧,并在新的數據集cifar10上進行測試,最后介紹卷積神經網絡的逆過程:反卷積神經網絡。

4.1 主要任務及起源

  • 人類獲取外界信息,主要依靠視覺、聽覺、觸覺、嗅覺、味覺等感覺器官,其中80%的信息來自于視覺,而且視覺獲取的信息也是最豐富、最復雜的。人的生理構造決定了能夠看清楚并理解身邊的場景,而要讓計算機看懂這個世界卻是一件非常困難的事情,即使在很多人看來,現在的計算機技術已經足夠先進了,但是要達到看懂并自主分析各種復雜信息的程度,還有很長的一段路要走,這也是計算機視覺這門學科要解決的事情。
  • 計算機視覺的核心任務之一是圖像識別,人類對于圖片的識別相當容易,然而機器卻面臨了很多問題,如視角變換,光照條件,背景干擾,物體變形,正是由于這些問題的干擾,使得計算機在圖像識別時候的準確率較低。
  • 如何實現一個算法來分類圖片呢?人們不可能制定一個規則決定哪張圖片屬于哪一類,所以要通過學習算法讓機器知道如何分類,這就是機器學習的核心。機器學習算法是依賴于數據集的,所以也稱為數據驅動算法。
  • 在卷積神經網絡流行起來之前,圖像處理使用的都是一些傳統的方法,比如提取圖像中的邊緣、紋理、線條、邊界等特征,依據這些特征再進行下一步處理,這樣的處理不僅效率特別低,準確率也不高。隨著計算機視覺的快速發展,在某些圖像集上機器的識別準確率已經超過了人類,這一切都要歸公于卷積神經網絡。

4.2 卷積神經網絡的原理和結構

  • 以下三個觀點使得卷積神經網絡真正起作用,分別對應著卷積神經網絡中的三種思想。
    (1)局部性
    對于一張圖片而言,需要檢測圖片中的特征來決定圖片的類別,通常情況下這些特征都不是由整張圖片決定的,而是由一些局部的區域決定的。

    (2)相同性
    對于不同的圖片,如果它們具有相同的特征,這些特征會出現在圖片不同的位置,也就是說可以用同樣的檢測模式去檢測不同圖片的相同特征,只不過這些特征處于圖片中不同的位置,但是特征檢測所做的操作幾乎一樣。兩張圖片的鳥喙處于不同的位置,但是可以用相同的檢測模式去檢測。

    (3)不變性
    對于一張大圖片,如果進行下采樣,那么圖片的性質基本保持不變。
  • 卷積神經網絡和全連接神經網絡是相似的,也是由一些神經元構成。這些神經元中,有需要學習的參數,通過網絡輸入最后輸出結果,通過損失函數來優化網絡中的參數。
  • 卷積神經網絡與全連接神經網絡的不同之處在于網絡的層結構不同。全連接神經網絡由一系列隱藏層構成,每個隱藏層由若干個神經元構成,其中每一個神經元都和前一層的所有神經元相連,但是每一層中的神經元是相互獨立的。如下圖所示。
  • 全連接神經網絡在處理圖片上存在諸多問題。比如在MNIST數據集上,圖片大小是2828,那么第一個隱藏層的單個神經元的權重數目就是2828=784個,這似乎還不是特別大,但這只是一張小圖片,且是灰度圖。對于一張較大的圖片而言,比如2002003,就會導致權重數目是2002003=120000,如果設置幾個隱藏層中神經元數目,就會導致參數增加特別快。其實這樣的圖片在現實中并不算是大圖片,所以全連接神經網絡對于處理圖片并不是一個好的選擇。
  • 卷積神經網絡的處理過程不同于一般的全連接神經網絡,卷積神經網絡是一個3D容量的神經元,也就是說神經元是以三個維度來排列的:寬度、高度、深度。比如輸入的圖片是32323,那么這張圖片的寬度就是32,高度也是32,深度是3.
  • 卷積神經網絡中的主要層結構有三個:卷積層、池化層和全連接層,通過堆疊這些層結構形成了一個完整的卷積神經網絡結構。卷積神經網絡將原始圖片轉化為最后的類別得分,其中一些層包含參數,一些層沒有包含參數,比如卷積層和全連接層擁有參數,而激活層和池化層不含有參數,這些參數通過梯度下降法來更新,最后使得模型盡可能正確的識別出圖片類別。

4.2.1 卷積層

卷積層是卷積神經網絡的核心,大多數計算都是在卷積層中進行的。

1.概述

  • 首先介紹卷積神經網絡的參數。這些參數是由一些可學習的濾波器集合構成的,每個濾波器在空間上(寬度和高度)都比較小,但是深度和輸入數據的深度保持一致。例如,卷積神經網絡的第一層卷積一個典型的濾波器的尺寸可以是553(寬和高都是5),或者333,寬度和高度可以任意定義,但深度必須是3,因為深度要和輸入一致,而輸入的圖片是3通道的。在前向傳播的時候,讓每個濾波器都在輸入數據的寬度和高度上滑動(卷積),然后計算整個濾波器和輸入數據任意一處的內積。
  • 當濾波器沿著輸入數據的寬度和高度滑動時,會生成一個二維的激活圖,激活圖上的每個空間位置表示了原圖片對于該濾波器的反應。直觀來看,網絡會讓濾波器學習到當它看到某些類型的視覺特征的時候就激活,具體的視覺特征可以是邊界、顏色、輪廓,甚至可以是網絡更高層上的蜂窩狀或者車輪狀圖案。
  • 在每個卷積層上,會有一整個集合的濾波器,比如20個,這樣就會形成20張二維的、不同的激活圖,將這些激活圖在深度方向上層疊起來,就形成了卷積層的輸出。
  • 如果用大腦和生物神經元做比喻,那么輸出的3D數據中的每個數據都可以看成是神經元的一個輸出,而該神經元只是觀察輸入數據中的一種特征,并且和空間上左右兩邊的所有神經元共享參數,因為這些輸出都是使用同一個濾波器得到的結果。

2.局部連接

  • 在處理圖像這樣高維度輸入的時候,讓每個神經元與它那一層中的所有神經元,進行全連接是不現實的。相反,讓每個神經元只與輸入數據的一個局部區域連接是可行的。這是因為圖片特征的局部性,所以只需要通過局部就能提取出相應的特征。
  • 與神經元連接的空間大小叫做神經元的感受野(Receptive Field),它的大小是一個人為設置的超參數,這其實就是濾波器的寬和高。在深度方向上,其大小總是和輸入的深度相等。最后對待空間維度(寬和高)和深度維度是不同的,連接在空間上是局部的,但是在深度上總是和輸入的數據深度保持一致。
  • 下圖展示了感受野在空間和深度上的大小,左邊表示輸入數據,中間是感受野,右邊每個小圓點表示一個神經元。
  • 例如,輸入的數據尺寸是32323,如果感受野(濾波器尺寸)是55,卷積層中每個神經元會有輸入數據中553區域的權重,一共553=75個權重。感受野的深度大小必須是3,和輸入數據保持一致。比如輸入數據尺寸是161620,感受野是33,卷積層中每個神經元和輸入數據體之間就有3320=180個連接,這里的深度必須是20,和輸入數據保持一致。

3.空間排列

  • 前面介紹了每個神經元只需要與輸入數據的局部區域相連接,但是沒有介紹卷積層中神經元的數量和它們的排列方式、輸出深度、滑動步長,以及邊界填充控制著卷積層的空間排布。
  • 首先,卷積層的輸出深度是一個超參數,它與使用的濾波器數量一致,每種濾波器所做的就是在輸入數據中尋找一種特征。比如說,輸入一張原始圖片,卷積層輸出的深度是20,這說明有20個濾波器對數據進行處理,每種濾波器尋找一種特征進行激活。
  • 其次,在滑動濾波器的時候,必須指定步長。比如步長為1,說明濾波器每次移動一個像素點。當步長為2時,濾波器會滑動兩個像素點。滑動的操作會使輸出的數據在空間上變得更小。
  • 最后是邊界填充,可以將輸入數據用0在邊界進行填充,這里將0填充的尺寸作為一個超參數,由一個好處就是,可以控制輸出數據在空間上的尺寸,最常用來保證輸入和輸出在空間上尺寸一致。
  • 輸出的尺寸到底是多少呢?其實可以用一個公式來計算,就是W?F+2Ps+1{{W-F+2P}\over s}+1sW?F+2P?+1,其中W表示輸入的數據大小,F表示卷積層中神經元的感受野尺寸,S表示步長,P表示邊界填充0的數量。比如輸入是7×77\times 77×7,濾波器是3×33\times 33×3,步長是1,填充的數量是0,那么根據公式,就能得到5,即輸出的空間大小是5×55\times 55×5,如果步長是2,那么輸出的空間大小就是3×33\times 33×3

4.零填充的使用

  • 零填充能夠保證輸入的數據和輸出的數據具有相同的空間尺寸。

5.步長限制

  • 步長的選擇是有所限制的。當輸入尺寸W=10的時候,如果不使用零填充,即P=0,濾波器尺寸F=3,這樣步長S=2就行不通,因為10?3+02+1=4.5{{10-3+0}\over 2}+1=4.5210?3+0?+1=4.5,結果不是一個整數,這就說明神經元不能整齊對稱地滑過輸入數據體,這樣的超參數設置是無效的,可以使用零填充讓設置變得合理。
  • 在卷積神經網絡的結構設計中,需要合理的設置網絡的尺寸,使得所有維度都能正常工作。

6.參數共享

  • 在卷積層使用參數共享,可以有效減少參數的個數。因為圖像特征具有相同性,說明相同的濾波器能夠檢測出不同位置的相同特征。比如一個卷積層的輸出是202032,那么其中神經元的個數就是202032=12800,如果窗口大小是33,而輸入的數據深度是10,那么每個神經元就有3310=900個參數,這樣合起來就有12800900=11520000個參數,單單一層卷積就有這么多參數,這樣的運算速度顯然特別慢。
  • 一個濾波器能檢測出一個空間位置(x1,y1)處的特征,那么也能夠有效檢測出(x2,y2)位置的特征,所以就可以用相同的濾波器來檢測相同的特征。基于這個假設,就能夠有效減少參數的個數。比如一共有32個濾波器,這使得輸出體的厚度是32,每個濾波器的參數為3310=900,總共的參數就有32*900=28800個,極大減少了參數的個數。
  • 由參數共享知道輸出體數據在深度切片上所有的權重都使用同一個權重向量,那么卷積層在向前傳播的過程中,每個深度切片都可以看成是神經元的權重對輸入數據體做卷積,就是把這些3D的權重集合稱為濾波器,或者卷積核。
  • 參數共享之所以能夠有效,是因為一個特征在不同位置的表現是相同的,比如一個濾波器檢測到了水平邊界這個特征,那么這個特征具有平移不變性,所以在其他位置也能夠檢測出來。但是有時候這樣的假設是沒有意義的,特別是當卷積神經網絡的輸入圖像,呈現的是一些明確的中心結構的時候,希望在圖片的不同位置學習到不同的特征。例如,人臉識別中,人臉一般位于圖片的中心,希望不同的特征能在不同的位置被學習到,比如眼睛特征或者頭發特征,正是這些特征在不同的地方,才能夠對人臉進行識別。

7.總結

總結以下卷積層的性質。

(1)輸入數據的尺寸是W1×H1×D1W_1\times H_1\times D_1W1?×H1?×D1?

(2)4個超參數,濾波器數量K,濾波器空間尺寸F,滑動步長S,零填充的數量P。

(3)輸出數據的尺寸為W2×H2×D2W_2\times H_2\times D_2W2?×H2?×D2?,其中W2=W1?F+2PS+1,H2=H1?F+2PS+1,D2=KW_2={{W_1-F+2P}\over{S}}+1,H_2={{H_1-F+2P}\over{S}}+1,D_2=KW2?=SW1??F+2P?+1,H2?=SH1??F+2P?+1,D2?=K

(4)由于參數共享,每個濾波器包含的權重數目是F×F×D1F\times F\times D_1F×F×D1?,卷積層一共有F×F×D1×KF\times F\times D_1\times KF×F×D1?×K個權重和K個偏置。

(5)在輸出體數據中,第d個深度切片(空間尺寸是W2×H2W_2\times H_2W2?×H2?),用d個濾波器和輸入數據進行有效卷積運算的結果,再加上第d個偏置。

對于卷積神經網絡的超參數,常見的設置是F=3,S=1,P=1,同時這些超參數也有一些約定俗稱的慣例和經驗。

4.2.2 池化層

  • 卷積層是卷積神經網絡的核心,通常會在卷積層之間周期性插入一個池化層,其作用是逐漸降低數據體的空間尺寸,這樣就能減少網絡中的參數數量,減少計算資源耗費,同時也能夠有效的控制過擬合。

  • 池化層和卷積層一樣也有一個空間窗口,通常采用的是取這些窗口中的最大值作為輸出結果,然后不斷滑動窗口,對輸入數據體每一個深度切片單獨處理,減少它的空間尺寸。

  • 從上圖能夠看出池化層能夠有效降低數據體空間的大小,下圖形象地說明了窗口大小是2,滑動步長是2的最大值池化是如何計算的:每次都從2*2的窗口中選擇最大的數值,同時每次滑動2個步長進入新的窗口。

  • 池化層之所以有效,是因為之前介紹的圖片特征具有不變性,也就是通過下采樣不會丟失圖片擁有的特征,由于這種特性,可以將圖片縮小再進行卷積處理,這樣能大大降低卷積運算的時間。

  • 最常用的池化層形式是尺寸為2×22\times 22×2的窗口,滑動步長為2,對圖像進行下采樣,將其中75%的激活信息都丟掉,選擇其中最大的保存下來,這其實是因為希望能夠更加激活里面數值大的特征,去除一些噪聲信息。

池化層有一些與卷積層類似的性質。
(1)數據輸入體的尺寸是W1×H1×D1W_1\times H_1\times D_1W1?×H1?×D1?
(2)有兩個需要設置的超參數,空間大小F和滑動步長S
(3)輸出體的尺寸是W2×H2×D2,W2=W1?FS+1,H2=H1?FS+1,D2=D1W_2\times H_2\times D_2,W_2={{W_1-F}\over{S}}+1,H_2={{H_1-F}\over{S}}+1,D_2=D_1W2?×H2?×D2?,W2?=SW1??F?+1,H2?=SH1??F?+1,D2?=D1?
(4)對輸入進行固定函數的計算,沒有參數引入
(5)池化層中很少引入零填充

在實際中,有兩種方式:一種是F=3,S=2,這種池化有重疊;另外一種更常用的是F=2,S=2.一般來說應該謹慎使用比較大的池化窗口,以免對網絡有破壞性。

除了最大值池化之外,還有一些其他的池化函數,比如平均池化,或者L2范數池化。在實際中證明,在卷積層之間引入最大池化的效果是最好的,而平均池化一般放在卷積神經網絡的最后一層。

4.2.3 全連接層

  • 全連接層和一般的神經網絡的結構是一樣的,每一個神經元與前一層所有的神經元全部連接,而卷積神經網絡只和輸入數據中的一個局部區域連接,并且輸出的神經元每個深度切片共享參數。
  • 一般經過了一系列的卷積層和池化層之后,提取出圖片的特征圖,比如說特征圖的大小是3×3×5123\times 3\times 5123×3×512,將特征圖中的所有神經元變成全連接層的樣子,直觀上也就是將一個3D的立方體重新排列,變成一個全連接層,里面有3×3×5123\times 3\times 5123×3×512=4608個神經元,在經過幾個隱藏層,最后輸出結果。
  • 在這個過程中,為了防止過擬合會引入Dropout。最近的研究表明,在進入全連接層之前,使用全局平均池化能夠有效的降低過擬合。

4.2.4 卷積神經網絡的基本形式

  • 卷積神經網絡中通常由卷積層、池化層、全連接層這三種層結構所構成,引入激活函數增加模型的非線性,所以卷積神經網絡最常見的形式就是將一些卷積層和ReLU層放在一起,有可能在ReLU層前面加上批標準化層,隨后是池化層,再不斷重復,直到圖像在空間上被縮小到一個足夠小的尺寸,然后將特征展開,連接幾層全連接層,最后輸出結果,比如分類評分。
  • 一種卷積神經網絡的基本結構。

1.小濾波器的有效性

  • 一般而言,幾個小濾波器卷積層的組合比一個大濾波器卷積層要好,比如層層堆疊了3個3*3的卷積層,中間含有非線性激活層。在這種排列下面,第一個卷積層中每個神經元對輸入數據的感受野是3*3,第二層卷積層對第一層卷積層的感受野也是3*3,這樣對于輸入數據的感受野就是5*5,同樣,第三層卷積層上對第二層卷積層的感受野是3*3,這樣第三層卷積層對于第一層輸入數據的感受野就是7*7.
  • 多個卷積層首先與非線性激活層交替的結構,比單一卷積層的結構更能提取出深層的特征。選擇小濾波器的卷積組合能夠對輸入數據表達出更有力的特征,同時使用參數也更少。唯一的不足是反向傳播更新參數的時候,中間的卷積層可能會占用更多的內存。

2.網絡的尺寸

對于卷積神經網絡的尺寸設計,沒有嚴格的數學證明,這是根據經驗指定出來的規則。

(1)輸入層
一般而言,輸入層的大小應該能夠被2整除很多次,常用的數字包括32,64,96,224.

(2)卷積層
卷積層應該盡可能使用小尺寸的濾波器,比如3*3或者5*5,滑動步長取1。還有一點就是需要對輸入數據體進行零填充,這樣可以有效的保證卷積層不會改變輸入數據體的空間尺寸。如果必須要使用更大的濾波器尺寸,比如7*7,通常用在第一個面對原始圖像的卷積層上。

(3)池化層
池化層負責對輸入的數據空間維度進行下采樣,常用的設置使用2*2的感受野做最大值池化,滑動步長取2.另外一個不常用的設置是使用3*3的感受野,步長設置為2.一般而言,池化層的感受野大小很小超過2,因為這樣會使池化過程過于激烈,造成信息的丟失,這通常會造成算法的性能變差。

(4)零填充
零填充的使用可以讓卷積層的輸入和輸出在空間上的維度保持一致。除此之外,如果不使用零填充,那么數據體的尺寸就會略微減少,在不斷進行卷積的過程中,圖像邊緣信息會過快的損失掉。

4.3 PyTorch卷積模塊

PyTorch作為一個深度學習庫,卷積神經網絡是其中最為基礎的一個模塊,卷積神經網絡中所有的層結構都可以通過nn這個包來調用。

4.3.1 卷積層

nn.Conv2d()就是PyTorch中的卷積模塊,常用的參數有5個,分別是in_channels, out_channels, kernel_size, stride, padding,還有參數dilation, groups, bias等

  • in_channels對應的是輸入數據體的深度;
  • out_channels表示輸出數據體的深度;
  • kernel_size表示濾波器(卷積核)的大小,可以使用一個數字來表示高和寬相同的卷積核,比如kernel_size=3, 也可以使用不同的數字來表示高和寬不同的卷積層,比如kernel_size=(3,2);
  • stride表示滑動的步長;
  • padding=0表示四周不進行零填充,而padding=1表示四周進行1個像素點的零填充;
  • bias是一個布爾值,默認bias=True表示使用偏置;
  • groups表示輸出數據體深度上和輸入數據體深度上的聯系,默認groups=1,也就是所有的輸入和輸出是相關聯的;如果groups=2,表示輸入的深度被分割成兩份,輸出的深度也被分割成兩份,它們之間分別對應起來,所以要求輸入和輸出都要被groups整除;
  • dilation表示卷積對于輸入數據體的空間間隔,默認dilation=1

4.3.2 池化層

nn.MaxPool2d()表示網絡中最大值池化,其中參數有kernel_size, strid, padding, dilation, return_indices, ceil_mode。

  • kernel_size, stride, padding, dilation與卷積層參數的含義相同。
  • return_indices表示是否返回最大值所處的下標,默認return_indices=False;一般不會設置這些參數。
  • nn.AvgPool2d()表示均值池化,里面的參數和nn.MaxPool2d()類似,但多一個參數count_include_pad,這個參數表示計算均值的時候是否包含零填充,默認為True。
  • 一般使用較多的就是nn.MaxPool2d()和nn.AvgPool2d(), 另外PyTorch還提供了一些別的池化層,如nn.LPPool2d(),nn.AdaptiveMaxPool2d()等不常用的池化層。
  • 一個簡單的多層卷積神經網絡
import torch.nn as nnclass SimpleCNN(nn.Module):def __init__(self):super(SimpleCNN, self).__init__()layer1 = nn.Sequential()layer1.add_module('conv1', nn.Conv2d(3, 32, 3, 1, padding=1))layer1.add_module('relu1', nn.ReLU(True))layer1.add_module('pool1', nn.MaxPool2d(2, 2))self.layer1 = layer1layer2 = nn.Sequential()layer2.add_module('conv2', nn.Conv2d(32, 64, 3, 1, padding=1))layer2.add_module('relu2', nn.ReLU(True))layer2.add_module('pool2', nn.MaxPool2d(2, 2))self.layer2 = layer2layer3 = nn.Sequential()layer3.add_module('conv3', nn.Conv2d(64, 128, 3, 1, padding=1))layer3.add_module('relu3', nn.ReLU(True))layer3.add_module('pool3', nn.MaxPool2d(2, 2))self.layer3 = layer3layer4 = nn.Sequential()layer4.add_module('fc1', nn.Linear(2048, 512))layer4.add_module('fc_relu1', nn.ReLU(True))layer4.add_module('fc2', nn.Linear(512, 64))layer4.add_module('fc_relu2', nn.ReLU(True))layer4.add_module('fc3', nn.Linear(64, 10))self.layer4 = layer4def forward(self, x):conv1 = self.layer1(x)conv2 = self.layer2(conv1)conv3 = self.layer3(conv2)fc_input = conv3.view(conv3.size(0), -1)fc_out = self.layer4(fc_input)return fc_outmodel = SimpleCNN() print(model)
  • 在上面的定義中,將卷積層、激活層、池化層,組合在一起構成了一個層結構,定義了3個這樣的層結構,最后定義了全連接層,輸出10.
  • 通過print(model)顯示網絡中定義了哪些層結構, 這些層結構
SimpleCNN((layer1): Sequential((conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(relu1): ReLU(inplace)(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(layer2): Sequential((conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(relu2): ReLU(inplace)(pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(layer3): Sequential((conv3): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(relu3): ReLU(inplace)(pool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(layer4): Sequential((fc1): Linear(in_features=2048, out_features=512, bias=True)(fc_relu1): ReLU(inplace)(fc2): Linear(in_features=512, out_features=64, bias=True)(fc_relu2): ReLU(inplace)(fc3): Linear(in_features=64, out_features=10, bias=True)) )

4.3.3 提取層結構

對于一個給定的模型,如果不想要模型中所有的層結構,只希望能夠提取網絡中的某一層或者幾層,應該如何實現?

  • 首先看nn.Module的幾個重要屬性。
  • 第一個是children(), 這個會返回下一級模塊的迭代器,比如上面這個模型,只會返回self.layer1, self.layer2, self.layer3,self.layer4上的迭代器,不會返回它們內部的東西。
  • 第二個是modules()會返回模型中所有模塊的迭代器,即它能夠訪問到最內層,比如self.layer1.conv1這個模塊。
  • 還有一個與前兩個相對應的是named_children()屬性以及named_modules(), 這兩個不僅會返回模塊的迭代器,還會返回網絡層的名字。

下面來提取網絡中需要的層。如果希望能提取出前面兩層,可以通過下面的方法實現。

model = SimpleCNN() new_model = nn.Sequential(*list(model.children())[:2]) print(new_model) Sequential((0): Sequential((conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(relu1): ReLU(inplace)(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(1): Sequential((conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(relu2): ReLU(inplace)(pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)) )
  • 如果希望提取出模型中所有的卷積層,可以通過下面的方法實現.使用isinstance()可以判斷這個模塊是不是所需要的類型實例,這樣就提取出了所有的卷積模塊。
conv_model = nn.Sequential() for layer in model.named_modules():if isinstance(layer[1], nn.Conv2d):conv_model.add_module(layer[0], layer[1])

4.3.4 如何提取參數及自定義初始化

  • 有時候提取出的層結構并不夠,還需要對里面的參數進行初始化,那么如何提取網絡中的參數并初始化呢?
  • 首先nn.Module里面有兩個特別重要的關于參數的屬性,分別是named_parameters()和parameters(), named_parameters()給出網絡層的名字和參數的迭代器, parameters()會給出一個網絡的全部參數的迭代器。
model = SimpleCNN() for param in model.named_parameters():print(param[0]) layer1.conv1.weight layer1.conv1.bias layer2.conv2.weight layer2.conv2.bias layer3.conv3.weight layer3.conv3.bias layer4.fc1.weight layer4.fc1.bias layer4.fc2.weight layer4.fc2.bias layer4.fc3.weight layer4.fc3.bias
  • 如何對權重做初始化呢?因為權重是一個Variable,所以只需要取出其中的data屬性,對其進行所需要的處理即可。
model = SimpleCNN() for m in model.modules():if isinstance(m, nn.Conv2d):init.normal_(m.weight.data)init.xavier_normal_(m.weight.data)init.kaiming_normal_(m.weight.data)m.bias.data.fill_(0)elif isinstance(m, nn.Linear):m.weight.data.normal_()

4.4 卷積神經網絡案例分析

4.4.1 LeNet

  • LeNet是整個卷積神經網絡的開山之作,1998年由LeCun提出,它的結構特別簡單。
  • 整個網絡結構特別清晰,一共7層,其中2層卷積和2層池化層交替出現,最后輸出3層全連接層得到整體的結果。
  • 網絡的層數很淺,也沒有添加激活層
import torch.nn as nnclass LeNet(nn.Module):def __init__(self):super(LeNet, self).__init__()layer1 = nn.Sequential()layer1.add_module('conv1', nn.Conv2d(1, 6, 3, padding=1))layer1.add_module('pool1', nn.MaxPool2d(2, 2))self.layer1 = layer1layer2 = nn.Sequential()layer2.add_module('conv2', nn.Conv2d(6, 16, 5))layer2.add_module('pool2', nn.MaxPool2d(2, 2))self.layer2 = layer2layer3 = nn.Sequential()layer3.add_module('fc1', nn.Linear(400, 120))layer3.add_module('fc2', nn.Linear(120, 84))layer3.add_module('fc3', nn.Linear(84, 10))self.layer3 = layer3def forward(self, x):x = self.layer1(x)x = self.layer2(x)x = x.view(x.size(0), -1)x = self.layer3(x)return x

4.4.2 AlexNet

  • AlexNet在2012年的ImageNet競賽上大放異彩,以領先第二名10%的準確率奪得冠軍,并成功展示了深度學習的威力。
  • 當時GPU計算能力不強,而AlexNet比較復雜,所以使用兩個GPU并行計算,現在完全可以用一個GPU代替。
  • AlexNet相對于LeNet層數更深,同時第一次引入了激活層ReLU,在全連接層引入了Dropout層防止過擬合。
import torch.nn as nnclass AlexNet(nn.Module):def __init__(self, num_classes):super(AlexNet, self).__init__()self.features = nn.Sequential(nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),nn.Conv2d(64, 192, kernel_size=5, padding=2),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),nn.Conv2d(192, 384, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(384, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2),)self.classifier = nn.Sequential(nn.Dropout(),nn.Linear(256 * 6 * 6, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, num_classes))def forward(self, x):x = self.features(x)x = x.view(x.size(0), 256 * 6 * 6)x = self.classifier(x)return x

4.4.3 VGGNet

  • VGGNet是ImageNet競賽2014年的亞軍,總結起來就是它使用了更小的濾波器,同時使用了更深的結構,AlexNet只有8層網絡,而VGGNet有16-19層網絡;AlexNet使用了11*11的大濾波器,而VGGNet只使用了3*3的卷積濾波器和2*2的大池化層。
  • VGGNet之所以使用很多小的濾波器,是因為層疊很多小的濾波器的感受野和一個大濾波器的感受野是相同的,還能減少參數,同時有更深的網絡結構。
  • VGG只是對網絡層進行不斷的堆疊,并沒有進行太多的創新,而增加深度確實可以一定程度改善模型效果。
import torch.nn as nnclass VGGNet(nn.Module):def __init__(self, num_classes):super(VGGNet, self).__init__()self.features = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.ReLU(True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(64, 128, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(128, 128, kernel_size=3, padding=1),nn.ReLU(True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(128, 256, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(256, 512, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(True),nn.MaxPool2d(kernel_size=2, stride=2),)self.classifier = nn.Sequential(nn.Linear(512 * 7 * 7, 4096),nn.ReLU(True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(True),nn.Dropout(),nn.Linear(4096, num_classes),)def forward(self, x):x = self.features(x)x = x.view(x.size(0), -1)x = self.classifier(x)return x

4.4.4 GoogLeNet

  • GoogLeNet也叫InceptionNet,是ImageNet競賽2014年的冠軍,采用了一種很有效的Inception模塊,沒有全連接層。
  • GoogLeNet采取了比VGGNet更深的網絡結構,一共有22層,但是它的參數卻比AlexNet少了12倍,同時有很高的計算效率。
  • Inception模塊設計了一個全局的網絡拓撲結構,然后將這些模塊堆疊在一起形成一個抽象層網絡結構。具體就是運用幾個并行的濾波器對輸入進行卷積和池化,這些濾波器有不同的感受野,最后將輸出的結果按深度拼接在一起形成輸出層。
  • 首先定義一個最基礎的卷積模塊,然后根據這個模塊定義了1*1,3*3,5*5的模塊和一個池化層,最后使用torch.cat()將它們按深度拼接起來,得到輸出結果。
import torch.nn as nn import torch from pandas.util._decorators import Fclass BasicConv2d(nn.Module):def __init__(self, in_channels, out_channels, **kwargs):super(BasicConv2d, self).__init__()self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)self.bn = nn.BatchNorm2d(out_channels, eps=0.001)def forward(self, x):x = self.conv(x)x = self.bn(x)return F.relu(x, inplace=True)class Inception(nn.Module):def __init__(self, in_channels, pool_features):super(Inception, self).__init__()self.branch1x1 = BasicConv2d(in_channels, 64, kernel_size=1)self.branch5x5_1 = BasicConv2d(in_channels, 48, kernel_size=1)self.branch5x5_2 = BasicConv2d(48, 64, kernel_size=5, padding=2)self.branch3x3db1_1 = BasicConv2d(in_channels, 64, kernel_size=1)self.branch3x3db1_2 = BasicConv2d(64, 96, kernel_size=3, padding=1)self.branch3x3db1_3 = BasicConv2d(96, 96, kernel_size=3, padding=1)self.branch_pool = BasicConv2d(in_channels, pool_features, kernel_size=1)def forward(self, x):branch1x1 = self.branch1x1(x)branch5x5 = self.branch5x5_1(x)branch5x5 = self.branch5x5_2(branch5x5)branch3x3db1 = self.branch3x3db1_1(x)branch3x3db1 = self.branch3x3db1_2(branch3x3db1)branch3x3db1 = self.branch3x3db1_3(branch3x3db1)branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)branch_pool = self.branch_pool(branch_pool)outputs = (branch1x1, branch5x5, branch3x3db1, branch_pool)return torch.cat(outputs, 1)

4.4.5 ResNet

  • ResNet是2015年ImageNet競賽的冠軍,由微軟研究院提出,通過殘差模塊能夠成功訓練高達152層的神經網絡。
  • ResNet最初的設計靈感來自于這個問題:在不斷加深神經網絡的時候,會出現一個Degradation,即準確率會先上升然后達到飽和,再持續增加深度則會導致模型準確率下降。
  • 這并不是過擬合的問題,因為不僅在驗證集上誤差增加,訓練集本身誤差也在增加。假設一個比較淺的網絡達到了飽和的準確率,那么在后面加上恒等映射層,誤差不會增加,也就是說更深的模型不會使模型效果下降。
  • 這里提到的使用恒等映射直接將前一層輸出傳到后面的思想,就是ResNet的靈感來源。假設某個神經網絡的輸入是x,期望輸出是H(x)H(x)H(x), 如果直接把輸入xxx傳到輸出作為初始結果,那么此時需要學習的目標就是F(x)=H(x)?xF(x)=H(x)-xF(x)=H(x)?x, 也就是殘差模塊。
  • ResNet的殘差學習單元相當于將學習目標改變了,不再是學習一個完整的輸出H(x)H(x)H(x), 而是學習輸出和輸入差別H(x)?xH(x)-xH(x)?x, 即殘差。
import torch.nn as nndef conv3x3(in_planes, out_planes, stride=1):"""3x3 convolution with padding:param in_planes::param out_planes::param stride::return:"""return nn.Conv2d(in_planes,out_planes,kernel_size=3,stride=stride,padding=1,bias=False)class BasicBlock(nn.Module):def __init__(self, inplanes, planes, stride=1, downsample=None):super(BasicBlock, self).__init__()self.conv1 = conv3x3(inplanes, planes, stride)self.bn1 = nn.BatchNorm2d(planes)self.relu = nn.ReLU(inplace=True)self.conv2 = conv3x3(planes, planes)self.bn2 = nn.BatchNorm2d(planes)self.downsample = downsampleself.stride = stridedef forward(self, x):residual = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample is not None:residual = self.downsample(x)out += residualout = self.relu(out)return out

4.5 實現MNIST手寫數字分類

  • 這個簡單的卷積神經網絡里面有4層卷積,2層最大池化,卷積之后使用批標準化加快收斂速度,使用ReLU激活函數增加非線性,最后使用全連接層輸出分類得分。
  • 結論:通過增加網絡深度和復雜化網絡結構,提高網絡的準確率是可行的。
from torch import nnclass CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.layer1 = nn.Sequential(nn.Conv2d(1, 16, kernel_size=3),nn.BatchNorm2d(16),nn.ReLU(inplace=True))self.layer2 = nn.Sequential(nn.Conv2d(16, 32, kernel_size=3),nn.BatchNorm2d(32),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2))self.layer3 = nn.Sequential(nn.Conv2d(32, 64, kernel_size=3),nn.BatchNorm2d(64),nn.ReLU(inplace=True))self.layer4 = nn.Sequential(nn.Conv2d(64, 128, kernel_size=3),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2))self.fc = nn.Sequential(nn.Linear(128 * 4 * 4, 1024),nn.ReLU(inplace=True),nn.Linear(1024, 128),nn.ReLU(inplace=True),nn.Linear(128, 10))def forward(self, x):x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = x.view(x.size(0), -1)x = self.fc(x)return x

4.6 圖像增強的方法

  • 一直以來,圖像識別這一計算機視覺的核心問題都面臨著很多挑戰,同一個物體在不同情況下都會得出不同的結論。對于一張照片,人類看到的是一些物體,對于計算機而言,看到的是一些像素點。
  • 如果拍攝照片的照相機位置發生了改變,那么拍攝的圖片對于我們而言,變化很小,但是對于計算機而言,圖片的像素變化是很大的。
  • 拍攝時的光照條件也是很重要的一個影響因素:光照太弱,照片里的物體和背景融為一體,它們的像素點就會很接近,計算機就無法正確識別出物體。
  • 物體本身的變形也會對計算機識別造成障礙,比如一只貓是趴著的,計算機能夠識別它,但是如果貓換一個姿勢,變成躺著的狀態,計算機就無法識別了。
  • 物體本身會隱藏在一些遮蔽物,這樣物體只呈現局部的信息,計算也難以識別。
  • torchvision.transforms包括所有圖像增強的方法。
  • scale 對圖片的尺寸進行縮小和放大
  • CenterCrop 對圖像正中心進行給定大小的剪裁
  • RandomCrop 對圖片進行給定大小的隨機剪裁
  • RandomHorizaontalFlip 對圖片進行概率為0.5的隨機水平反轉
  • RandomSizedCrop 首先對圖片進行隨機尺寸的裁剪,然后對裁剪的圖片進行一個隨機比例的縮放,最后將圖片變成給定大小,在InceptionNet中較流行。
  • pad 對圖片進行邊界零填充

4.7 實現cifar10分類

  • cifar10數據集有60000張圖片,每張圖片的大小都是32*32的三通道的彩色圖,一共是10種類別,每種類別有6000張圖片。
  • 首先進行圖像增強。只對訓練集進行圖片增強,提高泛化能力,對于測試集,僅對其中心化,不做其他的圖像增強。
from torchvision import transformstrain_transform = transforms.Compose([transforms.Scale(40),transforms.RandomHorizontalFlip(),transforms.RandomCrop(32),transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) ]) test_transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) ])
  • 定義ResNet的基本模塊。
def conv3x3(in_channels, out_channels, stride=1):return nn.Conv2d(in_channels,out_channels,kernel_size=3,stride=stride,padding=1,bias=False)# Residual Block class ResidualBlock(nn.Module):def __init__(self, in_channels, out_channels, stride=1, downsample=None):super(ResidualBlock, self).__init__()self.conv1 = conv3x3(in_channels, out_channels, stride)self.bn1 = nn.BatchNorm2d(out_channels)self.relu = nn.ReLU(inplace=True)self.conv2 = conv3x3(out_channels, out_channels)self.bn2 = nn.BatchNorm2d(out_channels)self.downsample = downsampledef forward(self, x):residual = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample:residual = self.downsample(x)out += residualout = self.relu(out)return out
  • 先定義殘差模塊,再將殘差模塊拼接起來,注意其中維度變化。
class ResNet(nn.Module):def __init__(self, block, layers, num_classes=10):super(ResNet, self).__init__()self.in_channels = 16self.conv = conv3x3(3, 16)self.bn = nn.BatchNorm2d(16)self.relu = nn.ReLU(inplace=True)self.layer1 = self.make_layer(block, 16, layers[0])self.layer2 = self.make_layer(block, 32, layers[0], 2)self.layer3 = self.make_layer(block, 64, layers[1], 2)self.avg_pool = nn.AvgPool2d(8)self.fc = nn.Linear(64, num_classes)def make_layer(self, block, out_channels, blocks, stride=1):downsample = Noneif (stride != 1) or (self.in_channels != out_channels):downsample = nn.Sequential(conv3x3(self.in_channels, out_channels, stride=stride),nn.BatchNorm2d(out_channels))layers = []layers.append(block(self.in_channels, out_channels, stride, downsample))self.in_channels = out_channelsfor i in range(1, blocks):layers.append(block(out_channels, out_channels))return nn.Sequential(*layers)def forward(self, x):out = self.conv(x)out = self.bn(out)out = self.relu(out)out = self.layer1(out)out = self.layer2(out)out = self.layer3(out)out = self.avg_pool(out)out = out.view(out.siza(0), -1)out = self.fc(out)return out

參考資料

  • 廖星宇《深度學習入門之PyTorch》電子工業出版社
  • 總結

    以上是生活随笔為你收集整理的深度学习入门之PyTorch学习笔记:卷积神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。