這里我們會用 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 萬張圖片,每張圖片只屬於一個種類,大小從幾千字節到幾百萬字節不等。卷積神經網絡也正是在此數據集上一戰成名。
三種經典的卷積神經網絡模型
三種經典的卷積神經網絡模型:LeNet 、AlexNet 、VGGNet。這三種卷積神經網絡的結構不算特別復雜,下面使用 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
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
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)
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網絡,就是將這些基本單元比如卷積層個數、全連接個數按自己需要搭配變換,

# 模型需要事先下載 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')
參考:https://www.cnblogs.com/wmr95/articles/7814892.html