Press "Enter" to skip to content

理解 VGG 卷积神经网络

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

VGG 网络是一种经典的图像分类网络,通过多层卷积操作提取图像特征实现图片分类。由于能够提取图像的特征,也应用于风格迁移网络中的损失函数。另外用于边缘检测的 HED(Holistically-Nested Edge Detection) 网络也是基于 VGG 网络发展而来。 VGG 名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)。

 

本文主要分析 VGG 网络的模型结构,从模型结构出发了解模型各层的作用,数学原理,以及 TensorFlow 实现。

 

根据卷积层数及参数的不同,VGG 网络有以下不同的结构:

VGG 网络结构

较常使用的是 VGG-16 与 VGG-19,其中16、19代表的是去除了池化层(maxpool) 与 softmax 层之后的网络层数,其中 VGG-19 比 VGG-16 多了 3 个卷积层。

 

下图展现了 VGG-16 图像分类网络的结构:

VGG-16 网络

从右下角的图例可以看出 VGG-16 图像分类网络主要包含了 4 类层级:

convolution + ReLU : 卷积层提取特征,ReLU 为激活函数 max(0, x)
max pooling: 最大池化层
fully connected + ReLU: 全连接层
softmax:用于输出最终各分类结果的概率分布

卷积层

 

先从卷积层入手,那幺何为卷积?我们称 (f * g)(n) 为 f, g 的卷积,在离散空间下有如下定义:

以掷骰子为例,求两枚骰子掷出和为 4 的概率,f(x) 表示第一个骰子掷出 x 的概率,g(x) 表示第二个骰子掷出 x 的概率,那幺 P = f(1)g(3) + f(2)g(2) + f(3)g(1)。以卷积的形式表示为:

矩阵的卷积稍有不同,通常有一个称为卷积核(也称为过滤器)的矩阵 w,步长为 1 针对矩阵 a 进行卷积,过程如下:

矩阵卷积

这里还有一个小细节,还记得离散卷积公式中的 n 吗,卷积过程中有个不变量,矩阵卷积中这个不变量是下标和,为了更直观并且方便计算,w 其实已经将卷积核上下翻转 180° 了,以达到下标和为 (1, 1) 的效果。

那幺矩阵卷积的公式可以对应为:

为了不让卷积操作减小原矩阵的尺寸,可以在输入矩阵的边界加入全 0 填充(zero-padding),这可以使得输出矩阵与输入矩阵尺寸保持一致。

当然步长也会影响最终卷积结果的尺寸:

那幺如果不使用全 0 填充,输出矩阵的尺寸由卷积核尺寸与步长共同决定:

 

out-length = (in-length – filter-length + 1) / stride-length

 

Tensorflow 可以方便的实现卷积层:

 

filters = tf.truncated_normal([3, 3, in_channels, out_channels], 0.0, 0.001)
biases = tf.truncated_normal([out_channels], .0, .001)
conv = tf.nn.conv2d(input=bottom, filter=filters, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.nn.bias_add(conv, conv_biases)
relu = tf.nn.relu(bias)

 

filters 声明的是一个四维矩阵,第一第二维代表卷积核(或者称过滤器)尺寸,第三维表示输入层的深度,第四维表示输出层的深度。biases 是个偏置量,每个深度对应一个偏置量,所以总共有输出层的深度个偏置量。

 

tf.nn.conv2d 实现了卷积层的向前传播算法,input 为输入矩阵,strides 为长度为 4 的数组,表示步长,因为步长只对矩阵长和宽有效,所以数组的第一位与第四位一般为 1 ,第二位与第三位表示步长。padding 参数为填充方法, SAME 表示全 0 填充, VALID 表示步填充。

 

然后卷积操作之后一般会加上偏置量,再通过激活函数输出给下一层。

 

池化层

 

池化层主要作用是缩小矩阵尺寸,减少参数,起到了加速计算同时防止过拟合的作用。与卷积层类似,池化层也有一个类似过滤器的结构,并且也有步长的概念。池化层主要分为最大池化层(max pooling)和平均池化层(average pooling)。

 

下图展示了过滤器尺寸为 2*2,步长为 2,使用 0 填充的最大池化层过程:

以下是 TensorFlow 实现的最大池化层向前传播算法:

 

tf.nn.max_pool(bottom, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

 

ksize 表示的是过滤器尺寸,是个长度为 4 的数组,第一、四位为 1,strides 表示步长,同样的第一、四位为 1。padding=’SAME’ 表示使用 0 填充。这里虽然使用了 0 填充,但是步长为 2 ,所以最终输出尺寸会缩小。

 

全连接层

 

在整个卷积神经网络中卷积层,池化层是针对输入的特征提取,那幺全连接层起到“分类器”的作用。实现上比较简单,直接使用矩阵相乘即可。

 

两个矩阵相乘有以下尺寸特征:已知矩阵 X 尺寸为 (a, b),矩阵 Y 尺寸为 (m, n),矩阵 Z = X * Y。那幺必须有 b == m,并且输出 Z 的尺寸为 (a, n)。

 

例如 VGG-16 中的第一个全连接层,输入尺寸为 (7,7,512),通过 tf.reshape 转换成 (1, 7*7*512) , 也就是 (1, 25088),矩阵乘另外一个大小为 (25088, 4096) 的矩阵,最终输出矩阵尺寸为 (1, 4096),用以下公式表示:

 

(1, 25088)*(25088, 4096) = (1, 4096)

 

使用 TensorFlow 实现以上逻辑:

 

weights = tf.truncated_normal([25088, 4096], 0.0, 0.001)
biases = tf.truncated_normal([4096], .0, .001)
x = tf.reshape(bottom, [-1, 25088])
fc = tf.nn.bias_add(tf.matmul(x, weights), biases)

 

其中 bottom 为尺寸是 (7,7,512) 的输入矩阵,使用 tf.matmul 实现矩阵层的向前传播算法,然后再使用 tf.nn.bias_add 加上偏置量。需要注意的是在使用 tf.matmul 的时候小心和 tf.multiply 混淆,后者是矩阵点乘。

 

Softmax 回归

 

Softmax 一般用于分类问题,有可以算作是一种激活函数,作用是将最后输出结果转换为归一化的概率分布。数学公式表示为:

对于输入向量 [1, 2, 3, 4, 1, 2, 3] 对应的 Softmax 函数的值为 [0.024, 0.064, 0.175, 0.475, 0.024, 0.064, 0.175]。从效果上来看和每一项除以总和有点类似,之所以在公式中引入自然参数 e 是为了 凸显其中最大的值并抑制远低于最大值的其他分量。

 

TensorFlow 中的实现如下:

 

prob = tf.nn.softmax(fc8, name="prob")

 

激活函数

 

我们经常会使用激活函数来去除模型的线性化,让网络可以使用更少的参数抽象更复杂的模型,常见的激活函数有 sigmoid函数、tanh函数、relu函数。

 

VGG-16 中使用 relu 作为激活函数,数学表示为:

 

relu = max(0, x)

 

sigmoid 与 tanh 函数的几何图像分别如下:

sigmoid

tanh

需要注意的是各激活函数的输出范围,比如 sigmoid 函数输出的范围是 (0, 1);tanh 函数输出范围是 (-1, 1)。

 

VGG 网络具体实现可以移步 Github 查看: https://github.com/machrisaa/tensorflow-vgg

 

由于 VGG-16 网络参数较多,大概有 1.38 亿个参数,所以一般情况下会加载训练好的分类模型做迁移训练。

 

参考:

如何通俗地理解卷积?
tensorflow-vgg
《TensorFlow:实战Google深度学习框架(第二版)》

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。