這裏咱們會用 Python 實現三個簡單的卷積神經網絡模型:LeNet 、AlexNet 、VGGNet,首先咱們須要瞭解三大基礎數據集:MNIST 數據集、Cifar 數據集和 ImageNet 數據集html
MNIST 數據集網絡
MNIST數據集是用做手寫體識別的數據集。MNIST 數據集包含 60000 張訓練圖片,10000 張測試圖片。其中每一張圖片都是 0~9 中的一個數字。圖片尺寸爲 28×28。因爲數據集中數據相對比較簡單,人工標註錯誤率僅爲 0.2%。架構
Cifar 數據集app
Cifar 數據集是一個圖像分類數據集。分爲 Cifar-10 和 Cifar-100 兩個數據集。Cifar 數據集中的圖片爲 32×32 的彩色圖片,這些圖片是由 Alex Krizhenevsky 教授、Vinod Nair 博士和 Geoffrey Hilton 教授整理的。Cifar-10 數據集收集了來自 10 個不一樣種類的 60000 張圖片,這些種類有:飛機、汽車、鳥、貓、鹿、狗、青蛙、馬、船和卡車。在 Cifar-10 數據集上,人工標註的正確率爲94%。ide
ImageNet數據集函數
ImageNet 數據集是一個大型圖像數據集,由斯坦福大學的李飛飛教授帶頭整理而成。在 ImageNet 中,近 1500 萬張圖片關聯到 WordNet 中 20000 個名次同義詞集上。ImageNet 每一年舉行計算機視覺相關的競賽, ImageNet 的數據集涵蓋計算機視覺的各個研究方向,其用作圖像分類的數據集是 ILSVRC2012 圖像分類數據集。學習
ILSVRC2012 數據集的數據和 Cifar-10 數據集一致,識別圖像中主要物體,其包含了來自 1000 個種類的 120 萬張圖片,每張圖片只屬於一個種類,大小從幾千字節到幾百萬字節不等。卷積神經網絡也正是在此數據集上一戰成名。測試
三種經典的卷積神經網絡模型:LeNet 、AlexNet 、VGGNet。這三種卷積神經網絡的結構不算特別複雜,下面使用 PyTorch 進行實現url
LeNet模型
spa
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')