cs231n 课程学习 四——卷积神经网络
一 从神经网络开始说起
神经网络介绍
神经网络由多层运算组合而成,在前边我们介绍了线性分类器,其都是通过一次点积运算之后得到分数,随后通过不同的损失函数得到损失值,最后最小化损失函数的值进行求解。而在神经网络中,我们将单次的点积运算扩充成了多层运算,并命名为两层神经网络以及三层神经网络
两层神经网络
f=W2max(0,W1x) f=W_2\max{(0,W_1x)} f=W2max(0,W1x)

其中,输入层为我们提供 xxx,隐藏层包括一次点积运算 W1xW_1xW1x 以及激活函数,输出层包括一次点积运算 W2x′W_2x'W2x′。两层神经网络拥有一个隐藏层。
三层神经网络
f=W3max(0,W2max(0,W1x)) f=W_3\max{(0,W_2\max{(0,W_1x)})} f=W3max(0,W2max(0,W1x))

三层神经网络包括两个隐藏层,每个隐藏层包括一次点积运算以及激活函数。
激活函数
我们拥有各种各样的激活函数,其主要用于设定一个阈值,使得超过该阈值函数拥有较大值,小于该阈值拥有较小值,增加数据的可区分性。通常有如下几种激活函数
损失函数以及求偏导
神经网络的输出层之后我们同样可以采用损失函数来衡量神经网络的性能,并以此为依据实施梯度下降法,对参数进行修改。我们可以通过如下代码简单的实现一个两层神经网络
import numpy as np
import numpy.random as randn
N, D_in, H, D_out = 64, 1000, 100, 10
x, y = randn(N, D_in), randn(N, D_out)
w1, w2 = randn(D_in, H), randn(H, D_out)
for t in range(2000):
# hidden layer, apply sigmoid activated function
h = 1 / (1 + np.exp(-x.dot(w1)))
# output layer, compute the output
y_perd = h.dot(w2)
# use the mse loss
loss = np.square(y_pred - y).sum()
print(t, loss)
# backpropagation, compute the gradient
grad_y_pred = 2 * (y_pred - y)
grad_w2 = h.T.dot(grad_y_pred) # here we use the conclusion in note 3
grad_h = grad_y_pred.dot(w2.T)
grad_w1 = x.T.dot(grad_h * h * (1 - h))
w1 -= 1e-4 * grad_w1
w2 -= 1e-4 * grad_w2
二 卷积神经网络(ConvNet)
卷积神经网络与普通的神经网络相似,都是由包含可学习的权重的神经元组成,每个神经元接受输入,执行点积运算并通过激活函数。整个网络实现了一个从图像像素集合到类别预测分数的映射,在最后一层都包括一个损失函数。
但是卷积神经网络默认输入都是图片,并对该特性进行了特定的优化,能够显著减少网络的参数并提高效率。传统的神经网络仅在输入层就有 W×H×CW\times H\times CW×H×C 个神经元,如果一幅图片大小为 200×200×3200\times200\times3200×200×3,则我们仅在第一个隐藏层就将拥有 120000120000120000 个权重参数。过多的权重将导致过拟合以及运算效率低下。
在卷积神经网络中,每一层的神经元都呈现出3D的架构,包括宽度、高度和深度。例如对于图片来说,其属于输入层,其3D维度为 W×H×CW\times H\times CW×H×C。每一层的神经元只会与其前一层的一小片区域相关联(注意在普通神经网络中,每个神经元都与上一层的所有神经元全相联)。最后的输出层大小为 1×1×K1\times1\times K1×1×K,KKK 表示总共的类别数。
综上,卷积网络的每一层都被组织成3D的形式,并且每一层都接受一个 3D 的输入,并产生一个 3D 的输出。我们通常使用三个主要类型的层来搭建卷积网络:卷积层、池化层和全相联层。例如我们可以搭建一个简单的卷积网络实现分类,其架构为
输入层 -》 卷积层 -》 RELU 层 -》池化层 -》 全相联层
-------------------------------------------------
INPUT -> CONV -> RELUE -> POOLING -> FC
- INPUT:32×32×332\times32\times332×32×3包括图片的原始像素值
- CONV:这一层每一个神经元与输入的一小部分区域相连,计算点积并输出。输出层数取决于我们选取的过滤器(filter)的数目
- RELU:对上一层结果施加一个简单的激活函数,输出与输入维度相同
- POOL:实施欠采样,降低宽度和高度,输出结果可以为 16×16×1216\times16\times1216×16×12
- FC:全相联层,与普通神经网络一样,输出 1×1×101\times1\times101×1×10
简易 VGG 网络举例
卷积层(Convolutional Layer)
基本内容
卷积层包括一个过滤器的集合。
首先考虑一个过滤器。过滤器的宽和高可用自己决定,但是深度必须和输入数据的深度一样(数据都是3维的)。比如来自上一层的输入是 32×32×332\times32\times332×32×3,则过滤器的大小可用为 3×3×33\times3\times33×3×3,也可以为 5×5×35\times5\times35×5×3。过滤器也是一个三维矩阵,与上一层输入对应区域大小做卷积运算,计算出一个标量。
过滤器的宽和高我们称为接收域(receptive field)
将目前我们的过滤器,沿着上一层输出的平面上进行滑动,每滑动一次,就会产生一个标量,这样我们就将产生一个由标量组成的新的二维平面。
此时我们将引入三个新的超参数,间距(stride)、零边宽度(zero-padding)和深度(depth)
- stride:决定了过滤器在上一层输出平面上滑行的间隔。在上图中,我们滑行的间隔与过滤器宽度相同,为 5,一般极少采用。一般都会采用间隔为 1或者间隔为 2,即过滤器从输入数据的一角开始,每次左移一位,计算一次,当前行计算完毕之后再向下移动一位。直到遍历完整个图形。
- zero-padding:不难发现,如果采用如上的求值方式,最终我们的输出平面的大小将小于输入平面的大小。初始平面为 32×32×332\times32\times332×32×3,过滤器大小为 5×5×35\times 5\times35×5×3如果我们按照
stride=1
的方式移动过滤器,则最后的输出平面大小为 28×28×128\times28\times128×28×1。因此需要补 0 边,如果设置zero-padding=2
,则表示在输入平面的上下左右都补上宽度为 222,深度为 333 的 000,经过过滤器之后输出平面大小为 32×32×132\times32\times132×32×1,同输入保持一致 - depth:该参数表示我们采用多少过滤器,从而决定了输出数据的深度。比如如果我们设置
depth=8
,那么我们将由 888 个过滤器,每个过滤器产生一个平面,我们将这些平面平行堆放在一起,可以形成大小为 32×32×832\times32\times832×32×8 的输出数据
如果我们设输入的宽度为 WWW,接收域的宽度为 FFF,移动间隔为 SSS,零边宽度为 PPP,则我们可以得到输出的宽度为
(W−F+2P)/S+1
(W-F+2P)/S+1
(W−F+2P)/S+1
我们可以通过设置零边宽度为 P=(F−1)/2P=(F-1)/2P=(F−1)/2 使得在 S=1S=1S=1 的时候输出面积与输入面积大小相同。
矩阵运算
可以通过 im2col
的技术将上述过程实现为卷积运算(image to column)。
- 给定 227×227×3227\times227\times3227×227×3 的图像,11×11×311\times11\times311×11×3 的过滤器,零边宽度为 000,移动间隔为 444。我们的接收域大小为 11×1111\times1111×11,我们可以将原图像对应大小为 11×11×311\times11\times311×11×3 的区域拉伸成 363×1363\times1363×1 的形状。按照公式,横向有 (227−11)/4+1=55(227-11)/4+1=55(227−11)/4+1=55 个位置,因此整幅图像上一共有 55×55=302555\times55=302555×55=3025 个点积位置,每一个点积位置的区域我们都进行拉伸。最后将原图转化成 363×3025363\times3025363×3025 的形状。其中会有许多重复的数值,因为过滤器在原图上的接收域有重合。我们将新得到的矩形称为
X_col
- 随后我们将过滤器拉伸成 1×3631\times 3631×363 的形状,如果我们一共有 969696 个过滤器,则将得到一个大小为 96×36396\times36396×363 的矩形,称之为
W_rol
- 随后我们应用矩阵乘法
W_rol.dot(X_col)
,再将新得到的结果的维度转化成 55×55×9655\times55\times9655×55×96,即可得到卷积结果。
该方法的占用内存较多,但是我们可以使用矩阵乘法的相关理论加速运算。
池化层(Pooling Layer)
池化层用于连续的卷积层之间,通过减少数据的大小从而减少参数,进一步减少计算量。其分别作用于输入的每一层上。池化层过滤器的大小通常为 2×22\times22×2,移动间隔为 222,采用 max\maxmax 运算,即选出 444 个值中的最大值,从而减少了 75%75\%75% 的数据。总的来说,池化层作用如下:
-
接受大小为 W1×H1×D1W_1\times H_1\times D_1W1×H1×D1 的输入
-
接受两个超参数
-
平面宽/高度,FFF
-
移动间隔 SSS
-
-
产生大小为 W1×H2×D2W_1\times H_2\times D_2W1×H2×D2 的数据
- W2=(W1−F)/S+1W_2=(W_1-F)/S+1W2=(W1−F)/S+1
- H2=(H2−F)/S+1H_2=(H_2-F)/S+1H2=(H2−F)/S+1
- D2=D1D_2=D_1D2=D1
池化层只有超参数,不需要参数,同时也不需要增加 000 边。两个常用的池化层过滤器大小为 F=3,S=2F=3,S=2F=3,S=2 或 F=2,S=2F=2,S=2F=2,S=2
池化层常使用 max\maxmax 运算,但也可以采用均值运算或者求二范数。
全相联层(Fully-Connected Layer)
与神经网络相同
全相联层到卷积层的转化
假设 FC 层的输入为 7×7×5127\times7\times5127×7×512,输出为 K=4096K=4096K=4096,那么可以将 FC 层看做 F=7,P=0,S=1,K=4096F=7,P=0,S=1,K=4096F=7,P=0,S=1,K=4096 的卷积层。即将整个全相联层看做一个过滤器。
转换后的FC层能够适应不同大小的图像。例如我们初始的输入图像大小为 224×224×3224\times224\times3224×224×3,中间经过多层卷积与池化之后,大小为 7×7×5127\times7\times5127×7×512(7×25=2247\times2^5=2247×25=224,说明经过了 555 轮池化),随后经过第一个 FC 层,得到 1×1×40961\times1\times40961×1×4096 的输出,经过第二个 FC 层,仍然得到 1×1×40961\times1\times40961×1×4096 的输出,最后经过第三个 FC 层,得到 1×1×10001\times1\times10001×1×1000 的输出。如果此时输入图像大小更改为 384×384×3384\times384\times3384×384×3,那么到达第一个 FCFCFC 层的数据大小为 12×12×51212\times12\times51212×12×512,FC 层无法适应大小的变化,因为其只能接收固定输入数目的参数。而如果将 FC 层改写为卷积层,那么三个改变后的 FC 层就能接收输入,并最后得到 6×6×10006\times6\times10006×6×1000 的输出。
三 卷积网络架构
层间架构
最常用的架构如下
INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC
POOL?
表示池化层是一个可选操作。通常情况下 0≤N≤30\leq N\leq30≤N≤3,0≤K<30\leq K<30≤K<3,下面是几种常见的神经架构
-
INPUT -> FC
线性分类器
-
INPUT -> CONV -> RELU -> FC
-
INPUT -> [CONV -> RELU -> POOL]*2 -> FC -> RELU -> FC
每一个卷积层之间都会有一个池化层
-
INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]*2 -> FC
每个池化层之间有两个卷积层,该架构适用于大规模深层的神经网络,卷积层可以在池化之前提取到较多特征。
相对于一个大的接收域的卷积层,通常更倾向于使用多层小接收域的卷积层。
单层大小
输入层应该被 222 的幂次整除,包括 32、64、96、224、384、512
卷积层应该使用较小的过滤器,如 3×33\times33×3到 5×55\times 55×5,取 stride=1
,并且设置 zero_padding
不改变输入数据的宽度和高度。
池化层最常见的是使用接收域为 2×22\times22×2,stride=2
的最大值池化层(max-pooling)。或者较少使用接收域为 3×33\times33×3,stride=2