【CS231n】斯坦福大学李飞飞视觉识别课程笔记
由官方授权的CS231n课程笔记翻译知乎专栏——智能单元,比较详细地翻译了课程笔记,我这里就是参考和总结。
【CS231n】斯坦福大学李飞飞视觉识别课程笔记(十八):卷积神经网络笔记(上)
卷积神经网络(CNNs / ConvNets)
卷积神经网络和上一章讲的常规神经网络非常相似:它们都是由神经元组成,神经元中有具有学习能力的权重和偏差。每个神经元都得到一些输入数据,进行内积运算后再进行激活函数运算。整个网络依旧是一个可导的评分函数:该函数的输入是原始的图像像素,输出是不同类别的评分。在最后一层(往往是全连接层),网络依旧有一个损失函数(比如SVM或Softmax),并且在神经网络中我们实现的各种技巧和要点依旧适用于卷积神经网络。
那么有哪些地方变化了呢?卷积神经网络的结构基于一个假设,即输入数据是图像,基于该假设,我们就向结构中添加了一些特有的性质。这些特有属性使得前向传播函数实现起来更高效,并且大幅度降低了网络中参数的数量。
结构概述
回顾:常规神经网络。在上一章中,神经网络的输入是一个向量,然后在一系列的隐层中对它做变换。每个隐层都是由若干的神经元组成,每个神经元都与前一层中的所有神经元连接。但是在一个隐层中,神经元相互独立不进行任何连接。最后的全连接层被称为“输出层”,在分类问题中,它输出的值被看做是不同类别的评分值。
常规神经网络对于大尺寸图像效果不尽人意。在 CIFAR-10 中,图像的尺寸是 32x32x3(宽高均为32像素,3个颜色通道),因此,对应的的常规神经网络的第一个隐层中,每一个单独的全连接神经元就有 32x32x3=3072 个权重。这个数量看起来还可以接受,但是很显然这个全连接的结构不适用于更大尺寸的图像。举例说来,一个尺寸为 200x200x3 的图像,会让神经元包含 200x200x3=120,000 个权重值。而网络中肯定不止一个神经元,那么参数的量就会快速增加!显而易见,这种全连接方式效率低下,大量的参数也很快会导致网络过拟合。
神经元的三维排列。卷积神经网络针对输入全部是图像的情况,将结构调整得更加合理,获得了不小的优势。与常规神经网络不同,卷积神经网络的各层中的神经元是3维排列的:宽度、高度和深度(这里的深度指的是激活数据体的第三个维度,而不是整个网络的深度,整个网络的深度指的是网络的层数)。举个例子,CIFAR-10 中的图像是作为卷积神经网络的输入,该数据体的维度是 32x32x3(宽度,高度和深度)。我们将看到,层中的神经元将只与前一层中的一小块区域连接,而不是采取全连接方式。对于用来分类 CIFAR-10 中的图像的卷积网络,其最后的输出层的维度是 1x1x10,因为在卷积神经网络结构的最后部分将会把全尺寸的图像压缩为包含分类评分的一个向量,向量是在深度方向排列的。下面是例子:
——————————————————————————————————————————————————————
左边是一个3层的神经网络。右边是一个卷积神经网络,图例中网络将它的神经元都排列成3个维度(宽、高和深度)。卷积神经网络的每一层都将 3D 的输入数据变化为神经元 3D 的激活数据并输出。在这个例子中,红色的输入层装的是图像,所以它的宽度和高度就是图像的宽度和高度,它的深度是3(代表了红、绿、蓝3种颜色通道)。
——————————————————————————————————————————————————————
卷积神经网络是由层组成的。每一层都有一个简单的 API :用一些含或者不含参数的可导的函数,将输入的 3D 数据变换为 3D 的输出数据。
用来构建卷积网络的各种层
一个简单的卷积神经网络是由各种层按照顺序排列组成,网络中的每个层使用一个可以微分的函数将激活数据从一个层传递到另一个层。卷积神经网络主要由三种类型的层构成:卷积层,汇聚(Pooling)层和全连接层(全连接层和常规神经网络中的一样)。通过将这些层叠加起来,就可以构建一个完整的卷积神经网络。
网络结构例子:这仅仅是个概述,下面会更详解的介绍细节。一个用于 CIFAR-10 图像数据分类的卷积神经网络的结构可以是 [输入层-卷积层-ReLU层-汇聚层-全连接层]。细节如下:
-
输入 [32x32x3] 存有图像的原始像素值,本例中图像宽高均为32,有3个颜色通道。
-
卷积层中,神经元与输入层中的一个局部区域相连,每个神经元都计算自己与输入层相连的小区域与自己权重的内积。卷积层会计算所有神经元的输出。如果我们使用12个滤波器(也叫作核),得到的输出数据体的维度就是 [32x32x12] 。
-
ReLU 层将会逐个元素地进行激活函数操作,比如使用以0为阈值的 max(0,x) 作为激活函数。该层对数据尺寸没有改变,还是 [32x32x12] 。
-
汇聚层在在空间维度(宽度和高度)上进行降采样(downsampling)操作,数据尺寸变为 [16x16x12] 。
-
全连接层将会计算分类评分,数据尺寸变为 [1x1x10],其中10个数字对应的就是 CIFAR-10 中10个类别的分类评分值。正如其名,全连接层与常规神经网络一样,其中每个神经元都与前一层中所有神经元相连接。
由此看来,卷积神经网络一层一层地将图像从原始像素值变换成最终的分类评分值。其中有的层含有参数,有的没有。具体说来,卷积层和全连接层(CONV/FC)对输入执行变换操作的时候,不仅会用到激活函数,还会用到很多参数(神经元的突触权值和偏差)。而 ReLU 层和汇聚层则是进行一个固定不变的函数操作。卷积层和全连接层中的参数会随着梯度下降被训练,这样卷积神经网络计算出的分类评分就能和训练集中的每个图像的标签吻合了。
小结:
- 简单案例中卷积神经网络的结构,就是一系列的层将输入数据变换为输出数据(比如分类评分)。
- 卷积神经网络结构中有几种不同类型的层(目前最流行的有卷积层、全连接层、ReLU层和汇聚层)。
- 每个层的输入是3D数据,然后使用一个可导的函数将其变换为3D的输出数据。
- 有的层有参数,有的没有(卷积层和全连接层有,ReLU层和汇聚层没有)。
- 有的层有额外的超参数,有的没有(卷积层、全连接层和汇聚层有,ReLU层没有)。
——————————————————————————————————————————————————————
一个卷积神经网络的激活输出例子。左边的输入层存有原始图像像素,右边的输出层存有类别分类评分。在处理流程中的每个激活数据体是铺成一列来展示的。因为对 3D 数据作图比较困难,我们就把每个数据体切成层,然后铺成一列显示。最后一层装的是针对不同类别的分类得分,这里只显示了得分最高的5个评分值和对应的类别。完整的网页演示在我们的课程主页。本例中的结构是一个小的 VGG 网络,VGG 网络后面会有讨论。
——————————————————————————————————————————————————————
现在讲解不同的层,层的超参数和连接情况的细节。
卷积层
卷积层是构建卷积神经网络的核心层,它产生了网络中大部分的计算量。
概述和直观介绍:首先讨论的是,在没有大脑和生物意义上的神经元之类的比喻下,卷积层到底在计算什么。卷积层的参数是有一些可学习的滤波器集合构成的。每个滤波器在空间上(宽度和高度)都比较小,但是深度和输入数据一致。举例来说,卷积神经网络第一层的一个典型的滤波器的尺寸可以是5x5x3(宽高都是5像素,深度是3是因为图像应为颜色通道,所以有3的深度)。在前向传播的时候,让每个滤波器都在输入数据的宽度和高度上滑动(更精确地说是卷积),然后计算整个滤波器和输入数据任一处的内积。当滤波器沿着输入数据的宽度和高度滑过后,会生成一个2维的激活图(activation map),激活图给出了在每个空间位置处滤波器的反应。直观地来说,网络会让滤波器学习到当它看到某些类型的视觉特征时就激活,具体的视觉特征可能是某些方位上的边界,或者在第一层上某些颜色的斑点,甚至可以是网络更高层上的蜂巢状或者车轮状图案。
在每个卷积层上,我们会有一整个集合的滤波器(比如12个),每个都会生成一个不同的二维激活图。将这些激活映射在深度方向上层叠起来就生成了输出数据。
以大脑做比喻:如果你喜欢用大脑和生物神经元来做比喻,那么输出的 3D 数据中的每个数据项可以被看做是神经元的一个输出,而该神经元只观察输入数据中的一小部分,并且和空间上左右两边的所有神经元共享参数(因为这些数字都是使用同一个滤波器得到的结果)。现在开始讨论神经元的连接,它们在空间中的排列,以及它们参数共享的模式。
局部连接:在处理图像这样的高维度输入时,让每个神经元都与前一层中的所有神经元进行全连接是不现实的。相反,我们让每个神经元只与输入数据的一个局部区域连接。该连接的空间大小叫做神经元的感受野(receptive field),它的尺寸是一个超参数(其实就是滤波器的空间尺寸)。在深度方向上,这个连接的大小总是和输入量的深度相等。需要再次强调的是,我们对待空间维度(宽和高)与深度维度是不同的:连接在空间(宽高)上是局部的,但是在深度上总是和输入数据的深度一致。
例1:假设输入数据体尺寸为 [32x32x3](比如CIFAR-10的RGB图像),如果感受野(或滤波器尺寸)是 5x5,那么卷积层中的每个神经元会有输入数据体中 [5x5x3] 区域的权重,共 5x5x3=75 个权重(还要加一个偏差参数)。
注意这个连接在深度维度上的大小必须为3,和输入数据体的深度一致。
例2:假设输入数据体的尺寸是 [16x16x20],感受野尺寸是 3x3,那么卷积层中每个神经元和输入数据体就有 3x3x20=180 个连接。再次提示:在空间上连接是局部的(3x3),但是在深度上是和输入数据体一致的(20)。
——————————————————————————————————————————————————————
左边:红色的是输入数据体(比如 CIFAR-10 中的图像),蓝色的部分是第一个卷积层中的神经元。卷积层中的每个神经元都只是与输入数据体的一个局部在空间上相连,但是与输入数据体的所有深度维度全部相连(所有颜色通道)。在深度方向上有多个神经元(本例中5个),它们都接受输入数据的同一块区域(感受野相同)。至于深度列的讨论在下文中有。
右边:神经网络章节中介绍的神经元保持不变,它们还是计算权重和输入的内积,然后进行激活函数运算,只是它们的连接被限制在一个局部空间。
——————————————————————————————————————————————————————
空间排列:上文讲解了卷积层中每个神经元与输入数据体之间的连接方式,但是尚未讨论输出数据体中神经元的数量,以及它们的排列方式。3个超参数控制着输出数据体的尺寸:深度(depth),步长(stride) 和零填充(zero-padding)。下面是对它们的讨论:
-
首先,输出数据体的深度是一个超参数:它和使用的滤波器的数量一致,而每个滤波器在输入数据中寻找一些不同的东西。举例来说,如果第一个卷积层的输入是原始图像,那么在深度维度上的不同神经元将可能被不同方向的边界,或者是颜色斑点激活。我们将这些沿着深度方向排列、感受野相同的神经元集合称为深度列(depth column),也有人使用纤维(fibre) 来称呼它们。
-
其次,在滑动滤波器的时候,必须指定步长。当步长为1,滤波器每次移动1个像素。当步长为2(或者不常用的3,或者更多,这些在实际中很少使用),滤波器滑动时每次移动2个像素。这个操作会让输出数据体在空间上变小。
-
在下文可以看到,有时候将输入数据体用0在边缘处进行填充是很方便的。这个零填充(zero-padding) 的尺寸是一个超参数。零填充有一个良好性质,即可以控制输出数据体的空间尺寸(最常用的是用来保持输入数据体在空间上的尺寸,这样输入和输出的宽高都相等)。
输出数据体在空间上的尺寸可以通过输入数据体尺寸(W),卷积层中神经元的感受野尺寸(F),步长(S)和零填充的数量(P)的函数来计算。(译者注:这里假设输入数组的空间形状是正方形,即高度和宽度相等)输出数据体的空间尺寸为。比如输入是7x7,滤波器是3x3,步长为1,填充为0,那么就能得到一个5x5的输出。如果步长为2,输出就是3x3。下面是例子:
——————————————————————————————————————————————————————
空间排列的图示。在本例中只有一个空间维度(x轴),神经元的感受野尺寸F=3,输入尺寸W=5,零填充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。这些权重是被所有黄色的神经元共享的(参数共享的内容看下文相关内容)。
——————————————————————————————————————————————————————
使用零填充:在上面左边例子中,注意输入维度是5,输出维度也是5。之所以如此,是因为感受野是3并且使用了1的零填充。如果不使用零填充,则输出数据体的空间维度就只有3,因为这就是滤波器整齐滑过并覆盖原始数据需要的数目。一般说来,当步长S=1时,零填充的值是,这样就能保证输入和输出数据体有相同的空间尺寸。这样做非常常见,在介绍卷积神经网络的结构的时候我们会详细讨论其原因。
步长的限制:注意这些空间排列的超参数之间是相互限制的。举例说来,当输入尺寸W=10,不使用零填充则P=0,滤波器尺寸F=3,这样步长S=2就行不通,因为(W-F+2P)/S+1=(10-3+0)/2+1=4.5,结果不是整数,这就是说神经元不能整齐对称地滑过输入数据体。因此,这些超参数的设定就被认为是无效的,一个卷积神经网络库可能会报出一个错误,或者修改零填充值来让设置合理,或者修改输入数据体尺寸来让设置合理,或者其他什么措施。在后面的卷积神经网络结构小节中,读者可以看到合理地设置网络的尺寸让所有的维度都能正常工作,这件事可是相当让人头痛的。而使用零填充和遵守其他一些设计策略将会有效解决这个问题。
真实案例:Krizhevsky构架赢得了2012年的ImageNet挑战,其输入图像的尺寸是 [227x227x3]。在第一个卷积层,神经元使用的感受野尺寸F=11,步长S=4,不使用零填充P=0。因为(227-11)/4+1=55,卷积层的深度K=96,则卷积层的输出数据体尺寸为 [55x55x96]。55x55x96 个神经元中,每个都和输入数据体中一个尺寸为 [11x11x3] 的区域全连接。在深度列上的96个神经元都是与输入数据体中同一个 [11x11x3] 区域连接,但是权重不同。有一个有趣的细节,在原论文中,说的输入图像尺寸是224x224,这是肯定错误的,因为(224-11)/4+1的结果不是整数。这件事在卷积神经网络的历史上让很多人迷惑,而这个错误到底是怎么发生的没人知道。我的猜测是Alex忘记在论文中指出自己使用了尺寸为3的额外的零填充。
参数共享:在卷积层中使用参数共享是用来控制参数的数量。就用上面的例子,在第一个卷积层就有 55x55x96=290,400个神经元,每个有 11x11x3=364 个参数和1个偏差。将这些合起来就是 290400x364=105,705,600 个参数。单单第一层就有这么多参数,显然这个数目是非常大的。
作一个合理的假设:如果一个特征在计算某个空间位置(x,y)的时候有用,那么它在计算另一个不同位置(x2,y2)的时候也有用。基于这个假设,可以显著地减少参数数量。换言之,就是将深度维度上一个单独的2维切片看做深度切片(depth slice),比如一个数据体尺寸为 [55x55x96] 的就有96个深度切片,每个尺寸为 [55x55]。在每个深度切片上的神经元都使用同样的权重和偏差。在这样的参数共享下,例子中的第一个卷积层就只有96个不同的权重集了,一个权重集对应一个深度切片,共有 96x11x11x3=34,848 个不同的权重,或34,944个参数(+96个偏差)。在每个深度切片中的 55x55 个权重使用的都是同样的参数。在反向传播的时候,都要计算每个神经元对它的权重的梯度,但是需要把同一个深度切片上的所有神经元对权重的梯度累加,这样就得到了对共享权重的梯度。这样,每个切片只更新一个权重集。
注意,如果在一个深度切片中的所有权重都使用同一个权重向量,那么卷积层的前向传播在每个深度切片中可以看做是在计算神经元权重和输入数据体的卷积(这就是“卷积层”名字由来)。这也是为什么总是将这些权重集合称为滤波器(filter)(或卷积核(kernel)),因为它们和输入进行了卷积。
——————————————————————————————————————————————————————
Krizhevsky等学习到的滤波器例子。这96个滤波器的尺寸都是 [11x11x3],在一个深度切片中,每个滤波器都被 55x55 个神经元共享。注意参数共享的假设是有道理的:如果在图像某些地方探测到一个水平的边界是很重要的,那么在其他一些地方也会同样是有用的,这是因为图像结构具有平移不变性。所以在卷积层的输出数据体的 55x55 个不同位置中,就没有必要重新学习去探测一个水平边界了。
——————————————————————————————————————————————————————
注意有时候参数共享假设可能没有意义,特别是当卷积神经网络的输入图像是一些明确的中心结构时候。这时候我们就应该期望在图片的不同位置学习到完全不同的特征。一个具体的例子就是输入图像是人脸,人脸一般都处于图片中心。你可能期望不同的特征,比如眼睛特征或者头发特征可能(也应该)会在图片的不同位置被学习。在这个例子中,通常就放松参数共享的限制,将层称为局部连接层(Locally-Connected Layer)。
Numpy例子:为了让讨论更加的具体,我们用代码来展示上述思路。假设输入数据体是numpy数组。那么:
- 一个位于的深度列(或纤维)将会是
X[x,y,:]
。 - 在深度为处的深度切片,或激活图应该是
X[:,:,d]
。
卷积层例子:假设输入数据体的尺寸X.shape:(11,11,4)
,不使用零填充(P=0),滤波器的尺寸是F=5,步长S=2。那么输出数据体的空间尺寸就是(11-5)/2+1=4,即输出数据体的宽度和高度都是4。那么在输出数据体中的激活映射(称其为)看起来就是下面这样(在这个例子中,只有部分元素被计算):
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.shape: (5,5,4)
,因为滤波器的宽高是5,输入数据量的深度是4。注意在每一个点,计算点积的方式和之前的常规神经网络是一样的。同时,计算内积的时候使用的是同一个权重和偏差(因为参数共享),在宽度方向的数字每次上升2(因为步长为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 (或两个方向上同时)
我们访问的是的深度维度上的第二层(即index1),因为是在计算第二个激活图,所以这次试用的参数集就是了。在上面的例子中,为了简洁略去了卷积层对于输出数组中其他部分的操作。还有,要记得这些卷积操作通常后面接的是 ReLU 层,对激活图中的每个元素做激活函数运算,这里没有显示。
小结: 我们总结一下卷积层的性质:
-
输入数据体的尺寸为
-
4个超参数:
- 滤波器的数量
- 滤波器的空间尺寸
- 步长
- 零填充数量
-
输出数据体的尺寸为 ,其中:
- (宽度和高度的计算方法相同)
-
由于参数共享,每个滤波器包含个权重,卷积层一共有个权重和个偏置。
-
在输出数据体中,第个深度切片(空间尺寸是),用第个滤波器和输入数据进行有效卷积运算的结果(使用步长),最后在加上第个偏差。
对这些超参数,常见的设置是F=3,S=1,P=1。同时设置这些超参数也有一些约定俗成的惯例和经验,可以在下面的卷积神经网络结构章节中查看。
卷积层演示:下面是一个卷积层的运行演示。因为 3D 数据难以可视化,所以所有的数据(输入数据体是蓝色,权重数据体是红色,输出数据体是绿色)都采取将深度切片按照列的方式排列展现。输入数据体的尺寸是,卷积层参数K=2,F=3,S=2,P=1。就是说,有2个滤波器,滤波器的尺寸是,它们的步长是2。因此,输出数据体的空间尺寸是(5-3+2)/2+1=3。注意输入数据体使用了零填充P=1,所以输入数据体外边缘一圈都是0。下面的例子在绿色的输出激活数据上循环演示,展示了其中每个元素都是先通过蓝色的输入数据和红色的滤波器逐元素相乘,然后求其总和,最后加上偏差得来。
——————————————————————————————————————————————————————
译者注:请读者前往斯坦福课程官网查看此演示。
——————————————————————————————————————————————————————
用矩阵乘法实现:卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。卷积层的常用实现方式就是利用这一点,将卷积层的前向传播变成一个巨大的矩阵乘法:
-
输入图像的局部区域被操作拉伸为列。比如,如果输入是 [227x227x3],要与尺寸为11x11x3的滤波器以步长为4进行卷积,就取输入中的 [11x11x3] 数据块,然后将其拉伸为长度为11x11x3=363的列向量。重复进行这一过程,因为步长为4,所以输出的宽高为(227-11)/4+1=55,所以得到操作的输出矩阵的尺寸是 [363x3025],其中每列是拉伸的感受野,共有55x55=3,025个。注意因为感受野之间有重叠,所以输入数据体中的数字在不同的列中可能有重复。
-
卷积层的权重也同样被拉伸成行。举例,如果有96个尺寸为 [11x11x3] 的滤波器,就生成一个矩阵,尺寸为[96x363]。
-
现在卷积的结果和进行一个大矩阵乘是等价的了,能得到每个滤波器和每个感受野间的点积。在我们的例子中,这个操作的输出是 [96x3025],给出了每个滤波器在每个位置的点积输出。
-
结果最后必须被重新变为合理的输出尺寸 [55x55x96]。
这个方法的缺点就是占用内存太多,因为在输入数据体中的某些值在中被复制了多次。但是,其优点是矩阵乘法有非常多的高效实现方式,我们都可以使用(比如常用的BLAS API)。还有,同样的思路可以用在汇聚操作中。
反向传播:卷积操作的反向传播(同时对于数据和权重)还是一个卷积(但是是和空间上翻转的滤波器)。使用一个1维的例子比较容易演示。
1x1卷积:一些论文中使用了1x1的卷积,这个方法最早是在论文Network in Network中出现。人们刚开始看见这个1x1卷积的时候比较困惑,尤其是那些具有信号处理专业背景的人。因为信号是2维的,所以1x1卷积就没有意义。但是,在卷积神经网络中不是这样,因为这里是对3个维度进行操作,滤波器和输入数据体的深度是一样的。比如,如果输入是 [32x32x3],那么1x1卷积就是在高效地进行3维点积(因为输入深度是3个通道)。
扩张卷积:最近一个研究(Fisher Yu和Vladlen Koltun的论文)给卷积层引入了一个新的叫扩张(dilation)的超参数。到目前为止,我们只讨论了卷积层滤波器是连续的情况。但是,让滤波器中元素之间有间隙也是可以的,这就叫做扩张。举例,在某个维度上滤波器w的尺寸是3,那么计算输入x的方式是:w[0]*x[0] + w[1]*x[1] + w[2]*x[2],此时扩张为0。如果扩张为1,那么计算为: w[0]*x[0] + w[1]*x[2] + w[2]*x[4]。换句话说,操作中存在1的间隙。在某些设置中,扩张卷积与正常卷积结合起来非常有用,因为在很少的层数内更快地汇集输入图片的大尺度特征。比如,如果上下重叠2个3x3的卷积层,那么第二个卷积层的神经元的感受野是输入数据体中5x5的区域(可以成这些神经元的有效感受野是5x5)。如果我们对卷积进行扩张,那么这个有效感受野就会迅速增长。
卷积神经网络笔记(上)结束。