您的位置:首页 > 理论基础 > 计算机网络

卷积神经网络(Convolutional Neural Networks,CNNS/ConvNets)

2017-09-21 15:21 495 查看
本文翻译自 Convolutional Neural Networks(CNNs / ConvNets),更多内容请访问:http://cs231n.github.io/

卷积神经网络非常类似于普通的神经网络:它们都是由具有可以学习的权重和偏置的神经元组成。每一个神经元接收一些输入,然后进行点积和可选的非线性运算。而整个网络仍然表示一个可微的得分函数:从原始的图像像素映射到类得分。在最后一层(全连接层)也有损失函数(例如 SVM / Softmax),而且训练普通神经网络的已有技巧 / 技术都可以应用。

然而差别在哪里呢?卷积网络结构具有特殊的图像输入,这个显式的输入假设使得网络结构具有一些特定的性质。这些性质使得前向函数能够高效的实现,并且极大的减少网络的参数个数。

结构概览

回顾:普通神经网络。神经网络接收一个输入(向量),然后通过一系列的隐藏层对输入进行变换。每一个隐藏层都由神经元的集合组成,每一个神经元与前一层的所有神经元连接,而同一层的神经元之间完全独立,也不共享任何连接。最后的全连接层称为“输出层”,在分类情形下它表示类得分。

普通神经网络不能适用所有图像。CIFAR-10 数据集中的图像大小只有 32x32x3 (宽 32,高 32,3 个颜色通道),因此一个普通神经网络的第一个隐藏层的一个全连接神经元的参数个数是 32*32* 3 = 3072。这样的大小看起来好像很容易处理,但显然全连接结构不能扩展到大图像。例如,一张更可观的图像,它的大小为 200x200x3,那么一个神经元的就有 200*200*3 = 120,000 个参数。而且,我们几乎总是需要很多这样的神经元,因此参数合在一起会快速的增长。很明显,这样的全连接是浪费的,而且大量的参数会很快的导致过拟合。

神经元的 3D 方体(?,volume)。卷积神经网络充分的利用了输入是图像这一事实,而且以一种更有意义的方式来限制它们的结构。特别的,不像普通神经网络,卷积网络中的层以 3 维(宽、高、深度,这里的深度不是指整个网络的深度——网络中所有层的个数,而是指激活方体的第三个维度)的方式来排列神经元。例如,CIFAR-10 中的图像是维数为 32x32x3(宽、高、深度)的激活输入方体。我们马上就会看到,卷积网络层中的神经元不是以全连接的方式连接到前一层的所有神经元,而是只与一个小局部相连。而且,依据卷积网络结构,用于 CIFAR-10 的卷积网络的最后的输出层的维数是 1x1x10,就把整张图像简化成一个沿着深度方向的类得分向量。下图是两种网络的一个可视化:



左图:普通的 3 层神经网络。右图:卷积网络,同一层的神经元使用三维(宽、高、深度)来可视化。卷积网络的每一层把神经元的激活的 3D 的输入方体变换为 3D 的输出方体。在这个例子里,红色的输入层保存图像,所以它的宽度和高度是图像的维数,而深度是 3 (红、绿、蓝通道)。

卷积网络由层组成。每一层都有一个 API:它把 3D 的输入方体通过一个可微函数(参数可有可无)变成一个 3D 的输出方体。

组成卷积网络的层

正如我们在前面说的,一个简单的卷积网络是层的一个序列,每一个层通过一个可微函数把一个激活方体变成另一个。我们使用三种主要类型的层:卷积层(Convolutional Layer)池化层(Pooling Layer)全连接层(Fully-Connected Layer)来建立卷积网络结构。我们将这三种类型的层堆叠起来形成整个卷积网络的结构(architecture)[/b]。

结构例子:概览。我们会在以后更详细的描述卷积网络,但一个用于 CIFAR-10 分类的简单卷积网络具有结构 [INPUT - CONV - RELU - POOL - FC]。更详细的:

INPUT [32x32x3] 保留图像的原始像素值,这里的情形是宽 32,高 32,和具有 R,G,B 三个颜色通道的图像。

CONV 层计算与输入局部连接的神经元的输出,每一个神经元计算它们的权重与局部连接的输入单元的权重的点积。如果我们使用 12 个过滤器,则得到 [32x32x12] 的单元。

RELU 层逐元素的使用激活函数 max{0, x}。它保持单元的大小 [32x32x12] 不变。

POOL 层沿着空间维度(宽、高)进行降采样运算,得到如 [16x16x12] 的单元。

FC(fully-connected)层计算类得分,得到的单元大小为 [1x1x10],其中 10 个数中的每一个对应到一个类得分(CIFAR-10 总共 10 个类别)。顾名思义,与普通神经网络一样,这个层的每一个神经元都与前一层的所有神经元连接。

使用这种方法,卷积网络一层一层的把原始图像的原始像素值变换成最后的类得分。注意到一些层包含参数,而另一些层没有。特别的,CONV / FC 层进行的变换不仅是输入方体的激活的函数,也是神经元的参数(权重和偏置)的函数。另一方面,RELU / POOL 层实现固定的函数。CONV / FC 层的参数通过梯度下降算法训练,使得卷积网络计算的类得分和训练集中的每一张图像的类标号一致。

综合来说:

卷积网络结构是把图像方体变成输出方体(如保存类得分)的变换的层列表的最简单的情形

只有几种不同类型的层而已(例如,当前 CONV / FC / RELU / POOL 最流行)

每一层都接收 3D 的输入方体,然后通过一个可微函数把它变成 3D 输出方体

每一层可能有参数,也可能没有(例如,CONV / FC 层有,而 RELU / POOL 层没有)

每一层可能有额外的超参数,也可能没有(例如,CONV / FC / POOL 有,而 RELU 没有)



