天天看點

【圖像分類】 一文讀懂AlexNet

目錄

1、 模型介紹

2、 模型結構

3、 模型特性

4、Pytorch官方實作

5、keras實作

AlexNet是由Alex Krizhevsky 提出的首個應用于圖像分類的深層卷積神經網絡,該網絡在2012年ILSVRC(ImageNet Large Scale Visual Recognition Competition)圖像分類競賽中以15.3%的top-5測試錯誤率赢得第一名。也是在那年之後,更多的更深的神經網絡被提出,比如優秀的vgg,GoogLeNet。 這對于傳統的機器學習分類算法而言,已經相當的出色。

AlexNet中包含了幾個比較新的技術點,也首次在CNN中成功應用了ReLU、Dropout和LRN等Trick。同時AlexNet也使用了GPU進行運算加速。

AlexNet将LeNet的思想發揚光大,把CNN的基本原理應用到了很深很寬的網絡中。AlexNet主要使用到的新技術點如下:

(1)成功使用ReLU作為CNN的激活函數,并驗證其效果在較深的網絡超過了Sigmoid,成功解決了Sigmoid在網絡較深時的梯度彌散問題。雖然ReLU激活函數在很久之前就被提出了,但是直到AlexNet的出現才将其發揚光大。

(2)訓練時使用Dropout随機忽略一部分神經元,以避免模型過拟合。Dropout雖有單獨的論文論述,但是AlexNet将其實用化,通過實踐證明了它的效果。在AlexNet中主要是最後幾個全連接配接層使用了Dropout。

(3)在CNN中使用重疊的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出讓步長比池化核的尺寸小,這樣池化層的輸出之間會有重疊和覆寫,提升了特征的豐富性。

(4)提出了LRN層,對局部神經元的活動建立競争機制,使得其中響應比較大的值變得相對更大,并抑制其他回報較小的神經元,增強了模型的泛化能力。

(5)使用CUDA加速深度卷積網絡的訓練,利用GPU強大的并行計算能力,處理神經網絡訓練時大量的矩陣運算。AlexNet使用了兩塊GTX 580 GPU進行訓練,單個GTX 580隻有3GB顯存,這限制了可訓練的網絡的最大規模。是以作者将AlexNet分布在兩個GPU上,在每個GPU的顯存中儲存一半的神經元的參數。因為GPU之間通信友善,可以互相通路顯存,而不需要通過主機記憶體,是以同時使用多塊GPU也是非常高效的。同時,AlexNet的設計讓GPU之間的通信隻在網絡的某些層進行,控制了通信的性能損耗。 

(6)資料增強,随機地從256*256的原始圖像中截取224*224大小的區域(以及水準翻轉的鏡像),相當于增加了2*(256-224)^2=2048倍的資料量。如果沒有資料增強,僅靠原始的資料量,參數衆多的CNN會陷入過拟合中,使用了資料增強後可以大大減輕過拟合,提升泛化能力。進行預測時,則是取圖檔的四個角加中間共5個位置,并進行左右翻轉,一共獲得10張圖檔,對他們進行預測并對10次結果求均值。同時,AlexNet論文中提到了會對圖像的RGB資料進行PCA處理,并對主成分做一個标準差為0.1的高斯擾動,增加一些噪聲,這個Trick可以讓錯誤率再下降1%。

【圖像分類】 一文讀懂AlexNet

首先這幅圖分為上下兩個部分的網絡,論文中提到這兩部分網絡是分别對應兩個GPU,隻有到了特定的網絡層後才需要兩塊GPU進行互動,這種設定完全是利用兩塊GPU來提高運算的效率,其實在網絡結構上差異不是很大。為了更友善的了解,我們假設現在隻有一塊GPU或者我們用CPU進行運算,我們從這個稍微簡化點的方向區分析這個網絡結構。網絡總共的層數為8層,5層卷積,3層全連接配接層。

第一層:卷積層1,輸入為 224 × 224 × 3 的圖像,卷積核的數量為96,論文中兩片GPU分别計算48個核; 卷積核的大小為 11 × 11 × 3,stride = 4,stride表示的是步長,padding = 2。

卷積後的圖形大小是怎樣的呢?

wide = (224 + 2 * padding - kernel_size) / stride + 1 = 55

height = (224 + 2 * padding - kernel_size) / stride + 1 = 55

dimention = 96

然後進行 (Local Response Normalized), 後面跟着池化pool_size = (3, 3), stride = 2, pad = 0 最終獲得第一層卷積的feature map

最終第一層卷積的輸出為96×55×55

