PyTorch中CNN的Forward方法 | PyTorch系列(十七)

點擊上方AI算法與圖像處理”,選擇加"星標"或“置頂”

重磅乾貨,第一時間送達

文 |AI_study

原標題:CNN Forward Method - PyTorch Deep Learning Implementation

神經網絡程序設計系列(綜述)

到目前爲止,在這個系列中,我們已經準備好了我們的數據,現在構建我們的模型。

我們通過擴展nn.Module PyTorch基類來創建網絡,然後在類構造函數中將網絡層定義爲類屬性。現在,我們需要實現網絡的 forward() 方法,最後,我們將準備訓練我們的模型。

  • 準備數據

  • 構建模型

    • 創建一個擴展nn.Module基類的神經網絡類。

    • 在類構造函數中,將網絡層定義爲類屬性。

    • 使用網絡的層屬性以及nn.functional API操作來定義網絡的前向傳遞

  • 訓練模型

  • 分析模型的結果

回顧一下網絡

目前,我們知道forward()方法接受張量作爲輸入,然後返回張量作爲輸出。現在,返回的張量與傳遞的張量相同。

但是,在構建實現之後,返回的張量將是網絡的輸出。

這意味着forward 方法實現將使用我們在構造函數內部定義的所有層。這樣,前向方法顯式定義了網絡的轉換。

forward()方法是實際的網絡轉換。forward 方法是將輸入張量映射到預測輸出張量的映射。讓我們看看這是如何完成的。

回想一下,在網絡的構造函數中,我們可以看到定義了五層。

class Network(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)
        
        self.fc1 = nn.Linear(in_features=12 * 4 * 4, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60, out_features=10)
    
    def forward(self, t):
        # implement the forward pass      
        return t

我們有兩個卷積層和三個Linear 層。如果算上輸入層,這將爲我們提供一個總共六層的網絡。

實現forward 方法

讓我們對此進行代碼編寫。我們將從輸入層開始。

輸入層#1

任何神經網絡的輸入層都由輸入數據確定。例如,如果我們的輸入張量包含三個元素,那麼我們的網絡將在其輸入層中包含三個節點。

因此,我們可以將輸入層視爲 identity transformation 。從數學上講,這是下面的函數

f(x)=x.

我們給任何x 作爲輸入,我們得到相同的結果 x  作爲輸出。無論我們使用的是具有三個元素的張量,還是表示具有三個通道的圖像的張量,此邏輯都是相同的。輸入是數據輸出!

這非常瑣碎,這就是使用神經網絡API時通常看不到輸入層的原因。輸入層隱式存在。

絕對不是必需的,但是爲了完整起見,我們將在forward方法中顯示標識操作。

# (1) input layer
t = t

隱藏的卷積層:第2層和第3層

就執行轉換而言,兩個隱藏的卷積層都將非常相似。在深度學習基礎知識系列中,我們在有關層的文章中解釋說,不是輸入或輸出層的所有層都稱爲隱藏層,這就是爲什麼我們將這些卷積層稱爲隱藏層。

深度學習基礎:https://deeplizard.com/learn/video/gZmobeGL0Yg

層的解釋:https://deeplizard.com/learn/video/FK77zZxaBoI

爲了執行卷積運算,我們將張量傳遞給第一卷積層self.conv1的forward 方法。我們已經瞭解了所有PyTorch神經網絡模塊如何具有forward() 方法,並且當我們調用nn.Module的forward() 方法時,有一種特殊的調用方法。

當要調用nn.Module實例的forward() 方法時,我們將調用實際實例,而不是直接調用forward() 方法。

代替執行此self.conv1.forward(tensor),我們執行此self.conv1(tensor)。確保您看到了本系列的上一篇文章,以瞭解有關此主題的所有詳細信息。

讓我們繼續並添加實現兩個卷積層所需的所有調用。

# (2) hidden conv layer
t = self.conv1(t)
t = F.relu(t)
t = F.max_pool2d(t, kernel_size=2, stride=2)


# (3) hidden conv layer
t = self.conv2(t)
t = F.relu(t)
t = F.max_pool2d(t, kernel_size=2, stride=2)

正如我們在這裏看到的那樣,當我們在卷積層中移動時,輸入張量將發生變換。第一卷積層具有卷積運算,然後是 relu 激活運算,其輸出隨後傳遞到kernel_size = 2和stride = 2的最大池化中。

然後將第一個卷積層的輸出張量 t 傳遞到下一個卷積層,除了我們調用self.conv2()而不是self.conv1()以外,其他卷積層均相同。

這些層中的每一個都由權重(數據)和收集操作(代碼)組成。權重封裝在nn.Conv2d() 類實例中。relu() 和max_pool2d() 調用只是純運算。這些都不具有權重,這就是爲什麼我們直接從nn.functional API調用它們的原因。

