这里介绍四种。
LeNet-5
意义:LeNet-5是最早的卷积神经网络,是其他深度学习模型的基础, 主要用来做手写数字识别。
结构:2个5x5卷积层 + 2个全连接层+ 输出层
输入:32x32
输出:10分类
tensorflow实现:
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, models, utils, optimizers, losses
from tensorflow.keras.datasets import mnist
# 1 数据获取
(X_train, y_train), (X_test, y_test) = mnist.load_data()
print('训练集大小:', X_train.shape, '测试集大小:', X_test.shape)
# 2数据处理
# 添加通道数
X_train = tf.expand_dims(X_train, 3)
X_test = tf.expand_dims(X_test, 3)
# 3模型搭建
net = models.Sequential([
# 卷积层1
layers.Conv2D(filters=6, kernel_size=5, activation='sigmoid', input_shape=(28, 28, 1)),
# 池化层1
layers.MaxPool2D(pool_size=2),
# 卷积层2
layers.Conv2D(filters=16, kernel_size=5, activation='sigmoid'),
# 池化层2
layers.MaxPool2D(pool_size=2),
# 更改维度
layers.Flatten(),
# 全连接层
layers.Dense(120, activation='sigmoid'),
layers.Dense(84, activation='sigmoid'),
layers.Dense(10, activation='softmax')
], name='LeNet-5')
# 4模型编译和训练
sgd = optimizers.SGD(learning_rate=0.9, momentum=0.5)
net.compile(optimizer=sgd, loss='SparseCategoricalCrossentropy', metrics=['accuracy'])
# history = net.fit(X_train, y_train, epochs=100, batch_size=128) # 5模型评估和预测
history = net.fit(X_train, y_train, validation_data=(X_test[:100], y_test[:100]), epochs=80, batch_size=128)
# 评估
loss, accuracy = net.evaluate(X_test[101:], y_test[101:], verbose=True)
AlexNet
意义:开山之作,证明学习到的特征可以超越手工设计的特征,打破了cv的研究方向
8层网络:5个卷积层+2个全连接层+1个输出层
其中使用了1个11x11、1个5x5和3个3x3的卷积核
输入:227x227的图像
输出:1000分类
Tensor flow 实现:
import tensorflow as tf
from tensorflow.keras import models, layers
import numpy as np
class MyModel(models.Model):
def __init__(self):
super(MyModel, self).__init__()
self.conv1 = layers.Conv2D(96, kernel_size=11, strides=4, activation='relu')
self.maxpool1 = layers.MaxPool2D(pool_size=3, strides=2)
self.conv2 = layers.Conv2D(256, kernel_size=5, padding='same', activation='relu')
self.maxpool2 = layers.MaxPool2D(pool_size=3, strides=2)
self.conv3 = layers.Conv2D(384, kernel_size=3, padding='same', activation='relu')
self.conv4 = layers.Conv2D(384, kernel_size=3, padding='same', activation='relu')
self.conv5 = layers.Conv2D(256, kernel_size=3, padding='same', activation='relu')
self.maxpool3 = layers.MaxPool2D(pool_size=3, strides=2)
self.flat = layers.Flatten()
self.fc1 = layers.Dense(4096, activation='relu')
self.dropout1 = layers.Dropout(0.5)
self.fc2 = layers.Dense(4096, activation='relu')
self.dropout2 = layers.Dropout(0.5)
self.out = layers.Dense(1000, activation='softmax')
def call(self, inputs):
x = self.conv1(inputs)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.conv4(x)
x = self.conv5(x)
x = self.maxpool3(x)
x = self.flat(x)
x = self.fc1(x)
x = self.fc2(x)
x = self.out(x)
return x
model = MyModel()
input = tf.random.uniform((1, 227, 227, 1))
y = model(input)
print(model.summary())
VGG-16
意义:2014年牛津大学计算机视觉组和Google一起研发的新的神经网络,取得了ILSVR2014年比赛分类项目的第二名,主要贡献是使用很小的卷积核构建卷积神经网络结构,展现出深度是算法优良性能的关键部分。
网络架构:
VGG使用的基本结构是vgg块,一共有5个卷积块,前2块使用两个卷积层、而后3块使用三个卷积层,第一个输出通道是64,之后每次对输出通道数翻倍,知道变为512
输入:224x224
输出:1000分类,也是在ImageNet中训练的
vgg块的实现:
# 定义VGG网络中的卷积块:卷积层的个数,卷积层中卷积核的个数
def vgg_block(num_convs, num_filters):
# 构建序列模型
blk = tf.keras.models.Sequential()
# 遍历所有的卷积层
for _ in range(num_convs):
# 每个卷积层:num_filter个卷积核,卷积核大小为3*3,padding是same,激活函数是relu
blk.add(tf.keras.layers.Conv2D(num_filters,kernel_size=3,
padding='same',activation='relu'))
# 卷积块最后是一个最大池化,窗口大小为2*2,步长为2
blk.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2))
return blk
GoogLeNet
意义:这里应该注意不是googleNet,而是GooLeNet,是为了致敬LeNet。GoogLeNet和AlexNet/VGGNet这类依靠加深网络结构的深度的思想不完全一样。GoogLeNet在加深度的同时做了结构上的创新,引入了一个叫做Inception的结构来代替之前的卷积加激活的经典组件。GoogLeNet在ImageNet分类比赛上的Top-5错误率降低到了6.7%。
网络结构:
这里大量使用了1x1的卷积,主要作用是实现跨通道的语义信息融合和降维减少参数量。
实现:
class Inception(tf.keras.layers.Layer):
# 输入参数为各个卷积的卷积核个数
def __init__(self, c1, c2, c3, c4):
super().__init__()
# 线路1:1 x 1卷积层,激活函数是RELU,padding是same
self.p1_1 = tf.keras.layers.Conv2D(
c1, kernel_size=1, activation='relu', padding='same')
# 线路2,1 x 1卷积层后接3 x 3卷积层,激活函数是RELU,padding是same
self.p2_1 = tf.keras.layers.Conv2D(
c2[0], kernel_size=1, padding='same', activation='relu')
self.p2_2 = tf.keras.layers.Conv2D(c2[1], kernel_size=3, padding='same',
activation='relu')
# 线路3,1 x 1卷积层后接5 x 5卷积层,激活函数是RELU,padding是same
self.p3_1 = tf.keras.layers.Conv2D(
c3[0], kernel_size=1, padding='same', activation='relu')
self.p3_2 = tf.keras.layers.Conv2D(c3[1], kernel_size=5, padding='same',
activation='relu')
# 线路4,3 x 3最大池化层后接1 x 1卷积层,激活函数是RELU,padding是same
self.p4_1 = tf.keras.layers.MaxPool2D(
pool_size=3, padding='same', strides=1)
self.p4_2 = tf.keras.layers.Conv2D(
c4, kernel_size=1, padding='same', activation='relu')
# 完成前向传播过程
def call(self, x):
# 线路1
p1 = self.p1_1(x)
# 线路2
p2 = self.p2_2(self.p2_1(x))
# 线路3
p3 = self.p3_2(self.p3_1(x))
# 线路4
p4 = self.p4_2(self.p4_1(x))
# 在通道维上concat输出
outputs = tf.concat([p1, p2, p3, p4], axis=-1)
return outputs
GoogLeNet:
共分为5个模块:
B1模块:64个 7x7卷积层
B2模块:64个1x1卷积层 + 1个3x3卷积层+1个3x3池化层
B3模块:2个Inception模块 + 3x3最大池化
B4模块:5个Inception + 3x3最大池化,还有两个辅助输出类,防止网络太深过拟合
B5模块:2个Inception + GAP全局平均池化 + 全连接层输出分类结果
ResNet
在这之前,所有研究都倾向于通过加深网络来提高网络的精度,大家一致认为网络越深,获取的信息就越多,特征就越丰富。但在实践中,随着网络的加深,优化效果反而越差,测试数据和训练数据的准确率反而降低了。
意义:提出了残差连接结构,解决了网络退化的问题
网络结构:
残差块:
一共是两种残差块,右边的残差块中1x1的卷积,用来改变输入图像的通道数
残差块实现:
```python
# ResNet网络中模块的构成
class ResnetBlock(tf.keras.layers.Layer):
# 网络层的定义:输出通道数(卷积核个数),模块中包含的残差块个数,是否为第一个模块
def __init__(self,num_channels, num_residuals, first_block=False):
super(ResnetBlock, self).__init__()
# 模块中的网络层
self.listLayers=[]
# 遍历模块中所有的层
for i in range(num_residuals):
# 若为第一个残差块并且不是第一个模块,则使用1*1卷积,步长为2(目的是减小特征图,并增大通道数)
if i == 0 and not first_block:
self.listLayers.append(Residual(num_channels, use_1x1conv=True, strides=2))
# 否则不使用1*1卷积,步长为1
else:
self.listLayers.append(Residual(num_channels))
# 定义前向传播过程
def call(self, X):
# 所有层依次向前传播即可
for layer in self.listLayers.layers:
X = layer(X)
return X
```