第二層:卷積層2, 輸入為上一層卷積的feature map, 卷積的個數為256個,論文中的兩個GPU分别有128個卷積核。卷積核的大小為:5 × 5 × 48, padding = 2, stride = 1; 然後做 LRN, 最後 max_pooling, pool_size = (3, 3), stride = 2;

第三層:卷積3, 輸入為第二層的輸出,卷積核個數為384, kernel_size = (3 × 3 × 256 ), padding = 1, 第三層沒有做LRN和Pool

第四層:卷積4, 輸入為第三層的輸出,卷積核個數為384, kernel_size = (3 × 3 ), padding = 1, 和第三層一樣,沒有LRN和Pool

第五層:卷積5, 輸入為第四層的輸出,卷積核個數為256, kernel_size = (3 × 3 ), padding = 1。然後直接進行max_pooling, pool_size = (3, 3), stride = 2;

第6,7,8層是全連接配接層,每一層的神經元的個數為4096,最終輸出softmax為1000,因為上面介紹過,ImageNet這個比賽的分類個數為1000。全連接配接層中使用了RELU和Dropout。

下圖是對上面參數的總結。

【圖像分類】 一文讀懂AlexNet

所有卷積層都使用ReLU作為非線性映射函數,使模型收斂速度更快

在多個GPU上進行模型的訓練,不但可以提高模型的訓練速度,還能提升資料的使用規模

使用LRN對局部的特征進行歸一化,結果作為ReLU激活函數的輸入能有效降低錯誤率

重疊最大池化(overlapping max pooling),即池化範圍z與步長s存在關系z>s(如中核尺度為3×3/2),避免平均池化(average pooling)的平均效應

使用随機丢棄技術(dropout)選擇性地忽略訓練中的單個神經元,避免模型的過拟合

pytorch的官方并沒有嚴格按照AlexNet論文的實作,第一個卷積是64個卷積核,官方是96個。

class AlexNet(nn.Module):

   def __init__(self, num_classes=1000):

       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.Conv2d(192, 384, kernel_size=3, padding=1),

           nn.Conv2d(384, 256, kernel_size=3, padding=1),

           nn.Conv2d(256, 256, kernel_size=3, padding=1),

       )

       self.avgpool = nn.AdaptiveAvgPool2d((6, 6))

       self.classifier = nn.Sequential(

           nn.Dropout(),

           nn.Linear(256 * 6 * 6, 4096),

           nn.Linear(4096, 4096),

           nn.Linear(4096, num_classes),

   def forward(self, x):

       x = self.features(x)

       x = self.avgpool(x)

       x = torch.flatten(x, 1)

       x = self.classifier(x)

       return x

【圖像分類】 一文讀懂AlexNet

import os

import pandas as pd

import numpy as np

from keras.callbacks import EarlyStopping, ModelCheckpoint

from matplotlib import pyplot as plt

from skimage.io import imread, imshow

from skimage import transform

import warnings

from tqdm import tqdm

from keras.layers import Input, Lambda, Conv2D, MaxPool2D, BatchNormalization, Dense, Flatten, Dropout

from keras.models import Model

from keras.utils import to_categorical

def AlexNet(input_shape, num_classes):

   inputs = Input(input_shape, name="Input")

   x = ZeroPadding2D(((3, 0), (3, 0)))(inputs)

   x = Conv2D(96,

              (11, 11),

              4,

              kernel_initializer=initializers.RandomNormal(stddev=0.01),

              name="Conv_1")(x)

   x = Lambda(tf.nn.local_response_normalization, name="Lrn_1")(x)

   x = Activation(activation="relu")(x)

   x = MaxPool2D(name="Maxpool_1")(x)

   x = Conv2D(256,

              (5, 5),

              padding="SAME",

              name="Conv_2")(x)

   x = Lambda(tf.nn.local_response_normalization, name="Lrn_2")(x)

   x = MaxPool2D(name="Maxpool_2")(x)

   x = Conv2D(384,

              (3, 3),

              padding="Same",

              name="Conv_3_1")(x)

              name="Conv_3_2")(x)

              activation="relu",

              name="Conv_3_3")(x)

   x = MaxPool2D(name="Maxpool_3")(x)

   x = Flatten(name="Flt_1")(x)

   x = Dense(4096,

             activation="relu",

             kernel_initializer=initializers.RandomNormal(stddev=0.01),

             name="fc_1")(x)

   x = Dropout(0.5, name="drop_1")(x)

             name="fc_2")(x)

   x = Dropout(0.5, name="drop_2")(x)

   output = keras.layers.Dense(num_classes, activation="softmax", name="Output")(x)

   m = keras.Model(inputs, output, name="AlexNet")

   m.summary()

   return m