PyTorch實現AlexNet模型及參數詳解

一、卷積池化層原理

該層的順序是:卷積——>ReLU——>池化——>歸一化。
卷積池化層原理:
根據神經元公式:h(x)=f(wx+b)
上式子就是神經元所表示的函數,x表示輸入(圖片矩陣),w表示權重(卷積核參數),b表示偏置,f表示激活函數(ReLU),h(x)表示輸出,輸出後的數據經過池化,然後歸一化。形成完成的卷積池化層。
訓練卷積神經網絡的過程就是不斷調整權重w(卷積核參數)與偏置b的過程,以使其輸出h(x)達到預期值。
總結:
首先對輸入的圖片與卷積核w進行運算,並且每個卷積核參加卷積運算時添加偏置b,然後用激活函數處理卷積運算結果,激活後的圖片尺寸不變,然後再進行池化運算,池化運算可以降低圖片尺寸,然後對池化後的結果進行歸一化處理。

二、全連接層原理

在這裏插入圖片描述
根據神經元公式:h(x)=f(wx+b)
全連接層x表示上一層的輸出值,w表示本層的神經元權重,權重數量等於本全連接層神經元數目,然後再加偏置b,偏置數量等於本層神經元數目。

三、模型參數詳解

原始論文中的圖如下:網絡模型由5個卷積池化層,3個全連接層組成,最後一層全連接層的輸出是1000維softmax的輸入,softmax會產生1000類標籤的分佈。

注:AlexNet論文錯誤點

:原始論文中的圖有點小錯誤,輸入圖片的大小應該是227 x 227,而不是224 x 224,否則後面參數將不能被整除。
在這裏插入圖片描述
爲了更加清晰的說明參數,引入如下更全的網絡結構。
在這裏插入圖片描述
在這裏插入圖片描述

1.卷積池化層1

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第一層輸入數據爲原始的227x227x3的圖像,這個圖像被11x11x3的卷積核進行卷積運算,卷積覈對原始圖像的每次卷積都生成一個新的像素。卷積核沿原始圖像的x軸方向和y軸方向兩個方向移動,移動的步長是4個像素。因此,卷積核在移動的過程中會生成(227-11)/4+1=55個像素,行和列的55x55個像素形成對原始圖像卷積之後的像素層。共有96個卷積核,會生成55x55x96個卷積後的像素層。

(2)分組

96個卷積核分成2組,每組48個卷積核。對應生成2組55x55x48的卷積後的像素層數據。

(3)激活函數層

這些像素層經過relu1單元的處理,生成激活像素層,尺寸仍爲2組55x55x48的像素層數據。

(4)池化層

經過激活函數的像素層經過pool運算的處理,池化運算的尺度爲3x3,運算的步長爲2,則池化後圖像的尺寸爲(55-3)/2+1=27。 即池化後像素的規模爲27x27x96。

(5)歸一化處理

歸一化運算的尺度爲5x5,即選擇5x5的像素區域進行批歸一化處理。第一卷積層運算結束後形成的像素層的規模爲27x27x96。分別對應96個卷積核所運算形成。這96層像素層分爲2組,每組48個像素層,每組在一個獨立的GPU上進行運算。

(6)參數數量

卷積層的參數 = 卷積核大小 x 卷積核的數量 + 偏置數量(即卷積核的數量)。
本層參數數量爲: (11 x 11 x 3 x 96) + 96 = 34848, 注:參數分爲2部分 w 和 b ,“+”前面一部分是 w 的數量, “+”後面那部分是 b 的數量,後面的層也按這個思路來計算。

2.卷積池化層2

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第二層輸入數據爲第一層輸出的27x27x96的像素層,爲便於後續處理,每幅像素層的左右兩邊和上下兩邊都要填充2個像素;27x27x96的像素數據分成272748的兩組像素數據,兩組數據分別再兩個不同的GPU中進行運算。每組像素數據被5548的卷積核進行卷積運算,共有256個5x5x48卷積核。卷積核沿原始圖像的x軸方向和y軸方向兩個方向移動,移動的步長是1個像素。因此,卷積核在移動的過程中會生成(27-5+2x2)/1+1=27個像素。即會生成27x27x256個卷積後的像素層。

(2)分組

這256個卷積核分成兩組。會生成兩組27x27x128個卷積後的像素層。

(3)激活函數層

這些像素層經過relu2單元的處理,生成激活像素層,尺寸仍爲兩組27x27x128的像素層。