有時,我們可能會看到稱爲池化層的池化操作。有時我們甚至可能聽到稱爲激活層的激活操作。

但是,使層與操作區分開的原因在於層具有權重。由於池操作和激活功能沒有權重,因此我們將它們稱爲操作,並將其視爲已添加到層操作集合中。

例如,我們說網絡中的第二層是一個卷積層,其中包含權重的集合,並執行三個操作,即卷積操作,relu激活操作和最大池化操作。

請注意,此處的規則和術語並不嚴格。這只是描述網絡的一種方式。還有其他表達這些想法的方法。我們需要知道的主要事情是哪些操作是使用權重定義的,哪些操作不使用任何權重。

從歷史上看,使用權重定義的操作就是我們所說的層。後來,其他操作被添加到mix中,例如激活功能和池化操作,這引起了術語上的一些混亂。

從數學上來說,整個網絡只是函數的組合,函數的組合就是函數本身。因此,網絡只是一種函數。諸如層,激活函數和權重之類的所有術語僅用於幫助描述不同的部分。

不要讓這些術語混淆整個網絡只是函數的組合這一事實,而我們現在正在做的就是在forward()方法中定義這種組合。

隱藏的Linear層:第4層和第5層

在將輸入傳遞到第一個隱藏的Linear 層之前,我們必須reshape() 或展平我們的張量。每當我們將卷積層的輸出作爲Linear層的輸入傳遞時,都是這種情況。

由於第四層是第一個Linear層,因此我們將reshape操作作爲第四層的一部分。

# (4) hidden linear layer
t = t.reshape(-1, 12 * 4 * 4)
t = self.fc1(t)
t = F.relu(t)


# (5) hidden linear layer
t = self.fc2(t)
t = F.relu(t)

我們在CNN權重的文章中看到,reshape 操作中的數字 12 由來自前一個卷積層的輸出通道數確定。

然而,4 * 4仍然是一個懸而未決的問題。讓我們現在揭示答案。4 * 4實際上是12個輸出通道中每個通道的高度和寬度。

我們從1 x 28 x 28輸入張量開始。這樣就給出了一個單一的彩色通道,即28 x 28的圖像,並且在我們的張量到達第一 Linear 層時,尺寸已經改變。

通過卷積和池化操作,將高度和寬度尺寸從28 x 28減小到4 x 4。

卷積和池化操作是對高度和寬度尺寸的化簡操作。我們將在下一篇文章中看到這是如何工作的,並看到用於計算這些減少量的公式。現在,讓我們完成實現此forward() 方法。

張量重構後,我們將展平的張量傳遞給 Linear 層,並將此結果傳遞給relu() 激活函數。

輸出層#6

我們網絡的第六層也是最後一層是 Linear 層,我們稱爲輸出層。當我們將張量傳遞到輸出層時,結果將是預測張量。由於我們的數據具有十個預測類別,因此我們知道我們的輸出張量將具有十個元素。

# (6) output layer
t = self.out(t)
#t = F.softmax(t, dim=1)

十個組件中的每個組件內的值將對應於我們每個預測類的預測值。

在網絡內部,我們通常使用relu() 作爲我們的非線性激活函數,但是對於輸出層,每當我們嘗試預測一個類別時,我們就使用softmax()。softmax函數針對每個預測類返回正概率,並且概率之和爲1。

但是,在本例中,我們不會使用softmax(),因爲我們將使用的損失函數F.cross_entropy()在其輸入上隱式執行softmax()操作,因此我們只返回 最後的線性變換。

這意味着我們的網絡將使用softmax操作進行訓練,但是當訓練過程完成後將網絡用於推理時,無需計算額外的操作。

結論

很好!我們做到了。這就是我們在PyTorch中實現神經網絡forward方法的方式。

PyTorch在__ call __()方法中運行的額外代碼就是我們從不直接調用forward()方法的原因。如果我們這樣做,額外的PyTorch代碼將不會被執行。因此,每當我們想要調用forward()方法時,我們都會調用對象實例。這既適用於層,也適用於網絡,因爲它們都是PyTorch神經網絡模塊。

現在可以實現網絡的forward()方法了。

文章中內容都是經過仔細研究的,本人水平有限,翻譯無法做到完美,但是真的是費了很大功夫,希望小夥伴能動動你性感的小手,分享朋友圈或點個“在看”,支持一下我 ^_^

英文原文鏈接是:

https://deeplizard.com/learn/video/MasG7tZj-hw

加羣交流

歡迎小夥伴加羣交流,目前已有交流羣的方向包括:AI學習交流羣,目標檢測,秋招互助,資料下載等等;加羣可掃描並回復感興趣方向即可(註明:地區+學校/企業+研究方向+暱稱)

 謝謝你看到這裏! ????

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章