一个样例卷机网络的激活。初始的方体保存原始图像像素(左边),而最后的方体保存类得分(右边)。沿着处理路径的每一个激活的方体由列来显示。由于很难可视化 3D 方体,所以通过行来展示每一个方体的切片。最后一层的方体保存每一个类的得分,这里我们仅列出了排名前 5 的得分,及它们的标签。访问网站查看完整的 web-based demo。这里展示的是小型VGGNet(后面会讨论)的结构。

现在我们来描述特定层的超参数和它们连接的细节。

卷积层

卷积层(convolutional layer,Conv layer)是卷积网络的核心模块,它对卷积网络的计算性能提升最多。

直观概览。首先来看看没有脑 / 神经元类比的卷积层的计算。它的参数由可学习的过滤器组成。每一个过滤器都是一个小的沿着宽和高方向的空间区块,但也可以扩展到输入方体的整个深度。例如,卷积网络的第一层的一个典型的过滤器具有 5x5x3 的大小(也就是宽和高 5 个像素,而 3 是因为图像有 3 个颜色通道)。在前向传播阶段,我们沿着输入方体的宽和高滑动(更精确的,卷积)每一个过滤器,在每一个位置计算输入和过滤器元素之间的点积。这将产生一个 2 维的激活映射(activation map),它是对过滤器在每一个空间位置的响应。直观的,网络学习的过滤器会在看到某些类型的可见特征(例如,网络第一层的某些方向的边缘,或者某些颜色的斑块,或网络上层的最终的蜂窝似或轮似的模式)的时候激活。在每一个 CONV 层我们有过滤器的一个集合,它们每一个都产生一个分离的 2 维激活映射。沿着深度维数方向把这些激活映射堆叠起来就产生了输出方体。

脑观点。如果从脑 / 神经元的类比来看,3D 输出方体的每一个元素都可以解释成神经元的输出,这些神经元只依赖输入的一个小区域,而且它们与左右空间的神经元共享参数(因为它们都使用相同的过滤器)。现在我们来阐述神经元连接的细节,它们的空间排列,以及它们的参数共享方式。

局部连接。当我们处理高维输入(如图像)的时候,把神经元与它前一层的所有神经元连接是不现实的。实际上,我们只把它和前一层的一个小区域相连。这个连接的空间范围是称为神经元的感受野(receptive field)的一个超参数,它等价于过滤器的大小。沿着深度轴的连接范围总是等于输入方体的深度。需要再次强调的是我们在处理空间维度(宽和高)和深度维度的时候是不对称的:连接在沿着输入方体的空间(宽和高)方向是局部的,而在深度方向则总是等于整个深度。

例1。假设输入方体大小为 [32x32x3](例如一张 RGB CIFAR-10 图像)。如果感受野(或过滤器大小)为 5x5,则卷积层的每一个神经元连接到输入方体的 [5x5x3] 的区域,总共有 5*5*3 = 75 个权重(再加 1 个偏置参数)。注意,沿着深度方向的连接范围必须是 3,因为它就是输入方体的深度。

例2。假设输入方体的大小是 [16x16x20]。则使用 3x3 大小的感受野,卷积层的每一个神经元与输入方体有 3*3*20 = 180 个连接。再次注意,连接在空间上是局部的(如 3x3),但在输入深度上是整体的。



左图:一个样例输入方体(红色,例如一张 32x32x3 的 CIFAR-10 图像),和一个样例第一个卷积层的神经元方体。卷积层的每一个神经元在空间上都连接到输入方体的一个局部,但要连接整个深度(例如,所有的颜色通道)。注意,沿着深度有多个神经元(如图片上有 5 个),每一个都连接到输入的相同区域(见下文 depth column 的讨论)。右图:与普通神经网络的神经元一样:它们仍然要计算权重与输入的点积,然后再进行非线性激活,只是它们的连接被限制到局部空间。

空间排列。我们已经解释了卷积层的每一个神经元与输入方体的连接,但我们还没有讨论输出方体中神经元的个数以及它们的排列方式。有三个超参数控制着输出方体的大小:深度(depth),步幅(stride)0-填充(zero-padding)。我们分别讨论如下:

首先,输出方体的深度是一个超参数:它对应到我们使用的过滤器的个数,每一个过滤器学习输入的不同特征。例如,第一个卷积层把原始图像作为输入,则沿着深度维数的不同神经元会在不同方向边缘或颜色块出现时激活。我们把连接到输入的相同区域的神经元集合称为 depth column(某些人更倾向于术语 fibre)(原文:We will refer to a set of neuros that are all looking at the same region of the input as a depth column (some people also prefer the term fibre))。

其次,我们必须指定过滤器滑动的步幅。当步幅为 1 时,过滤器一次移动一个像素。当步幅为 2(或者,不同寻常的 3 或更大,这些实际很少使用) 时,过滤器一次跳动两个像素。这将会产生在空间上更小的输出方体。

我们将在后面看到,往输入方体的边界填充 0 有时是方便的。这种 0-填充的大小是一个超参数。0-填充的一个非常好的特性是它可以让我们控制输出方体的空间大小(大部分情况,如我们将看到的,我们使用它来保持输出方体与输入方体相同的空间大小,即具有相同的宽和高)。

输出方体的大小可以通过输入方体的大小(W),卷积层神经元的感受野的大小(F),使用的过滤器的步幅(S),以及边界 0-填充的量(P)的一个函数来计算。你可以确认一下,计算输出神经元个数的正确公式是 (W - F + 2 P) / S + 1。例如,对于 7x7 的输入,步幅为 1 的 3x3 过滤器,以及 0 个填充,我们得到 5x5 的输出。如果步幅为 2,则得到 3x3 的输出。让我们来看一个更图形化的例子:



空间排列的说明。在这个例子中,空间维数只有一个(x-轴),一个神经元的感受野的大小为 F = 3,输入大小为 W = 5,0-填充个数 P = 1。左图:步幅 S = 1,从而输出大小为 (5 - 3 + 2) / 1 + 1 = 5。右图:步幅 S = 2,输出大小为 (5 - 3 + 2) / 2 + 1 = 3。注意,步幅 S = 3 不能用,因为它不匹配方体的大小。用方程的术语来说,这可由 (5 - 3 + 2) = 4 不被 3 整除来确定。

这个例子中,神经元的权重是 [1, 0, -1](图中最右侧),偏置是 0。这些权重被所有黄色的神经元共享(见下文参数共享)。

使用 0-填充。注意到在上面例子的左图,输入和输出的维数都是 5。这之所以成立,是因为感受野是 3 而我们使用了 1 的 0-填充。如果没有使用 0-填充,则输出的空间维数只有 3。一般的,如果步幅 S = 1,则设置 0-填充 P = (F - 1) / 2 就能确保输入方体和输出方体具有相同的空间维数。使用这种方式的 0-填充非常常见,我们在进一步讲卷积结构的时候会讨论这样做的充足理由。

步幅的限制。再次注意到空间排列超参数是两两相互制约的。例如,当输入大小是 W = 10 时,如果没有 0-填充 P = 0,并且过滤器的大小 F = 3,则不能使用步幅 S = 2,因为 (W - F + 2 P) / S + 1 = (10 - 3 + 0) / 2 + 1 = 4.5,也就是说,不是一个整数,这意味着神经元不是整齐和对称的与输入做卷积。因此,超参数这样设置是无效的,而且一个卷积库(ConvNet library)会抛出一个例外,或者使用 0 的 0-填充,或者截断输入,或其它方式来匹配这些超参数。我们将在卷积网络结构那一节看到,合适的设置卷积网络的大小使得所有的维数都匹配确实令人头痛,而这是 0-填充以及其它一些设计指导会帮我们显著缓解的。

现实例子。Krizhevsky 等人的结构赢得了 2012 年的 ImageNet 比赛,它接受的图像大小是 [227x227x3]。在第一个卷积层,神经元的感受野大小 F = 11,步幅 S = 4,0-填充 P = 0。因为 (227 - 11) / 4 + 1 = 55,以及卷积层具有深度 K = 96,所以卷积层的输出方体大小为 [55x55x96]。这个有 55*55*96 个神经元的方体中的每一个神经元都与输入方体的大小为 [11x11x3] 的区域相连。另外,每一个深度列(depth column)中的 96 个神经元都连接到输入方体的相同 [11x11x3] 的区域,当然它们的权重不一样。有趣的是,他们的论文中说输入图像大小是 224x224,这显然是不正确的,因为 (224 - 11) / 4 + 1 并不是一个整数。这在卷积网络的历史中困扰了很多人,而且很少有人知道发生了什么。我们自己最好的猜测是 Alex 使用了 3 个额外像素的 0-填充,而他没有在论文中指出。

参数共享。卷积层中使用参数共享方式来控制参数个数。使用上面现实的例子,我们已经知道在第一个卷积层中有 55*55*96 = 290,400 个神经元,每一个神经元都有 11*11*3 = 363 个权重和 1 个偏置。加起来,在第一个卷积层就有 290400*363 =105,705,600 个参数。显然,这个数非常大。

一个被证明能够极大的减少参数个数的合理的假设是:如果一个特征对某个空间位置 (x,y)的计算是有用的,则它对不同位置 (x2,y2)的计算也是有用的。换句话说,如果记一个深度的二维切片为 depth slice (例如,一个大小为 [55x55x96] 的方体有 96 个 depth slice,每一个大小为 [55x55]),我们将限制每一个 depth slice,使得它的所有神经元都使用相同的权重和偏置。使用这种参数共享模式,我们的例子的第一个卷积层只有 96 个权重集(每一个 depth slice 对应一个),因此共有 96*11*11*3 = 34,848 个权重,或者 34,944 个参数(+96 个偏置)。也就是,每一个 depth slice 中的所有 55*55 个神经元都使用相同的参数。在实际的反向传播过程中,方体中的每一个神经元都会对参数求梯度,但每个 depth slice 中的这些梯度都会加到一起,而且权重也只更新一次。

注意到,如果一个 depth slice 中的所有神经元都使用相同的权重向量,则卷积层在前向传播过程时,每个 depth slice 计算神经元的权重与输入方体的卷积(因此得名:卷积层)。这也是为什么通常把权重集称为过滤器(filter)(或核,kernel),它与输入进行卷积。



学到的过滤器的例子(Krizhevsky et al)。96 个过滤器中的每一个都具有大小 [11x11x3],而且都被每一个 depth slice 中的 55*55 个神经元共享。注意到参数共享这个假设是合理的:如果在图像中某个位置检测到一个水平边缘是重要的,则由于图像的平移不变结构,直观的说,在其他位置也是有用的。从而,不需要在卷积层的输出方体中的 55*55 个位置中的每一个都重新去学习检测水平边缘。

注意,有时参数共享假设可能没有意义。特别是如果卷积网络的输入图像具有特定的中心结构这种情况更是如此,在这里,我们期望,比如,学到图像的一侧相对于另一侧完全不同的特征。一个实际例子是,当输入是居于图像中心的人脸时。你可能期望在不同的空间位置学习到不同的眼睛或头发特征。这种情况通常会放松参数共享模式,取而代之的是局部连接层(Locally-Connected Layer)

Numpy 例子。为了让讨论变得更精确,我们把相同的想法用代码和特定的例子来表达。假设输入方体是一个 numpy 数组
X
。则:

在位置
(x, y)
中 depth column(或 fibre)是激活 X[x, y, :]。

在深度
d
的 depth slice,或等价的,activation map 是激活 X[:, :, d]。