(4)池化層

這些像素層經過pool運算(池化運算)的處理,池化運算的尺度爲3*3,運算的步長爲2,則池化後圖像的尺寸爲(57-3)/2+1=13。 即池化後像素的規模爲2組13x13x128的像素層;

(5)歸一化處理

然後經過歸一化處理,歸一化運算的尺度爲5x5,即選擇5x5的像素區域進行批歸一化處理。第二卷積層運算結束後形成的像素層的規模爲2組13x13x128的像素層。分別對應2組128個卷積核所運算形成。

(4)參數數量

卷積層, 使用256個5x5x48卷積核,節點數量:27x27x128x2 = 186624,參數數量:(5x5x48x128+128)x2 = 307456,最後"*2"是因爲網絡層均勻分佈在兩個GPU上,分2組,先計算單個GPU上的參數,再乘以GPU數量2。

3.卷積層3

在這裏插入圖片描述

(1)卷積運算

第三層輸入數據爲第二層輸出的2組13x13x128的像素層;爲便於後續處理,每幅像素層的左右兩邊和上下兩邊都要填充1個像素;2組像素層數據都被送至2個不同的GPU中進行運算。每個GPU中都有192個卷積核,每個卷積核的尺寸是3x3x256。因此,每個GPU中的卷積核都能對2組13x13x128的像素層的所有數據進行卷積運算。

(2)分組

這384個卷積核分成兩組。會生成兩組13x13x192個卷積後的像素層。

(3)激活層

這些像素層經過relu3單元的處理,生成激活像素層,尺寸仍爲2組13x13x192像素層,共13x13x384個像素層。

(4)參數數量

卷積層,使用384個3x3x256卷積核,節點數量:13x13x192x2 = 64896,參數數量:3x3x256x384+384 = 885120。

4.卷積層4

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第四層輸入數據爲第三層輸出的2組13x13x192的像素層;爲便於後續處理,每幅像素層的左右兩邊和上下兩邊都要填充1個像素;2組像素層數據都被送至2個不同的GPU中進行運算。每個GPU中都有192個卷積核,每個卷積核的尺寸是3x3x192。因此,每個GPU中的卷積核能對1組13x13x192的像素層的數據進行卷積運算。卷積覈對每組數據的每次卷積都生成一個新的像素。卷積核沿像素層數據的x軸方向和y軸方向兩個方向移動,移動的步長是1個像素。因此,運算後的卷積核的尺寸爲(13-3+12)/1+1=13,2個GPU中共1313*384個卷積後的像素層。即與上一層相同。

(2)分組

這384個卷積核分成兩組。會生成兩組13x13x192個卷積後的像素層。

(3)激活層

這些像素層經過relu4單元的處理,生成激活像素層,尺寸仍爲2組13x13x192像素層,共13x13x384個像素層。

(4)參數數量

卷積層,使用384個3x3x192卷積核,分2組,節點數量:13x13x192x2 = 64896,參數數量:(3x3x192x192+192)x2 = 663936。

5.卷積池化層5

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第五層輸入數據爲第四層輸出的2組13x13x192的像素層;爲便於後續處理,每幅像素層的左右兩邊和上下兩邊都要填充1個像素;2組像素層數據都被送至2個不同的GPU中進行運算。每個GPU中都有128個卷積核,每個卷積核的尺寸是3x3x192。因此,每個GPU中的卷積核能對1組13x13x192的像素層的數據進行卷積運算。卷積覈對每組數據的每次卷積都生成一個新的像素。卷積核沿像素層數據的x軸方向和y軸方向兩個方向移動,移動的步長是1個像素。因此,運算後的卷積核的尺寸爲(13-3+1x2)/1+1=13,每個GPU中共13x13x128個卷積核。

(2)分組

分組爲2組13x13x128像素層,共13x13x256個像素層。

(3)激活函數

這些像素層經過relu5單元的處理,生成激活像素層,尺寸仍爲2組13x13x128像素層,共13x13x256個像素層。

(4)池化層

2組13x13x128像素層分別在2個不同GPU中進行池化(pool)運算處理。池化運算的尺度爲3x3,運算的步長爲2,則池化後圖像的尺寸爲(13-3)/2+1=6。 即池化後像素的規模爲兩組6x6x128的像素層數據,共6x6x256規模的像素層數據。

(5)參數數量

卷積層,使用256個3x3x192卷積核,分2組,節點數量:13x13x128x2 = 43264,參數數量:(3x3x192x128+128)x2 = 442624。

