参考论文:VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION作者:Karen Simonyan ∗ & Andrew Zisserman +
2014年ImageNet图像分类竞赛亚军、定位竞赛冠军
1、VGGNet简介
VGGNet 是 2014 年 ImageNet Challenge 图像识别比赛的亚军。参赛团队是来自牛津 大学的研究组 VGG (Visual Geometry Group) 。VGGNet 的很多设计思想都受到 AlexNet 的影响,所以跟 AlexNet 也有一点点相似的地方。VGGNet 不仅在图像识别方向有着广泛应 用,很多目标检测,目标分割,人脸识别等方面的应用也会使用 VGGNet 作为基础模型。
VGGNet 在 2014,2015 年左右的流行程度甚至超过了 2014 年 ImageNet Challenge 图像识别比赛的冠军 GoogleNet,是当时用得最多的深度学习模型。VGGNet 被广泛使用 也是有一定原因的,VGGNet 的网络结构比较简单,也容易搭建,并且 VGGNet 的单模型结 果与 GoogleNet 相当。ImageNet Challenge 是一个比赛,在比赛中我们经常会使用模型 融合(Ensemble Model)策略,把多个模型组合在一起,这样有可能会得到更好的结果。 2014 年,在 ImageNet Challenge 比赛中,多个 GoogleNet 融合后的结果比多个 VGGNet 融合后的结果要更好,所以 GoogleNet 得到了冠军。最早提出 VGGNet 的论文是《Very Deep Convolutional Networks for Large-Scale Image Recognition》
2、VGGNet模型结构
VGGNet 有多个版本,如下图所示。
图中 ConvNet Configuration 表示网络结构;weight layers 表示网络层数;input 表示输入;conv 表示卷积;maxpool 表示最大池化;FC 表示全连接层。
我们可以看出 VGGNet 有 6 个不同的版本,他们的主要区别是网络层数和网络结构的区 别。图中的 conv3 表示 3×3 的卷积,conv1 表示 1×1 的卷积;conv3-128 表示 3×3 的 卷积计算后生成 128 个特征图;LRN(Local Response Normalization)是局部响应归一 化,一种在 AlexNet 中使用的数据归一化计算,不过 VGGNet 的作者认为 LRN 并没有什么 用,所以在 VGGNet 中并没有使用。
其中使用得比较多的有 B,因为它有 13 层,我们称之为 VGG13。使用得比较多的还有 D,因为它有 16 层,我们称之为 VGG16。使用得比较多的还有 E,因为它有 19 层,我们称 之为 VGG19。在 ImageNet Challenge 图像识别比赛中效果最好的是 VGG19,其次到 VGG16,最后是 VGG13。
上面几种模型对应的参数量(单位:百万)
每个版本的模型网络结构不同,所以参数的数量也有所不同。参数数量最少的是 A,有 1 亿 3 千多万个参数。最多的是 E,有 1 亿 4 千多万个参数。别看网络中有很多的卷积层,其 实网络中大部分的参数都是在全连接层中。比如在 VGG16 中,卷积层的参数数量占所有参 数的 13%,而全连接层的参数数量占到了 87%。
3、VGG16架构
由于在应用中VGG16架构用的多一点,所以这里研究并复现一下该架构。
3.1 模型结构
图中 fc 表示 fully connected,代表全连接;pool 表示 max pooling,代表最大池化; conv 表示 convolution,代表卷积;output 表示输出。
VGG16 的所有卷积都是 3×3,步长为 1,same padding;所有池化都是 2×2,步长 为 2,same padding;输出层函数为 softmax,除了输出层以外,其他层激活函数都是 ReLU 函数。
VGG16 受 AlexNet 的影响和启发,图片的输入为 224×224 的大小,卷积层后面也使用了 3 个全连接层,并且全连接层也是使用 4096 个神经元。
VGG16 是一个 16 层的网络,它的结构比较简单易懂,叠加了很多个卷积池化层。2× 2,步长为 2 的池化特会使得特征图的长宽减少为原来 1/2,池化后的下一个卷积会使得特征图的数量会变成原来的 2 倍。
3.2 各block块参数说明
VGG16 的输入是 224×224 大小的图片。
block1 为第 1,2 层,其中包含了 2 个卷积和 1 池化,卷积后图像大小没有发生变化 224×224,池化后特征图大小变成了 112×112,特征图的数量为 64。
block2 为第 3,4 层,其中包含了 2 个卷积和 1 池化,卷积后图像大小没有发生变化 112×112,池化后特征图大小变成了 56×56,特征图的数量为 128。
block3 为第 5,6,7 层,其中包含了 3 个卷积和 1 池化,卷积后图像大小没有发生变化 56×56,池化后特征图大小变成了 28×28,特征图的数量为 256。
block4 为第 8,9,10 层,其中包含了 3 个卷积和 1 池化,卷积后图像大小没有发生变 化 28×28,池化后特征图大小变成了 14×14,特征图的数量为 512。
block5 为第 11,12,13 层,其中包含了 3 个卷积和 1 池化,卷积后图像大小没有发生 变化 14×14,池化后特征图大小变成了 7×7,特征图的数量为 512。大家可能会稍微有点 疑惑,block5 中的特征图的数量按照规律不应该会变成 1024 吗,但是这里还是 512。这里的原因我猜测是作者他们肯定也尝试过 1024,但是最后的效果估计跟 512 的效果差不多。 并且改成 1024 后会增加很多计算量和需要训练的权值,所以最后的版本中就没有使用 1024。
第 14 层计算。把 pool5 的 512 个 7×7 的特征图数据跟 fc1 中的 4096 个神经元进行全 连接计算。
第 15 层计算。把 fc2 的 4096 个神经元跟 fc1 中的 4096 个神经元进行全连接计算。
第 16 层计算。把 output 的 1000(ImageNet Challenge 比赛有 1000 个分类)个神 经元跟 fc2 中的 4096 个神经元进行全连接计算。最后再经过 softmax 计算得到类别的概率 值进行输出。
VGGNet 网络结构本身并没有太多创新的内容,它可以看成是对 AlexNet 网络的改进优 化版本。
3.3 VGG16模型复现
import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Conv2D, MaxPool2D, Flatten, Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
from tensorflow.keras.callbacks import LearningRateScheduler
from plot_model import plot_model
这里我是在自己的数据集上面跑的,所以只有17个类别,原始应该是用的ImageNet数据集,有1000个类别
# 类别数
num_class = 17
# 批次大小
batch_size = 32
# 周期数
epochs = 100
# 图片大小
image_size = 224
网络搭建
本来想训练下这个模型,但是近一亿四千万的参数我机器实在是跑不动,想跑的话还是用预训练好的模型跑吧,这里主要是复现模型结构。
# VGG16网络搭建
inputs = Input(shape=(image_size,image_size,3))
# block1
x=Conv2D(filters=64,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(inputs)
x=Conv2D(filters=64,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=MaxPool2D(pool_size=(2,2),strides=2,padding='same')(x)
# block2
x=Conv2D(filters=128,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=128,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=MaxPool2D(pool_size=(2,2),strides=2,padding='same')(x)
# block3
x=Conv2D(filters=256,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=256,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=256,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=MaxPool2D(pool_size=(2,2),strides=2,padding='same')(x)
# block4
x=Conv2D(filters=512,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=512,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=512,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=MaxPool2D(pool_size=(2,2),strides=2,padding='same')(x)
# block5
x=Conv2D(filters=512,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=512,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=Conv2D(filters=512,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu')(x)
x=MaxPool2D(pool_size=(2,2),strides=2,padding='same')(x)
# 全连接
x=Flatten()(x)
x=Dense(4096,activation='relu')(x)
x=Dropout(0.5)(x)
x=Dense(4096,activation='relu')(x)
x=Dropout(0.5)(x)
out=Dense(1000,activation='softmax')(x)
model=Model(inputs=inputs,outputs=out)
model.summary()
plot_model(model,to_file='img/VGG16.png',show_shapes=True)
模型摘要如下:
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 224, 224, 3)] 0
_________________________________________________________________
conv2d (Conv2D) (None, 224, 224, 64) 1792
_________________________________________________________________
conv2d_1 (Conv2D) (None, 224, 224, 64) 36928
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 112, 112, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 112, 112, 128) 73856
_________________________________________________________________
conv2d_3 (Conv2D) (None, 112, 112, 128) 147584
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 56, 56, 128) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 56, 56, 256) 295168
_________________________________________________________________
conv2d_5 (Conv2D) (None, 56, 56, 256) 590080
_________________________________________________________________
conv2d_6 (Conv2D) (None, 56, 56, 256) 590080
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 28, 28, 256) 0
_________________________________________________________________
conv2d_7 (Conv2D) (None, 28, 28, 512) 1180160
_________________________________________________________________
conv2d_8 (Conv2D) (None, 28, 28, 512) 2359808
_________________________________________________________________
conv2d_9 (Conv2D) (None, 28, 28, 512) 2359808
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 14, 14, 512) 0
_________________________________________________________________
conv2d_10 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
conv2d_11 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
conv2d_12 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 512) 0
_________________________________________________________________
flatten (Flatten) (None, 25088) 0
_________________________________________________________________
dense (Dense) (None, 4096) 102764544
_________________________________________________________________
dropout (Dropout) (None, 4096) 0
_________________________________________________________________
dense_1 (Dense) (None, 4096) 16781312
_________________________________________________________________
dropout_1 (Dropout) (None, 4096) 0
_________________________________________________________________
dense_2 (Dense) (None, 1000) 4097000
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
References
Karen Simonyan, & Andrew Zisserman (2015). Very Deep Convolutional Networks for Large-Scale Image Recognition