使用PyTorch簡單實現卷積神經網絡模型


  這里我們會用 Python 實現三個簡單的卷積神經網絡模型:LeNet 、AlexNet 、VGGNet,首先我們需要了解三大基礎數據集:MNIST 數據集、Cifar 數據集和 ImageNet 數據集

三大基礎數據集

MNIST 數據集

  MNIST數據集是用作手寫體識別的數據集。MNIST 數據集包含 60000 張訓練圖片,10000 張測試圖片。其中每一張圖片都是 0~9 中的一個數字。圖片尺寸為 28×28。由於數據集中數據相對比較簡單,人工標注錯誤率僅為 0.2%。

Cifar 數據集

  Cifar 數據集是一個圖像分類數據集。分為 Cifar-10 和 Cifar-100 兩個數據集。Cifar 數據集中的圖片為 32×32 的彩色圖片,這些圖片是由 Alex Krizhenevsky 教授、Vinod Nair 博士和 Geoffrey Hilton 教授整理的。Cifar-10 數據集收集了來自 10 個不同種類的 60000 張圖片,這些種類有:飛機、汽車、鳥、貓、鹿、狗、青蛙、馬、船和卡車。在 Cifar-10 數據集上,人工標注的正確率為94%。

ImageNet數據集

  ImageNet 數據集是一個大型圖像數據集,由斯坦福大學的李飛飛教授帶頭整理而成。在 ImageNet 中,近 1500 萬張圖片關聯到 WordNet 中 20000 個名次同義詞集上。ImageNet 每年舉行計算機視覺相關的競賽, ImageNet 的數據集涵蓋計算機視覺的各個研究方向,其用做圖像分類的數據集是 ILSVRC2012 圖像分類數據集。

  ILSVRC2012 數據集的數據和 Cifar-10 數據集一致,識別圖像中主要物體,其包含了來自 1000 個種類的 120 萬張圖片,每張圖片只屬於一個種類,大小從幾千字節到幾百萬字節不等。卷積神經網絡也正是在此數據集上一戰成名。

 

三種經典的卷積神經網絡模型

  三種經典的卷積神經網絡模型:LeNetAlexNetVGGNet。這三種卷積神經網絡的結構不算特別復雜,下面使用 PyTorch 進行實現

LeNet模型

  LeNet 具體指的是 LeNet-5。LeNet-5 模型是 Yann LeCun 教授於 1998 年在論文 Gradient-based learning applied to document recognition 中提出的,它是第一個成功應用於數字識別問題的卷積神經網絡。在 MNIST 數據集上,LeNet-5 模型可以達到大約 99.2% 的正確率。LeNet-5 模型總共有7層,包括有2個卷積層,2個池化層,2個全連接層和一個輸出層,下圖展示了 LeNet-5 模型的架構。

 使用 PyTorch 實現

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1   = nn.Linear(16*5*5, 120)
        self.fc2   = nn.Linear(120, 84)
        self.fc3   = nn.Linear(84, 10)
    def forward(self, x):
        out = F.relu(self.conv1(x))
        out = F.max_pool2d(out, 2)
        out = F.relu(self.conv2(out))
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = F.relu(self.fc1(out))
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return out
PyTorch 實現 LeNet 模型

 

AlexNet模型

  Alex Krizhevsky 提出了卷積神經網絡模型 AlexNet。AlexNet 在卷積神經網絡上成功地應用了 Relu,Dropout 和 LRN 等技巧。在 ImageNet 競賽上,AlexNet 以領先第二名 10% 的准確率而奪得冠軍。成功地展示了深度學習的威力。它的網絡結構如下:

  由於當時GPU計算能力不強,AlexNet使用了兩個GPU並行計算,現在可以用一個GPU替換。以單個GPU的AlexNet模型為例,包括有:5個卷積層,3個池化層,3個全連接層。其中卷積層和全連接層包括有relu層,在全連接層中還有dropout層。具體參數的配置可以看下圖

 使用 PyTorch 實現

class AlexNet(nn.Module):
    def __init__(self, num_classes):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 256, 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
PyTorch 實現 AlexNet 模型

 

VGGNet模型

  VGGNet是牛津大學計算機視覺組和Google DeepMind公司的研究人員一起研發的一種卷積神經網絡。通過堆疊3×3 的小型卷積核和2×2的最大池化層,VGGNet成功地構築了最深達19層的卷積神經網絡。由於VGGNet的拓展性強,遷移到其他圖片數據上的泛化性比較好,可用作遷移學習。

下圖為 VGG16 的整體架構圖

  從左至右,一張彩色圖片輸入到網絡,白色框是卷積層,紅色是池化,藍色是全連接層,棕色框是預測層。預測層的作用是將全連接層輸出的信息轉化為相應的類別概率,而起到分類作用。

可以看到 VGG16 是13個卷積層+3個全連接層疊加而成。