卷积层例子。假设输入方体具有形状
X.shape: (11, 11, 4)
。再假设我们不使用 0-填充(P = 0),过滤器大小 F = 5,步幅 S = 2。则输出方体的的空间大小是 (11 - 5) / 2 + 1 = 4,即宽和高是 4。输出方体的激活映射(称它为
V
)看起来如下(在这个例子中只有其中的一些元素被计算):

V[0, 0, 0] = np.sum(X[:5, :5, :] * W0) + b0


V[1, 0, 0] = np.sum(X[2:7, :5, :] * W0) + b0


V[2, 0, 0] = np.sum(X[4:9, :5, :] * W0) + b0


V[3, 0, 0] = np.sum(X[6:11, :5, :] * W0) + b0


记住在 numpy 中, 上面的运算符 * 指数组中的元素级乘法。同时要注意神经元的权重向量是
W0
,而
b0
是偏置。这里,
W0
假设具有形状
W0.shape: (5, 5, 4)
,因为过滤器大小是 5 以及输入方体的深度是 4。而且在每一个点,我们像通常的神经网络一样计算点积。另外,我们使用相同的权重和偏置(由于参数共享),同时沿着宽度方向的维数以 2 的步长(也就是步幅)增长。要构造输出方体的第二个激活映射,我们有:

V[0, 0, 1] = np.sum(X[:5, :5, :] * W1) + b1


V[1, 0, 1] = np.sum(X[2:7, :5, :] * W1) + b1


V[2, 0, 1] = np.sum(X[4:9, :5, :] * W1) + b1


V[3, 0, 1] = np.sum(X[6:11, :5, :] * W1) + b1


V[0, 1, 1] = np.sum(X[:5, 2:7, :] * W1) + b1
(沿着 y 方向的例子)

V[2, 3, 1] = np.sum(X[4:9, 6:11, :] * W1) + b1
(沿着两个方向的例子)

这里我们看到,
V
的深度维数的下标是 1,因为我们计算的是第二个激活映射,而且现在使用的参数(
W1
)也不同。为了简单起见,卷积层的输出数组
V
的其它部分并没有全部计算。另外,这些激活映射之后通常都会使用像 ReLU 这样的激活函数,虽然这里并没有演示。

总结。总而言之,卷积层:

接收一个大小为 W1×H1×D1 方体

需要 4 个超参数:

滤波器个数 K,

滤波器大小 F,

步幅 S,

0-填充数量 P

产生一个大小为 W2×H2×D2 的方体:

W2=(W1−F+2P)/S+1

H2=(H1−F+2P)/S+1 (宽和高对称的计算)

D2=K

使用参数共享,每个滤波器引入 F⋅F⋅D1 个权重,总共 (F⋅F⋅D1)⋅K 个权重和 K 个偏置

输出方体中,第 d 个 depth slice(具有大小W2×H2)是第 d 个滤波器与输入方体进行步幅为S的有效卷积(valid convolution),再使用第d个偏置的结果

常用的超参数设置是 F=3,S=1,P=1。然而也有其它常见约定和经验法则来设置超参数。见下面的卷积网络结构一节。

卷积演示。下面是一个卷积层的演示动图。由于 3D 方体很难可视化,所有方体(输入方体(蓝色),权重方体(红色),输出方体(绿色))使用 depth slice 的按行堆叠来进行可视化。输入方体大小为 W1=5,H1=5,D1=3,卷积层参数为K=2,F=3,S=2,P=1。也就是说,我们有两个大小为 3×3 的过滤器,并且步幅为 2。因此,输出方体的空间大小为 (5 - 3 + 2) / 2 + 1 = 3。另外,因为对输入方体使用了 P=1 的填充,使得输入方体的外边界为 0。下面的可视化是对输出激活(绿色)的重复,显示每一个元素都是通过高亮输入(蓝色)和过滤器(红色)的元素相乘,然后相加,再加上偏置得来的。

动图链接

作为矩阵乘法实现。注意,卷积运算本质上是过滤器与输入的局部区域之间的点积。卷积层的常用实现模式充分利用了这一点,把卷积层的前向传播格式化为大的矩阵乘法,如下:

把输入图像的局部区域通过通常称为 im2col 的运算扩展成列向量。例如,如果输入大小为 [227x227x3],它与 11x11x3 的过滤器进行步幅为 4 的卷积,则我们把输入的 [11x11x3] 的像素块展开为大小为 11*11*3 = 363 的列向量。重复这个过程,得到宽和高都是 (227 - 11) / 4 + 1 = 55 个位置,从而 im2col 的输出矩阵
X_col
大小为 [363x3025],其中每一列都是展开的感受野,总共有 55*55 = 3025 个。注意到,因为感受野是重合的,输入方体中的每一个数会重复出现在不同的列中。

卷积层的权重类似的展开成行向量。例如,如果有 96 个大小为 [11x11x3] 的过滤器,则得到大小为 [96x363] 的矩阵
W_row


现在卷积的结果就等价于进行矩阵乘法
np.dot(W_row, X_col)
,这是每一个过滤器与每一个感受野进行点积。在我们的例子中,这个运算的输出大小为 [96x3025],给出了每一个滤波器在每一个位置的点积。

最后的结果再次重现塑形成合适的输出维数 [55x55x96]。

这个方法的缺点是它要使用大量的内存,因为输入方体电话中一些值会在
X_col
中重复多次。然而,它的好处是存在很多的我们可以充分利用的矩阵乘法的高效实现(比如,经常使用的 BLAS API)。另外,相同的 im2col 的思想也可以用于进行池化运算(我们将在后面讨论)。

反向传播。(对于数据和权重的)卷积运算的反向过程也是卷积(只不过过滤器是空间翻转的)。这可以使用非常简单的 1 维例子来推导(这里略过)。

