原文地址:https://cs231n.github.io/convolutional-networks

网络结构概述

传统神经网络每层的神经元都与上一层的每个神经元连接,即每层都是全连接层。这样的网络如果用于图像分类任务,会导致参数量巨大。而卷积神经网络中,每层的神经元只与上一层的一小部分神经元连接。

网络中的层

神经网络是由一系列层构成,每层通过一个可导的函数,将3D的数据转换成另一个3D的数据。
举例来说,对于CIFAR-10任务,网络结构可以是INPUT - CONV - RELU - POOL - FC

  • INPUT层输入[32x32x3]图片数据
  • CONV层计算卷积,可以使用12个filter,得到[32x32x12]
  • RELU层使用激活函数max(0,x),将负值变为0,输出仍为[32x32x12]
  • POOL层使用下采样,得到[16x16x12]
  • FC层计算出10个类别的得分,输出[1x1x10]

其中CONV和FC层是有参数的,即神经元的权重和偏移,其输出取决于输入和参数;而RELU和POOL层是无参数的。模型训练过程就是利用梯度下降更新参数,使得模型输出与标签尽可能一致。
一个微型VGG网络如下图所示:
image.png

卷积层

神经元 === 卷积核 === filter
卷积层承担了大部分计算工作。每层的输入是一个三维矩阵(宽x高x深)。每一个filter的宽高比较小,比如第一层一般是5x5,而深度与输入相同。将filter在输入矩阵上滑动做点乘运算,即卷积,则得到一个二维矩阵。一组filter,比如12个,就会得到12个二维矩阵。将这些二维矩阵叠放在一起,得到深度为12的一个三维矩阵,作为输出。
靠前的层可以感受到物体边缘、斑点等局部信息,靠后的层则可以感受到蜂窝、轮胎等更大范围的模式信息。
如果类比生物的神经系统的话,那么filter可类比为神经元。每层之间的神经元互相连接形成神经网络。
每个神经元感知到一小块区域的信息,类比于树突;点乘之后得到一个值作为输出,类比于轴突。
神经元只与上一层的一部分神经元连接,连接的数量称为感受野(receptive field),等同于filter的大小。需要注意,在宽高维度上连接是局部的,但在深度维度上,连接是全局的。神经元的深度与输入的深度始终相同。
每个神经元的权重数量等于其size,如5x5x3的神经元有75个权重,而有1个偏移。点乘的结果再加上偏移才是最终的输出值。
神经元在输入上平移时,权重和偏移是相同的;不同的神经元生成输出的不同的二维矩阵,这些神经元之间的权重和偏移是不同的。平移参数不变是基于这样的假设:在图片一处有效的特征提取,在另一处也应该同样有效。
注意,这一假设并非总是成立,比如人脸图像,不同部分应该使用不同的特征提取,此时神经元在平移时会使用不同的参数,而这样的层也不叫做卷积层,而称为局部连接层。
感受野中每个数都来自上一层的一个神经元的输出,所以神经元的size等于连接数。
每层的输出shape受几个因素影响:

  • 神经元个数:深度等于神经元的个数
  • 步长:宽高受卷积移动步长stride的影响,步长2得到的宽高会比步长1小一半
  • 补零:补零处理输入的边界,通常使输入输出宽高保持一致,简化卷积层参数的匹配;补零还用于处理步长为2时尺寸不兼容问题;补零还能够避免图像边缘信息的损失

卷积过程可视化,视频:屏幕录制2022-05-28 03.52.21 480.mov
image.png
卷积运算的具体实现,是将矩阵转换成向量,再进行向量乘法,空间换时间。
反向传播也是卷积运算。

池化层

作用是减少特征图大小,减少参数量,避免过拟合。
具体做法是,在2x2区域内求最大值,步长为2,对不同深度的每一层做同样操作,得到宽高减半、深度不变的图。在生成模型中一般不使用池化层。
image.png image.png

全连接层

神经元与上一层的每个神经元都连接,参见普通神经网络。
全连接层也可以看作是一个特殊的卷积层,卷积核宽高等于输入宽高,所以输出就等于1x1xK,K是卷积核个数。
最后一个全连接层得出的就是K个分类的得分。

层如何构成网络

层的模式