6.全連接層1

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第六層輸入數據的尺寸是6x6x256,採用6x6x256=9216尺寸的濾波器對第六層的輸入數據進行卷積運算;每個6x6x256尺寸的濾波器對第六層的輸入數據進行卷積運算生成一個運算結果,通過一個神經元輸出這個運算結果;共有4096個6x6x256尺寸的濾波器對輸入數據進行卷積運算,通過4096個神經元輸出運算結果;

(2)激活函數

這4096個運算結果通過relu6激活函數生成4096個值。

(3)Dropout

**可以看到卷積神經網絡的計算量其實主要集中在全連接層,這些層參數太多了,也最容易發生過擬合。**DropOut通過訓練時隨機使得一部分結點失效,不貢獻連接權重而減少過擬合風險。同時強迫這些神經元去學習互補的一些特徵。因此最後通過dropout6運算後輸出4096個本層的輸出結果值。

(4)參數數量

根據全連接層的參數數量 = 上一層節點數量(pooling之後的) x 下一層節點數量 + 偏置數量(即下一層的節點數量)
參數數量爲:(66128*2)*4096+4096 = 37752832,可以看到這個參數數量遠遠大於之前所有卷積層的參數數量之和。也就是說AlexNet的參數大部分位於後面的全連接層。

7.全連接層2

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第六層輸出的4096個數據與第七層的4096個神經元進行全連接

(2)激活函數

然後經由relu7進行處理後生成4096個數據

(3)Dropout

再經過dropout7處理後輸出4096個數據。

(4)參數數量

全連接層,節點數量爲4096。參數數量爲:4096*4096 + 4096= 16781312。

8.全連接層3

在這裏插入圖片描述
在這裏插入圖片描述

(1)卷積運算

第七層輸出的4096個數據與第八層的1000個神經元進行全連接,經過訓練後輸出被訓練的數值。

(2)參數數量

全連接層,節點數量爲1000。參數數量爲: 4096*1000 + 1000 = 4097000。

四、PyTorch實現

import torch
from torch import nn
import numpy as np
from torch.autograd import Variable
class AlexNet(nn.Module):
    def __init__(self):
        super().__init__()
        
        # 第一層是 5x5 的卷積,輸入的channels 是 3,輸出的channels是 64,步長 1,沒有 padding
        # Conv2d 的第一個參數爲輸入通道,第二個參數爲輸出通道,第三個參數爲卷積核大小
        # ReLU 的參數爲inplace,True表示直接對輸入進行修改,False表示創建新創建一個對象進行修改
        self.conv1 = nn.Sequential(
            nn.Conv2d(3,64,5),
            nn.ReLU()
        )
        
        
        # 第二層爲 3x3 的池化,步長爲2,沒有padding
        self.max_pool1 = nn.MaxPool2d(3, 2)
        
        # 第三層是 5x5 的卷積, 輸入的channels 是64,輸出的channels 是64,沒有padding
        self.conv2 = nn.Sequential(
            nn.Conv2d(64, 64, 5, 1),
            nn.ReLU(True)
        )
        
        #第四層是 3x3 的池化, 步長是 2,沒有padding
        self.max_pool2 = nn.MaxPool2d(3,2)
        
        #第五層是全連接層,輸入是 1204 ,輸出是384
        self.fc1 = nn.Sequential(
            nn.Linear(1024,384),
            nn.ReLU(True)
        )
        
        # 第六層是全連接層,輸入是 384, 輸出是192
        self.fc2 = nn.Sequential(
            nn.Linear(384, 192),
            nn.ReLU(True)
        )
        
        # 第七層是全連接層,輸入是192, 輸出是 10
        self.fc3 = nn.Linear(192, 10)
        
    def forward(self, x):
        x = self.conv1(x)
        x = self.max_pool1(x)
        x = self.conv2(x)
        x = self.max_pool2(x)

        #將圖片矩陣拉平
        x = x.view(x.shape[0], -1)

        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x  
alexnet = AlexNet()
print(alexnet)

輸出:
在這裏插入圖片描述
輸入圖像大小(1, 3, 32, 32),驗證模型輸出大小。

input_demo = Variable(torch.zeros(1, 3, 32, 32))
output_demo = alexnet(input_demo)
print(output_demo.shape)

參考博客

  1. https://blog.csdn.net/zyqdragon/article/details/72353420

  2. https://vimsky.com/article/3664.html

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