1x1 卷积。在 Network in Network 首次提出后,一些论文也使用 1x1 的卷积。一些具有信号处理背景的人第一次看到 1x1 卷积的时候会很困惑,因为正常信号都是 2 维的,因此 1x1 的卷积没有意义(它只是进行逐点的伸缩)。然而,在卷积网络中情况却不是这样,因为我们现在是在 3 维输入方体进行操作,而且过滤器永远在输入方体的整个深度上扩展。例如,如果输入大小是 [32x32x3],则使用 1x1 卷积进行 3 维点积是有效的(因为输入深度有 3 个通道)。

Dilated convolutions。最近的发展(例如,Fisher Yu 和 Vladlen Koltun 的论文)往卷积层上又引入了一个称为 dilation 的超参数。到目前为止,我们讨论的卷积过滤器都是接触的,然而,也可以有单元之间是分离的过滤器。例如,1 维的时候,一个大小为 3 的过滤器在输入 x 上进行计算:
w[0]*x[0]+w[1]*x[1]+w[2]*x[2]
,这是 dilation 为 0 的情况。对于 dilation 为 1 的过滤器,则计算
w[0]*x[0]+w[1]*x[2]+w[2]*x[4]
。换句话说,权重使用的时候是有间隔的。当与 0-dilated 的过滤器进行联合使用的时候,这变得非常有用,因为它只用很少的层就能高度的融合输入的空间信息。例如,如果你堆叠两个 3x3 的卷积层,你可以确认第二个层的神经元是输入的的 5x5 的图块的函数(我们说这些神经元的有效感受野是 5x5)。假如我们使用 dilated convolution,则这些有效感受野会更快的增长。

池化层

在卷积结构中通常会在相继的卷积层之间周期性的插入池化层(Pooling Layer),它的作用是逐渐的减少表示的空间大小,从而减少网络的参数的个数核计算量,进而控制过拟合。池化层在输入的每个 depth slice 上使用 MAX 操作独立的计算,改变它的空间大小。池化层最常用的形式是在输入的每个 depth slice 上使用 2x2 大小的过滤器和步幅为 2 的降采样,忽略其中 75% 的激活。每一个 MAX 操作都是对 4 个数(depth slice 中的 2x2 小区域)取最大。另外,网络的深度保持不变。一般的,池化层:

接收大小为W1×H1×D1 的方体

需要 2 个超参数:

空间范围 F

步幅S

产生大小为 W2×H2×D2 的方体,其中:

W2=(W1−F)/S+1

H2=(H1−F)/S+1

D2=D1

不引入新的参数,因为它是计算输入的固定函数

池化层通常不使用 0-填充

值得注意的是实际中常用的最大池化层(max pooling layer)只有两种:F=3,S=2的池化层(也称为重叠池化)和更常见的 F=2,S=2 的池化层。具有更大感受野的池化大小是有害的。

一般池化。除了最大池化,池化单元还可以进行其它函数运算,比如平均池化(average pooling)和 L2-范数池化(L2-norm pooling)。平均池化在过去较为常用,因为相比之下,最大池化在实际中性能更好。



池化层对输入方体的每一个 depth slice 在空间上进行独立的降采样。左图:在这个例子中,对大小为 [224x224x64] 的输入方体进行过滤器大小为 2 和步幅为 2 的池化,得到大小为 [112x112x64] 的输出方体。这里注意到方体的深度保持不变。右图:最常用的降采样操作是取最大值的最大池化(max pooling),这里显示的是步幅为 2 的最大池化,每一次都对 4 个数(2x2 的小方块)取最大值。

反向传播。对 max(x, y) 的反向传播操作可以简单的解释成只是对正向过程的最大值的输入进行梯度运算。因此,在池化层的正向过程中通常要追踪最大激活(有时也称为开关(switches))的下标,使得反向传播很高效。

不使用池化。很多人不喜欢池化运算,并且认为我们可以不需要它。例如,Striving for Simplicity: The All Convolutional Net 建议忽略池化层而用只有不停重复的卷积层的结构来取代。为了减小表示的大小,他们建议在卷积层中使用大的步幅。不使用池化层对训练好的生成模型,例如变分自编码器(variational autoencodes, VAEs)或生成对抗网络(generative adversarial networks, GANs),也被发现是重要的。现在看起来,未来不使用池化层的结构会很少。

标准化层

在卷积网络结构中使用了很多类型的标准化层(normalization layer),有时是为了实现在生物脑中观察到的抑制模式的意图。然而这些层已经不再受欢迎,因为在实际使用时它们的作用很小(如果有的话)。要了解各种类型的标准化,参考 Alex Krizhevsky 的分析 cuba-convnet library API

全连接层

正如普通神经网络中那样,全连接层(fully-connected layer,FC layer)中的神经元与它前一层的所有激活都先连。因此,这种激活可以通过矩阵乘法,然后加上偏置来计算。(请参考这一系列的 Neural Network 获取更多信息)

全连接层转化为卷积层

全连接层(FC layer)和卷积层(CONV layer)仅有的区别是:卷积层中的神经元只连接到输入的局部区域,而且很多的神经元共享参数。然而,这两种层中的神经元都要计算点积,因此它们的函数形式是一样的。从而,将全连接层转化为卷积层是可行的:

对任意卷积层,存在一个全连接层能实现相同的前向函数,对应的权重矩阵是一个只在特定块非零(由于局部连接)的大矩阵,而且很多块的权重相等(由于参数共享)。

反过来,任意的全连接层可以转化为卷积层。例如,对于具有 K=4096 个神经元,且与大小为 7×7×512 的输入方体相连的全连接层,可以等价的的表示成 F=7,P=0,S=1,K=4096 的卷积层。换句话说,我们令过滤器的大小恰好等于输入方体的大小,从而得到 1×1×4096 的输出,也就是对输入方体的每一个 depth slice 进行卷积,它的结果与原来全连接层的一样。