使用 PyTorch 實現

cfg = {
    'VGG11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'VGG13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'VGG16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
    'VGG19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}
class VGG(nn.Module):
    def __init__(self, vgg_name):
        super(VGG, self).__init__()
        self.features = self._make_layers(cfg[vgg_name])
        self.classifier = nn.Linear(512, 10)
    def forward(self, x):
        out = self.features(x)
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out
    def _make_layers(self, cfg):
        layers = []
        in_channels = 3
        for x in cfg:
            if x == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
                           nn.BatchNorm2d(x),
                           nn.ReLU(inplace=True)]
                in_channels = x
        layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
        return nn.Sequential(*layers)
PyTorch 實現 VGGNet 模型

 

  VGG16 是基於大量真實圖像的 ImageNet 圖像庫預訓練的網絡

  VGG16 對應的供 keras 使用的模型人家已經幫我們訓練好,我們將學習好的 VGG16 的權重遷移(transfer)到自己的卷積神經網絡上作為網絡的初始權重,這樣我們自己的網絡不用從頭開始從大量的數據里面訓練,從而提高訓練速度。這里的遷移就是平時所說的遷移學習。

from keras.models import Sequential
from keras.layers.core import Flatten, Dense, Dropout
from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
from keras.optimizers import SGD
import cv2, numpy as np


def VGG_16(weights_path=None):
    model = Sequential()
    model.add(ZeroPadding2D((1, 1), input_shape=(3, 244, 244))) # 卷積輸入層指定輸入圖像大小(網絡開始輸入(3,224,224)的圖像數據,即一張寬224,高244的彩色RGB圖片)
    model.add(Convolution2D(64, 3, 3, activation='relu')) # 64個3*3的卷積核,生成64*244*244的圖像
    model.add(ZeroPadding2D(1, 1)) # 補0,(1, 1)表示橫向和縱向都補0,保證卷積后圖像大小不變,可以用padding='valid'參數代替
    model.add(Convolution2D(64, 3, 3, activation='relu')) # 再次卷積操作,生成64*244*244的圖像,激活函數是relu
    model.add(MaxPooling2D((2, 2), strides=(2, 2))) # pooling操作,相當於變成64*112*112,小矩陣是(2,2),步長(2,2),指的是橫向每次移動2格,縱向每次移動2格。

    # 再往下,同理,只不過是卷積核個數依次變成128,256,512,而每次按照這樣池化之后,矩陣都要縮小一半。
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(128, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(128, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2))) # 128*56*56

    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(256, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(256, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(256, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))  # 256*28*28

    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))  # 512*14*14

    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(ZeroPadding2D((1, 1)))
    model.add(Convolution2D(512, 3, 3, activation='relu'))
    model.add(MaxPooling2D((2, 2), strides=(2, 2)))  # 128*7*7
    # 13層卷積和池化之后,數據變成了512 * 7 * 7

    model.add(Flatten) # 壓平上述向量,變成一維512*7*7=25088
    # 三個全連接層
    '''
    4096只是個經驗值,其他數當然可以,試試效果,只要不要小於要預測的類別數,這里要預測的類別有1000種,
    所以最后預測的全連接有1000個神經元。如果你想用VGG16 給自己的數據作分類任務,這里就需要改成你預測的類別數。
    '''
    model.add(Dense(4096, activation='relu')) # 全連接層有4096個神經元,參數個數是4096*25088
    model.add(Dropout(0.5)) # 0.5的概率拋棄一些連接
    model.add(Dense(4096, activation='relu'))  # 全連接層有4096個神經元,參數個數是4096*25088
    model.add(Dropout(0.5))  # 0.5的概率拋棄一些連接
    model.add(Dense(1000, activation='softmax'))

    if weights_path:
        model.load_weights(weights_path)
    return model

# 如果要設計其他類型的CNN網絡,就是將這些基本單元比如卷積層個數、全連接個數按自己需要搭配變換,
使用 Keras 實現 VGG16
# 模型需要事先下載
model = VGG_16('vgg16_weights.h5')

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(optimizer=sgd, loss='categorical_crossentropy')

# 加載圖片
def load_image(img):
    im = cv2.resize(cv2.imread(img), (224, 224)).astype(np.float32)
    im[:,:,0] -= 103.939
    im[:,:,1] -= 116.779
    im[:,:,2] -= 123.68
    im = im.transpose((2, 0, 1))
    im = np.expand_dims(im, axis=0)
    return im

# 讀取vgg16類別的文件
f = open('synset_words.txt', 'r')
lines = f.readline()
f.close()

def predict(url):
    im = load_image(url)
    pre = np.argmax(model.predict(im))
    print(lines[pre])
    
# 測試圖片
predict('xx.jpg')
測試 VGG16 模型

 

           

 

 

參考:https://www.cnblogs.com/wmr95/articles/7814892.html          

 

 

 

 

 

                 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM