通透!必會的六大卷積神經(jīng)網(wǎng)絡(luò)架構(gòu)
大家好,我是小寒。
今天給大家分享幾個經(jīng)典的卷積神經(jīng)網(wǎng)絡(luò)架構(gòu)。
這里主要是分享一些典型的架構(gòu),關(guān)于卷積神經(jīng)網(wǎng)絡(luò)的細節(jié)可以下面這篇文章。
1.LeNet
LeNet 是由 Yann LeCun 等人在 1998 年提出的卷積神經(jīng)網(wǎng)絡(luò)(CNN)架構(gòu),主要用于手寫數(shù)字識別。
LeNet 是最早的深度學習模型之一,它為后來的深度學習和計算機視覺領(lǐng)域奠定了基礎(chǔ)。
http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf
LeNet 架構(gòu)
整體架構(gòu)如下圖所示。
圖片
LeNet 包含以下幾個主要層。
- 輸入層,輸入尺寸為 32x32 的灰度圖像。
- 卷積層 C1,6 個 5x5 的卷積核,輸出尺寸為 28x28x6。
- 池化層 S2,平均池化,窗口大小為 2x2,輸出尺寸為 14x14x6。
- 卷積層 C3,16 個 5x5 的卷積核,輸出尺寸為 10x10x16。
- 池化層 S4,平均池化,窗口大小為 2x2,輸出尺寸為 5x5x16。
- 卷積層 C5,120 個 5x5 的卷積核,輸出尺寸為 1x1x120。
- 全連接層 F6,84 個神經(jīng)元。
- 輸出層,10 個神經(jīng)元
示例代碼
下面是使用 TensorFlow 和 Keras 實現(xiàn) LeNet 的示例代碼。
import tensorflow as tf
from tensorflow.keras import layers, models
import numpy as np
# LeNet-5 模型定義
def LeNet5():
model = models.Sequential()
# 輸入層(32x32x1)
model.add(layers.Input(shape=(32, 32, 1)))
# C1: 卷積層(6個5x5卷積核,輸出28x28x6)
model.add(layers.Conv2D(6, (5, 5), activatinotallow='tanh'))
# S2: 平均池化層(2x2池化窗口,輸出14x14x6)
model.add(layers.AveragePooling2D(pool_size=(2, 2), strides=2))
# C3: 卷積層(16個5x5卷積核,輸出10x10x16)
model.add(layers.Conv2D(16, (5, 5), activatinotallow='tanh'))
# S4: 平均池化層(2x2池化窗口,輸出5x5x16)
model.add(layers.AveragePooling2D(pool_size=(2, 2), strides=2))
# C5: 卷積層(120個5x5卷積核,輸出1x1x120)
model.add(layers.Conv2D(120, (5, 5), activatinotallow='tanh'))
# Flatten: 展平
model.add(layers.Flatten())
# F6: 全連接層(84個神經(jīng)元)
model.add(layers.Dense(84, activatinotallow='tanh'))
# 輸出層(10個神經(jīng)元,對應分類0-9)
model.add(layers.Dense(10, activatinotallow='softmax'))
return model
# 創(chuàng)建 LeNet-5 模型
model = LeNet5()
# 打印模型結(jié)構(gòu)
model.summary()
# 編譯模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 加載數(shù)據(jù)集(MNIST 手寫數(shù)字識別數(shù)據(jù)集)
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 歸一化
x_train, x_test = x_train / 255.0, x_test / 255.0
print(x_train[0])
# 擴展維度以匹配模型輸入
x_train = np.pad(x_train, ((0, 0), (2, 2), (2, 2)), 'constant')
x_test = np.pad(x_test, ((0, 0), (2, 2), (2, 2)), 'constant')
print(x_train[0])
# 擴展維度以匹配模型輸入
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]
print(x_train.shape)
# 訓練模型
model.fit(x_train, y_train, epochs=10, validation_data=(x_test, y_test))
2.AlexNet
AlexNet 是一個深度卷積神經(jīng)網(wǎng)絡(luò),于 2012 年在 ImageNet 圖像識別挑戰(zhàn)賽中獲勝,由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 開發(fā)。這個模型不僅大幅提高了圖像分類任務的準確率,而且也推動了深度學習在多個領(lǐng)域的廣泛應用。
https://proceedings.neurips.cc/paper_files/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
快速學會一個算法,CNN
架構(gòu)
AlexNet 的整體架構(gòu)如下所示。
圖片
AlexNet 包含以下幾個主要層。
- 輸入層,輸入圖像大小為 227x227x3。
- 第1個卷積層,使用 96 個 11x11 的卷積核,步長為 4,后接最大池化。
- 第2個卷積層,使用 256 個 5x5 的卷積核,步長為1,采用填充,后接最大池化。
- 第3個卷積層,使用 384 個 3x3 的卷積核,步長為1,采用填充。
- 第4個卷積層,使用 384 個 3x3 的卷積核,步長為1,采用填充。
- 第5個卷積層,使用 256 個 3x3 的卷積核,步長為1,采用填充,后接最大池化。
- 全連接層,三個全連接層,前兩個各有 4096 個神經(jīng)元,最后一個有 1000 個輸出神經(jīng)元,每個對應一個類別,使用softmax激活函數(shù)進行多類分類。
示例代碼
下面是使用 TensorFlow 和 Keras 實現(xiàn) AlexNet 的示例代碼。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Activation
model = Sequential([
# 第1層 - 卷積層
Conv2D(96, (11, 11), strides=(4, 4), padding='valid', input_shape=(227, 227, 3)),
Activation('relu'),
MaxPooling2D(pool_size=(3, 3), strides=(2, 2)),
# 第2層 - 卷積層
Conv2D(256, (5, 5), padding='same'),
Activation('relu'),
MaxPooling2D(pool_size=(3, 3), strides=(2, 2)),
# 第3層 - 卷積層
Conv2D(384, (3, 3), padding='same'),
Activation('relu'),
# 第4層 - 卷積層
Conv2D(384, (3, 3), padding='same'),
Activation('relu'),
# 第5層 - 卷積層
Conv2D(256, (3, 3), padding='same'),
Activation('relu'),
MaxPooling2D(pool_size=(3, 3), strides=(2, 2)),
# 展平層
Flatten(),
# 第6層 - 全連接層
Dense(4096),
Activation('relu'),
Dropout(0.5),
# 第7層 - 全連接層
Dense(4096),
Activation('relu'),
Dropout(0.5),
# 輸出層
Dense(1000),
Activation('softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()
3.VGGNet
VGGNet 是由牛津大學的視覺幾何組 (Visual Geometry Group, VGG) 開發(fā)的一種卷積神經(jīng)網(wǎng)絡(luò)架構(gòu)。它首次在 2014 年的 ImageNet 競賽中提出,并因其簡單而有效的結(jié)構(gòu)而受到廣泛關(guān)注。
https://arxiv.org/pdf/1409.1556
VGGNet 的主要創(chuàng)新是使用多個較小的卷積核(3x3)而不是較大的卷積核,這增加了網(wǎng)絡(luò)的深度來改善特征的學習能力,同時減少了參數(shù)數(shù)量。
VGGNet 有幾種不同的配置,常見的如 VGG16 和 VGG19,分別含有 16 和 19 層深度。
VGG16的架構(gòu)如下圖所示,它由 13 個卷積層和 3個全連接層組成。
圖片
VGG16 和 VGG19
VGG16 架構(gòu)
VGG16 包括 16 個權(quán)重層,其結(jié)構(gòu)包括:
- 13 個卷積層,使用 3x3 的卷積核,步長為1,邊緣填充也為1,確保卷積操作后特征圖的尺寸不變。
- 5 個最大池化層,用于降低特征圖的尺寸。
- 3 個全連接層,前兩個全連接層各有 4096 個節(jié)點,最后一個全連接層用于分類,節(jié)點數(shù)取決于類別數(shù)(通常為 1000,對應 ImageNet 數(shù)據(jù)集)。
VGG19 架構(gòu)
VGG19 是 VGG16 的一個更深版本,包括 19 個權(quán)重層。它的結(jié)構(gòu)包括:
- 16 個卷積層,配置與 VGG16 類似,但在第三、四、五個卷積塊中增加了更多的卷積層。
- 5 個最大池化層,與 VGG16 相同,用于特征下采樣。
- 3 個全連接層,配置同 VGG16。
圖片
示例代碼
下面是使用 TensorFlow 和 Keras 實現(xiàn) VGG16 的示例代碼。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
def VGG16(input_shape=(224, 224, 3), num_classes=1000):
model = Sequential([
# Block 1
Conv2D(64, (3, 3), padding='same', activatinotallow='relu', input_shape=input_shape),
Conv2D(64, (3, 3), padding='same', activatinotallow='relu'),
MaxPooling2D((2, 2), strides=(2, 2)),
# Block 2
Conv2D(128, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(128, (3, 3), padding='same', activatinotallow='relu'),
MaxPooling2D((2, 2), strides=(2, 2)),
# Block 3
Conv2D(256, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(256, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(256, (3, 3), padding='same', activatinotallow='relu'),
MaxPooling2D((2, 2), strides=(2, 2)),
# Block 4
Conv2D(512, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(512, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(512, (3, 3), padding='same', activatinotallow='relu'),
MaxPooling2D((2, 2), strides=(2, 2)),
# Block 5
Conv2D(512, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(512, (3, 3), padding='same', activatinotallow='relu'),
Conv2D(512, (3, 3), padding='same', activatinotallow='relu'),
MaxPooling2D((2, 2), strides=(2, 2)),
# Fully connected layers
Flatten(),
Dense(4096, activatinotallow='relu'),
Dense(4096, activatinotallow='relu'),
Dense(num_classes, activatinotallow='softmax')
])
return model
# 創(chuàng)建模型
model = VGG16()
model.summary()
4.GoogLeNet
GoogLeNet,也被稱為 Inception v1,是一種深度卷積神經(jīng)網(wǎng)絡(luò)(CNN),最初由Google的研究者在2014年提出。
https://arxiv.org/pdf/1409.4842
它在當年的ImageNet挑戰(zhàn)賽中取得了冠軍,以其創(chuàng)新的 “Inception模塊” 而著稱,該模塊能夠顯著增加網(wǎng)絡(luò)的寬度和深度,同時保持計算資源的合理使用。
整體架構(gòu)如下圖所示。
圖片
Inception模塊
圖片
如上所示,它與我們之前看到的順序架構(gòu)相比發(fā)生了巨大變化。
在單層中,存在多種類型的 “特征提取器”。這間接幫助網(wǎng)絡(luò)表現(xiàn)更好,因為訓練中的網(wǎng)絡(luò)本身在解決任務時有很多選擇。它可以選擇卷積輸入,也可以直接池化輸入。
下面是 Inception 模塊的代碼實現(xiàn)。
def inception_module(x, filters):
# 1x1卷積
path1 = Conv2D(filters=filters[0], kernel_size=(1, 1), padding='same', activatinotallow='relu')(x)
# 1x1卷積后接3x3卷積
path2 = Conv2D(filters=filters[1], kernel_size=(1, 1), padding='same', activatinotallow='relu')(x)
path2 = Conv2D(filters=filters[2], kernel_size=(3, 3), padding='same', activatinotallow='relu')(path2)
# 1x1卷積后接5x5卷積
path3 = Conv2D(filters=filters[3], kernel_size=(1, 1), padding='same', activatinotallow='relu')(x)
path3 = Conv2D(filters=filters[4], kernel_size=(5, 5), padding='same', activatinotallow='relu')(path3)
# 3x3最大池化后接1x1卷積
path4 = MaxPooling2D(pool_size=(3, 3), strides=(1, 1), padding='same')(x)
path4 = Conv2D(filters=filters[5], kernel_size=(1, 1), padding='same', activatinotallow='relu')(path4)
# 合并所有路徑
return concatenate([path1, path2, path3, path4], axis=-1)
5.ResNet
ResNet(殘差網(wǎng)絡(luò))是由微軟研究院的何凱明等人在 2015 年提出的一種深度學習模型,主要用于圖像識別和相關(guān)視覺任務。
https://arxiv.org/pdf/1512.03385
它解決了深層網(wǎng)絡(luò)訓練難題中的梯度消失或爆炸問題,使得網(wǎng)絡(luò)能夠通過增加層數(shù)來提高準確率,而不會降低訓練效果。
圖片
原理
ResNet 的核心概念是引入了 “殘差學習” 的思想。
在傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)中,每一層的輸出是直接傳遞給下一層的。
而在 ResNet 中,引入了跳躍連接,它允許輸入直接 “跳過” 一些層傳到更深的層。
圖片
其公式可以表示為:
其中,X 是輸入,F(xiàn)(x) 是殘差函數(shù),H(x) 是從輸入到輸出的映射函數(shù)。
殘差塊
殘差一樣采用模組化的方式,針對不同深度的 ResNet,作者提出了兩種殘差塊。
圖片
1.基本殘差塊對于較淺的 ResNet 模型(例如 ResNet-18 和 ResNet-34),使用的是基本殘差塊,這種塊的結(jié)構(gòu)相對簡單。下面是基本殘差塊的 python 代碼實現(xiàn)。
import torch
import torch.nn as nn
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_channels, out_channels, stride=1):
super(BasicBlock, self).__init__()
# 第一個卷積層
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
# 第二個卷積層
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
# 快捷連接,如果維度不匹配,需要通過1x1卷積調(diào)整維度
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != self.expansion * out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion * out_channels)
)
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += self.shortcut(identity)
out = self.relu(out)
return out
兩個 3×3 的卷積層每個卷積層后面通常跟有批量歸一化(Batch Normalization)和 ReLU 激活函數(shù)。第一個卷積層處理輸入數(shù)據(jù),而第二個卷積層進一步提煉特征。
跳躍連接輸入直接通過跳躍連接跳過兩個卷積層,最終與第二個卷積層的輸出相加。這種直接的連接幫助網(wǎng)絡(luò)學習恒等映射,即當增加更多層時,新層可以被訓練為不改變已學習特征的恒等映射,這樣不會損害網(wǎng)絡(luò)的性能。
2.瓶頸殘差塊對于更深的 ResNet 模型(如 ResNet-50 和 ResNet-152),使用的是瓶頸殘差塊,其設(shè)計更復雜,以提高計算效率。下面是瓶頸殘差塊的python代碼實現(xiàn)。
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, in_channels, out_channels, stride=1):
super(Bottleneck, self).__init__()
# 降維
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
# 特征提取
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
# 恢復維度
self.conv3 = nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(out_channels * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels * self.expansion:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels * self.expansion, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels * self.expansion)
)
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
out += self.shortcut(identity)
out = self.relu(out)
return out
第一層,使用 1×1 卷積核,主要目的是減少輸入的維度(通道數(shù)),這有助于減少后續(xù)層的計算負擔。
第二層,標準的 3×3 卷積層,在降維后的特征上進行空間特征提取。
第三層,再次使用 1×1 卷積核,目的是恢復通道數(shù),為將輸出與跳躍連接相加做準備。
跳躍連接,如果輸入與輸出的維度不匹配(通常在跨越殘差塊時會改變維度),跳躍連接上也會應用 1×1 卷積來調(diào)整維度,確保能夠與主路徑上的輸出相加。
6.ResNeXt
ResNeXt 是由 Facebook AI Research 提出的,它是一種改進的卷積神經(jīng)網(wǎng)絡(luò)架構(gòu),基于 ResNet,但通過引入組卷積(group convolution)進一步提高了模型的性能和效率。
ResNeXt 的核心思想是使用多個并行的路徑,每個路徑都有其獨立的卷積層,最終通過累加這些路徑的輸出來提升模型的表達能力。
https://arxiv.org/pdf/1611.05431v2
網(wǎng)絡(luò)架構(gòu)
ResNeXt 的特點在于將 ResNet 的 bottleneck 塊改進為類似于 Inception 的結(jié)構(gòu),通過多路徑的方式,將較大的通道數(shù)分解為多個較小的通道數(shù),從而達到同樣的效果。此外,ResNeXt 引入了一個新的超參數(shù) C(cardinality),用于表示路徑的數(shù)量。
ResNeXt 的架構(gòu)如下所示。
圖片