FC->CONV 的转化。在这两个转化中,将全连接层转化为卷积层在实际中特别有用。考虑一个卷积网络结构,它以 224x224x3 的图像作为输入,然后使用一系列的卷积层和池化层将图像简化为大小为 7x7x512 的激活方体(在后面我们将要看到的 AlexNet 中,这由 5 个池化层得到,每一个在空间上使用因子为 2 的降采样,使得最后的空间大小为 224/2/2/2/2/2 = 7)。之后,AlexNet 使用两个大小为 4096 的全连接层,最后再接一个具有 1000 个神经元的全连接层来计算类得分。我们可以把这三个全连接层转化为卷积层:

将第一个全连接层用具有大小为 F=7 的过滤器的卷积层替换,把 [7x7x512] 的输入方体变为 [1x1x4096] 的输出方体

将第二个全连接层用具有大小为 F=1 的过滤器的卷积层替换,得到 [1x1x4096] 的输出方体

类似的,替换最后一个全连接层,其中 F=1,最后的输出大小为 [1x1x1000]

每一个这样的转化在实际中都要将全连接层的权重矩阵重新塑形为卷积层的过滤器。这种转化只用一个前向传播过程,在大图像中沿着空间位置滑动的进行原来的卷积,被证明是非常高效的。

例如,如果一张 224x224 的图像给出 [7x7x512] 的输出,也就是简化 32 倍,那么对于一张 384x384 的图像,在转化结构下将给出等价的大小为 [12x12x512] 的方体,因为 384 / 32 = 12。然后再通过我们刚刚由全连接层转化来的卷积层,给出大小为 [6x6x1000] 的最终方体,因为 (12 - 7) / 1 + 1 = 6。注意,对于一幅 384x384 的图像,我们不再得到大小为 [1x1x1000] 的单个类得分向量,而是得到类得分的整个 6x6 的数组。

对 384x384 的图像使用步幅为 32 个像素的 224x224 剪切,再独立的使用原来的卷积网络(含有全连接层),得到的结果和转化来的卷积网络的是一样的。

自然的,转化来的卷积网络只计算一次,比原来的卷积网络计算 36 次要高效很多,因为这 36 次其实可以共享计算。这种技术在实际中经常用来得到更好的性能,例如,通常把图像调整为更大的形状,然后使用转化的卷积网络来计算多个空间位置的类得分,再对这些类得分进行平均。

最后,如果步幅小于 32 个像素,我们怎样能高效的将原来的卷积网络应用到图像?我们可以多次使用前向过程来得到。例如,如果我们想要使用 16 个像素的步幅,那我们可以将两个用转化来的卷积网络得到的方体组合起来:首先是对原始图像的,然后是对沿着宽和高各平移 16 个像素的图像的。

一个用 IPython Notebook 写的 Net Surgery 显示怎样实际的用代码(使用 Caffe)进行转化。

卷积网络结构

我们已经知道卷积网络通常只由三种类型的层组成:卷积层(CONV layer)、池化层(POOL layer,如果没有特别声明,都假设是 MAX pool)和全连接层(fully-connected layer,FC layer)。我们也会显式的写出整流线性单元(RELU)激活函数,它非线性的应用到每个元素。这一节我们讨论怎样将这些层堆叠起来形成整个卷积网络。

层模式

卷积网络最常见的形式是堆叠一些 CONV-RELU 层,之后是 POOL 层,然后重复这个模式直到图像在空间上已经被融合成小的尺寸。在某些节点,还通常要过渡到全连接层。最后的全连接层保存输出,例如类得分。换句话说,最常用的卷积网络遵循下述模式:

INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC


其中
*
表示重复,
POOL?
表示可选的池化层。另外,
N >= 0
(且通常
N <= 3
),
M >= 0
K >= 0
(且通常
K < 3
)。例如,一些经常见到的卷积网络结构有如下模式:

INPUT -> FC
,实现线性分类器。这里
N = M = K = 0


INPUT -> CONV -> RELU -> FC


INPUT -> [CONV -> RELU -> POOL]*2 -> FC -> RELU -> FC
。这里我们看到 POOL 层之间只有一个 CONV 层。

INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]*2 -> FC
。这里 POOL 层之间堆叠了两个 CONV 层。这对大而深的网络来说是个好想法,因为在具有破坏性的池化运算之前堆叠多个 CONV 层能够对输入方体提取出更多复杂的特征。

相比大感受野的卷积层优先选择堆叠小过滤器的卷积层。假设你接连堆叠三个 3x3 的卷积层(当然,它们之间还有非线性运算)。在第一个卷积层上的神经元对输入方体有 3x3 的视野,而第二层上的神经元对第一层也有 3x3 的视野,从而对输入方体有 5x5 的视野。类似的,第三层的神经元对第二层有 3x3 的视野,从而对输入方体有 7x7 的视野。假设我们只用一个具有 7x7 感受野的卷积层替代这三个 3x3 的卷积层。这些神经元虽然在空间范围上对输入方体的感受野也恰好是 7x7,但是却有一些缺点。首先,这些神经元只对输入计算了线性函数,而三个卷积层的堆叠却包含非线性从而使得特征更富有表示性。第二,如果我们假设所有方体的通道数都是 C,那么单个 7x7 卷积层具有 C×(7×7×C)=49C2 个参数,而三个 3x3 卷积层只有 3×(C×(3×3×C))=27C2 个参数。直观上,相比于单个具有大过滤器的卷积层,堆叠多个小过滤器的卷积层能表达输入的更强大的特征,而且参数更少。但这也有实现上的缺点,就是我们需要更多的内存来保存中间卷积层的结果,好让我们能进行反向传播。

背离趋势。值得注意的是,由层进行线性堆叠的约定范式最近已经发生改变,如 Google 的 Inception 结构,以及最近微软亚洲研究院(Microsoft Research Asia)的(state of the art)残差网络,这两个网络(细节见下面案例研究一节)都具有错综复杂的连接结构。