传统的网络结构如下,但更现代的网络会有更复杂的结构
INPUT -> [[CONV -> RELU]N -> POOL?]M -> [FC -> RELU]*K -> FC
CONV和RELU总是一起使用,有时也会把RELU算作CONV的一部分。
相比使用一个较大的卷积核,使用多层小卷积核在感受野相同时,由于有多层RELU,能够更好地提取特征,计算量也更小。
在实际工作中,我们基本上不需要自己设计网络结构,也不需要从头训练一个网络。
现实中的工作流程:找到对目标问题最有效的网络,下载预训练网络,对网络做微调,在自己的数据上训练网络。

层的参数

输入层:图片的尺寸需要能够被2整除很多次,比如ImageNet常用的224
卷积层:卷积核大小一般用3x3或5x5,配合步长和补零使得输入输出宽高不变
池化层:最常用的是2x2,步长2,使输出减半
使用多层小卷积核的问题是显存占用过多,所以可以在第一层使用大卷积核

一些经典网络

  • LeNet:第一个成功的卷积神经网络,1990年代由Yann LeCun发明,用于识别数字
  • AlexNet:由Alex Krizhevsky发明,2012年ImageNet冠军,引入了多层卷积
  • ZF Net:由Zeiler和Fergus发明,2013年冠军,减少了第一层尺寸,增大了中间层尺寸
  • GoogLeNet:由Google的Szegedy发明,2014年冠军,使用Inception模块,并用平均池化层替换了AlexNet中的FC层,大幅减少了参数量(60M -> 4M)
  • VGGNet:2014年ImageNet亚军,使用了很深的网络,有16组CONV/FC层,全程使用3x3卷积和2x2池化,参数量也达到了140M,开销巨大。后来研究发现可以去掉一些FC层,可以大幅减少参数量,不降低效果。
  • ResNet:Microsoft的何恺明发明,2015年冠军,使用了跳连接,大量的BN层,结束部分不使用FC

VGGNet的具体网络结构如下:

输出size 内存占用 参数量
INPUT [224x224x3] 2242243=150K 0
CONV3-64 [224x224x64] 22422464=3.2M (333)*64=1,728
CONV3-64 [224x224x64] 22422464=3.2M (3364)*64=36,864
POOL2 [112x112x64] 11211264=800K 0
CONV3-128 [112x112x128] 112112128=1.6M (3364)*128=73,728
CONV3-128 [112x112x128] 112112128=1.6M (33128)*128=147,456
POOL2 [56x56x128] 5656128=400K 0
CONV3-256 [56x56x256] 5656256=800K (33128)*256=294,912
CONV3-256 [56x56x256] 5656256=800K (33256)*256=589,824
CONV3-256 [56x56x256] 5656256=800K (33256)*256=589,824
POOL2 [28x28x256] 2828256=200K 0
CONV3-512 [28x28x512] 2828512=400K (33256)*512=1,179,648
CONV3-512 [28x28x512] 2828512=400K (33512)*512=2,359,296
CONV3-512 [28x28x512] 2828512=400K (33512)*512=2,359,296
POOL2 [14x14x512] 1414512=100K 0
CONV3-512 [14x14x512] 1414512=100K (33512)*512=2,359,296
CONV3-512 [14x14x512] 1414512=100K (33512)*512=2,359,296
CONV3-512 [14x14x512] 1414512=100K (33512)*512=2,359,296
POOL2 [7x7x512] 77512=25K 0
FC [1x1x4096] 4096 77512*4096=102,760,448
FC [1x1x4096] 4096 4096*4096=16,777,216
FC [1x1x1000] 1000 4096*1000=4,096,000

总内存占用:每张图片24M * 4 bytes ~= 93MB,前向+反向再x2
总参数量:138M
开始的CONV层贡献了大部分内存占用和计算量,最后的FC层贡献了大部分参数量。

计算上的考量

显存往往是实际计算中的瓶颈。

  • 中间层输出:训练模型时需要保存每一层的输出和梯度,用于反向传播;而在测试阶段可以只保存当前层的数据,丢弃之前所有层的数据,从而节省大量的内存。
  • 参数量:权重、梯度、step缓存,参数相关的内存占用可能是参数量的三倍
  • 此外还需要保存一些模型参数、加强后的图片数据等

当内存不足时,可以用更小的batch