实践:在 ImageNet 上使用什么网络最好。如果你正在为网络结构设计而苦苦思索,那么很高兴告诉你 90% 及以上的应用是不需要担忧这个问题的。我把这总结为一个观点:“不要做英雄”,与其为一个问题设计自己的结构,不如看看当前哪些网络在 ImageNet 上性能最好,然后下载一个预训练模型再用你自己的数据来精调。你几乎不需要痛苦的训练或设计一个卷积网络。我在 Deep Learning school 也说了这一观点。

层大小模式

到目前为止,我们还没有注意卷积网络中每一层的超参数的常用设置。我们将首先陈述一些结构大小的常用经验法则,然后基于这些法则来做一些分析:

输入层(input layer)(保存图像)应当可以被 2 除很多次。常见的大小包括 32(如 CIFAR-10),64,96(如 STL-10),或者 224(如常用的 ImageNet 卷积网络),384 和 512。

卷积层(conv layer)应该使用小过滤器(比如 3x3,至多 5x5),使用步幅 S=1,而且最重要的是使用 0-填充来确保卷积层不会改变输入的空间维数。也就是说,如果 F=1,则用 P=1 的 0-填充来保持输入的原始大小。当 F=5 时,P=2。对一般的 F,设置 P=(F−1)/2 来保持输入的大小。如果你一定要使用大尺寸的过滤器(比如 7x7 或更大),则通常是在输入图像之后的第一个卷积层使用。

池化层(pool layer)负责对输入的空间维数进行降采样。最常用的设置是使用步幅为 2 (S=2)的 2x2(F=2)感受野的最大池化(max-pooling),这恰好忽略了输入方体激活的 75%(因为对宽和高进行 2 个单位的降采样)。另外一个不常用的设置是使用步幅为 2 的 3x3 感受野。很少见到用于最大池化的感受野的大小超过 3,因为这样的池化太高度聚合而过多损失信息导致性能很差。

减轻头痛程度。上面的模式令人很愉快,因为所有的卷积层都保持输入的空间尺寸,而池化层则只负责在空间上进行降采样。如果换一种方式,我们使用步幅超过 1 或者不对卷积层的输入进行 0-填充,则我们要非常仔细的在卷积网络结构中跟踪输入方体以确保所有的步幅和过滤器起作用,以及卷积网络结构能好的和对称的串联起来。

为什么在卷积层中使用步幅 1。小的步幅在实际中效果更好。而且步幅为 1 使得卷积层只需要改变输入方体的深度,而将所有空间降采样的任务留给池化层。

为什么使用填充。使用 0-填充,除了在上面说的能在经过卷积层之后还保持空间大小的好处之外,它实际上还能提升性能。如果不使用 0-填充而仅进行有效卷积,则在每一个卷积层之后方体的大小都会轻微减小,而且边界信息会很快的消失。

内存限制的让步。在某些情况下(特别是早期的卷积网络结构),在上面所说的经验法则下内存消耗增长得非常快。例如,使用三个 3x3 的卷积层,每个具有 64 个过滤器,对 224x224x3 的图像先进行 1 个像素的填充,然后再进行卷积过滤,得到三个大小为 [224x224x64] 的激活方体。所有这些加在一起将近 1000 万个激活项,或者 72MB 的内存(每张图像,对激活和梯度都是)。因为 GPU 通常都有内存瓶颈,所以必须做出让步。在实际中,人们倾向于只对网络的第一个卷积层让步。比如,一个让步是在第一个卷积层使用步幅为 2 的 7x7 的过滤器(如将要看到的 ZFNet)。另一个例子是 AlexNet 使用步幅为 4 的 11x11 的过滤器。

案例研究

在卷积网络领域,有很多的结构都有命名。最常见的是:

LeNet。在上世纪九十年代,Yann LeCun 首次使得卷积网络获得成功应用。其中,最有名的结构是 LeNet,它用于读取邮政编码、数字等。

AlexNet。在计算机视觉领域,第一个最受欢迎的卷积网络是 AlexNet,它由 Alex Krizhevsky,IIya Sutskever 和 Geoff Hinton 建立。AlexNet 在 2012 年的 ImageNet ILSVRC challenge 上提交结果,表现远超第二名(相比第二名的 top 5 错误率 26%,它只有 16%)。这个网络的结构非常类似 LeNet,只是它更深、更大,而且有多个卷积层堆叠在一起(此前,通常一个卷积层之后紧跟着池化层)。

ZFNet。2013 年的 ILSVRC 由 Matthew Zeiler 和 Rob Fergus 提出的卷积网络获得冠军,即现在有名的 ZFNet(Zeiler & Fergus Net 的缩写)。它是由调整 AlexNet 的超参数改善而来,特别是扩大了 AlexNet 的中间卷积层大小,以及减小了第一个卷积层的步幅和过滤器大小。

GoogLeNet。2014 年的 ILSVRC 的冠军是由来源于谷歌的 Szegedy et al. 提出的卷积网络。它的主要贡献是发展了 Inception Module,可以极大的减少网络的参数个数(4M 相比于 AlexNet 的 60 M)。而且,在卷积网络顶层用平均池化层(Average Polling)替换了全连接层,消减了大量的无关紧要的参数。GoogLeNet 有很多的后续版本,最近的版本是 Inception-v4

VGGNet。2014 年的 ILSVRC 的亚军是 VGGNet,由 Karen Simonyan 和 Andrew Zisserman 提出。它的主要贡献是证明网络深度是好的性能的关键因素。他们最后最好的网络包含 16 个 CONV / FC 层,一个从始至终只进行 3x3 的卷积和 2x2 的池化的异常齐次的结构。他们的 pretrained model 可以公开下载(使用 Caffe 编写)。VGGNet 的一个缺点是:它的计算代价很高昂,使用大量内存和参数(140M)。大量的参数来源于第一个全连接层,而且现在已经知道这些全连接层就算被移除也不会让性能下降,但却可以显著的减少参数个数。

ResNet残差网络(Residual Network,ResNet)由何恺明(Kaiming He)等人建立,它是 ILSVRC 2015 年的冠军。它使用了特殊的 skip connections 和大量的应用了 batch normalization。网络结构的最后也没有使用全连接层。读者可以参考恺明的报告(视频幻灯片),最近一些实验用 Torch 复现了他们的结果。到目前为止,ResNet 是最好的(the state of the art)卷积神经网络,也是实际使用卷积网络的默认选择(截止到 2016 年 5 月 10 日)。特别的,最近的发展 Kaiming He et al. Identity Mappings in Deep Residual Networks(发表于 2016 年 4 月) 调整了原来的网络结构。

VGGNet细节。作为一个案例研究,让我们更详细的来分解一下 VGGNet。整个 VGGNet 由进行步幅为 1,1 个单位填充的 3x3 卷积的卷积层,和进行步幅为 2(没有填充)的 2x2 最大池化的池化层组成。我们可以把处理过程的每一步的表示的大小写出来,也可以追踪表示大小和参数个数:

INPUT: [224x224x3]        memory:  224*224*3=150K   weights: 0
CONV3-64: [224x224x64]  memory:  224*224*64=3.2M   weights: (3*3*3)*64 = 1,728
CONV3-64: [224x224x64]  memory:  224*224*64=3.2M   weights: (3*3*64)*64 = 36,864
POOL2: [112x112x64]  memory:  112*112*64=800K   weights: 0
CONV3-128: [112x112x128]  memory:  112*112*128=1.6M   weights: (3*3*64)*128 = 73,728
CONV3-128: [112x112x128]  memory:  112*112*128=1.6M   weights: (3*3*128)*128 = 147,456
POOL2: [56x56x128]  memory:  56*56*128=400K   weights: 0
CONV3-256: [56x56x256]  memory:  56*56*256=800K   weights: (3*3*128)*256 = 294,912
CONV3-256: [56x56x256]  memory:  56*56*256=800K   weights: (3*3*256)*256 = 589,824
CONV3-256: [56x56x256]  memory:  56*56*256=800K   weights: (3*3*256)*256 = 589,824
POOL2: [28x28x256]  memory:  28*28*256=200K   weights: 0
CONV3-512: [28x28x512]  memory:  28*28*512=400K   weights: (3*3*256)*512 = 1,179,648
CONV3-512: [28x28x512]  memory:  28*28*512=400K   weights: (3*3*512)*512 = 2,359,296
CONV3-512: [28x28x512]  memory:  28*28*512=400K   weights: (3*3*512)*512 = 2,359,296
POOL2: [14x14x512]  memory:  14*14*512=100K   weights: 0
CONV3-512: [14x14x512]  memory:  14*14*512=100K   weights: (3*3*512)*512 = 2,359,296
CONV3-512: [14x14x512]  memory:  14*14*512=100K   weights: (3*3*512)*512 = 2,359,296
CONV3-512: [14x14x512]  memory:  14*14*512=100K   weights: (3*3*512)*512 = 2,359,296
POOL2: [7x7x512]  memory:  7*7*512=25K  weights: 0
FC: [1x1x4096]  memory:  4096  weights: 7*7*512*4096 = 102,760,448
FC: [1x1x4096]  memory:  4096  weights: 4096*4096 = 16,777,216
FC: [1x1x1000]  memory:  1000 weights: 4096*1000 = 4,096,000

TOTAL memory: 24M * 4 bytes ~= 93MB / image (only forward! ~*2 for bwd)
TOTAL params: 138M parameters


注意到,与通常的卷积网络一样,大量的内存(和计算时间)都使用在早期的卷积层,而大部分的参数都来源于最后的全连接层。在这个特殊案例下,第一个全连接层包含整个网络的 140M 个参数中的 100M 个。

计算考量

在构造卷积网络结构的时候,最大的瓶颈是内存瓶颈。很多现代的 GPU 都有 3 / 4 / 6 GB 的内存限制,就算是最好的 GPU 也只有大约 12 GB 的内存。有三个主要的内存占用项:

来源于中间方体大小:它们是卷积网络的每一层的激活(activations)的原始数值,以及它们的梯度(大小相等)。通常大量的激活都集中在卷积网络的浅层(也就是前面的卷积层)。它们需要保留,因为在反向传播的时候要用到,当然在测试阶段,卷积网络聪明的实现是只保存任一层当前的激活而忽略前面层的激活,这在原则上可以大量的减少内存。

来源于参数大小:它们是网络参数(parameters)的数值,反向传播时的梯度,和通常还包括使用 momentum,Adagrad,或者 RMSProp 优化时的缓存。因此,保存参数向量的内存必须要乘以至少是 3 或者更大的因子。

每一个卷积网络的实现都要维护各种各样(miscellaneous)的内存,比如图像数据批量,可能是它们的增强版本等等。

一旦你对值(activations,gradients,misc)的总个数有一个大概的估计,这个数应当转化为多少 GB。取值的个数,乘以 4 就得到原始 bytes 数(因为每一个浮点数是 4 bytes,如果是双精度浮点数则是 8 bytes),然后除以 1024 多次来得到多少 KB,MB,GB 的内存。如果你的网络与内存不匹配,一个让它匹配的常用启发式方法是减小批量大小,因为大量的内存通常由激活消耗。

其它资源

与实现有关的其它资源:

卷积网络性能的 Soumith 基准

ConvNetJS CIFAR-10 demo 允许你在网页上设置卷积网络结构,查看实时计算的结果

Caffe,一个流行的卷积网络库

State of the art ResNets in Torch7
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