当前位置: 首页 > article >正文

CNN 卷积神经网络

前置基础知识

convolution operator 卷积运算

输入矩阵循环取子矩阵跟filter(kernal)按位乘后加和作为输出矩阵对应位置的值。

convolution与cross correlation

上面操作实际是cross correlation操作,两者之间的唯一区别是卷积操作需要在开始计算之前将卷积核进行翻转。但是在CNN领域都把互相关操作记为卷积操作了,因为这对模型没有任何影响并且计算量更小。

在一些信号处理场景中,真正的卷积(即带翻转)可以使卷积操作满足结合律,但这在CNN模型中没用。

边识别

使用下图中的filter可以发现竖直边。原因是边的两侧数值是不一样的,分别取正取负会是非0值,而没有边的地方两侧数值基本相等,分别取正取负是0值,因此非0值可以代表有竖直线。

同时值的正负可以代表两侧的颜色差异,是从暗到明,还是从明到暗,如果不关注明暗变化的话可以取绝对值。

使用不同的filter矩阵可以实现不同的效果。随着深度学习的出现可以使用可更新参数,在训练中反向传播更新,可以达到更好的效果。

padding

在上面的卷积运算中有两个问题:

1. 输出维度会缩小

2. 边缘的像素被使用到的次数比中心的点少

所以在进行卷积操作之前先将原始输入增加padding,可以规避上面两个问题

strided convolutions 跨步卷积

即卷积运算时不是一个格子一个移动,而是每次移动stride个格子。

convolution on volume

卷积核的层数与输入层数一致,每层分别计算卷积后加和作为最终输出,最终输出还是一层。

如果使用多个卷积核进行计算,则可以得到多层的输出。不同的卷积核可以发现不同方向的边

Pooling 池化

池化的操作跟卷积类似,圈选跟核同样的区域块并移动,在区域内与核进行操作,比如最大池化的操作变成了取区域内最大值,而不是按位乘加和。

池化用的最多的是最大池化,其次是平均池化。

通常不会padding后池化,偶尔也会。

平均池化就是区域内取平均值。

Networks in Networks(1×1 convolution)

池化用于减小网络Volume中的height和width,1×1卷积可以用来减小Volume中的channel数量。如下图6×6×32的网络层经过1×1×32的卷积核组成的卷积层,会输出6×6×#卷积核数量。也就是可以通过控制1×1 convolution的卷积核数量来缩放channel数量。并且可以引入非线性。

 Transpose Convolution 转置卷积

为了在语义分割模型实现上述过程的后半部分,需要使用转置卷积,即通过转置卷积操作,增大输出的width和height。

转置卷积计算过程如下图:输入的每个格子乘到卷积核上,然后忽略padding后填值到输出对应位置,过程中重叠的部分加和。

卷积神经网络模型结构

单层卷积神经网络

卷积神经网络的一层中,卷积核卷积操作类似于标准神经网络中的\omega参数,之后加一个b参数,然后使用激活函数激活得到输出。

维度分析如下:

深度卷积神经网络

简单卷积网络

最后一层输出将三个维度的数值展开为一个一维向量并输入到逻辑回归单元或者Softmax单元进行结果输出。

实际应用中一般会让宽度和高度逐步降低,channel数(厚度)变大。

常规卷积网络

卷积层后通常会带池化层,不过一般会把两者成为真正的一层,因为池化层只有超参,而没有需要训练的参数一般不统计在层数之内。

几层卷积层之后接入几层全连接层,然后接入Softmax层输出数字预测。

下面是在模型中每一层输出的shape、大小以及参数大小情况。

一般输出的大小是会下降的,但是要逐步下降,如果猛烈下降效果可能会不太好。

主要的参数量还是在全连接层

卷积神经网络优点

参数共享

如下图的一个卷积层,参数量:(5*5+1)*6=156。

而如果使用全连接映射3072维到4704维,则需要3072*4704=14M参数。

卷积神经网络通过参数共享大幅降低参数量,而能参数共享的原因是图像的不同部分会存在相同的特征,比如竖直边、横边等。也就是能识别一些具有平移不变性的特征。

稀疏连接

输出中的一个元素只跟输入中的部分元素相关,不是全连接的。这样可以使模型能够捕捉图片中的局部特征,更高效地处理图像的空间结构。

Case Study

在CNN模型中有非常多的超参,如果设置这些超参是个问题。通常不需要你全部从头调试,而是可以尝试从论文中或者其他人项目中参考其他人的参数即可。在CV里面如果其他人的效果比较好,那么大概率在你的项目也会不错。

经典网络

LeNet-5

跟之前的例子相似,只是原文由于历史原因有一些方式跟现在不同。比如池化使用的平均池化,闲杂一般使用最大池化;输出层不是使用的Softmax激活函数;隐藏层的激活函数用的SIGMOD/tanh,而非ReLU;当时算力不够,一个卷积层为了处理不同channel有复杂的处理方式;等等

AlexNet

在AlexNet出现之前深度学习在语音识别等领域已经很好用,但在CV领域影响力不大。2012年在ImageNet大规模视觉识别挑战赛中表现优异,使得深度学习在CV以及其他领域也开始有超强的影响力。

更复杂的模型,深度更深,60M的参数。

使用了两张GPU联合训练。

使用了ReLU激活函数。

VGGNet

VGG-16代表有16层带参数的网络,即不包含池化层。

大幅减少了超参数量,因为都是使用相同的卷积核(数量在变)、池化层。

138M参数。

ResNet

由于梯度消失或者爆炸之类的问题,很深的网络很难训练成功。Residual network避免了这个问题,让我们能够训练深达比如100层的网络。

通过将前序的隐藏层输出到隔一层的网络层实现。普通神经网络:

z^{[l+2]} = w^{[l+2]}a^{[l+1]}+b^{[l+2]}

a^{[l+2]}=g(z^{[l+2]}])

ResNet网络结构如下图,状态转移转移方程:

z^{[l+2]} = w^{[l+2]}a^{[l+1]}+b^{[l+2]}

a^{[l+2]}=g(z^{[l+2]}]+a^{[l]})

为什么奏效

a^{[l+2]}=g(z^{[l+2]}]+a^{[l]}),展开z^{[l+2]}得到a^{[l+2]}=g(w^{[l+2]}a^{[l+1]}+b^{[l+2]}+a^{[l]})

如果w^{[l+2]}b^{[l+2]}都等于0,可以得到a^{[l+2]}=g(a^{[l]}),由于我们使用的ReLU激活函数计算得到的a^{[l]}a^{[l]}大于等于0,再套一层ReLU函数值不变,即a^{[l+2]}=a^{[l]}

如果a^{[l]}的维度跟z^{[l+2]}不一致,可以在a^{[l]}前再乘一个矩阵转换维度到一致。

也就是说如果在l+2层出现了梯度消失或者学习出来这一层参数就是接近0,也能使输出维持在第l层,相当于第l+1和l+2层不存在,这不会影响我们的模型效果。

但是我们不仅希望不影响模型效果,我们是希望提高模型效果。在很深的模型中,有些层可能会产生坏的影响,添加残差网络后,可以使得模型能学习到这些恒等的层,让一些层不发挥坏作用,然后其他好的层就能更好的发挥作用。

应用了残差网络的CNN网络如图:

Inception Network

Inception模块
原理图版本

对一个输入,分别使用1×1、3×3、5×5卷积核进行卷积操作,以及一个最大池化操作,每个操作都会输出一个Volume,保证每个Volume的width和height一致,然后将其拼接起来获得一个最终输出。

优化版本

上面的版本存在一个问题就是计算量巨大,比如我们看下其中的5×5卷积核的卷积操作计算量:

一个卷积核一个框计算量 5*5,192个channel再*192,框需要移动28*28次,一共32个卷积核,总计算量5*5*192*28*28*32≈120M。

使用1×1卷积核降低计算量:

即先使用1×1卷积核降维,然后再使用正常卷积升维,这个1×1卷积层一般称为"bottleneck layer"瓶颈层。计算量降低为1*1*192*28*28*16 + 5*5*16*28*28*32≈12.4M。从原先的120M降低到12M,下降为原先的10%

并且,从实际的效果来看,使用1×1卷积层降维对于模型效果本身的效果并没有明显降低,可能的原因如下:

1. 增加非线性:1×1卷积后通常会接一个非线性激活函数(如ReLU),这可以引入额外的非线性能力,增强模型的表达能力;

2. 跨通道信息融合:1×1卷积可以融合不同通道的信息,从而提取更丰富的特征;

3. 防止过拟合:减少参数量可以降低模型的复杂度,从而在一定程度上防止过拟合。

最终结构

池化层不影响channel数量,所以再跟一个1×1卷积降低channel维度。

完整模型结构

下图是Inception V1版本,也叫做GoogleNet,后续还衍生成V2、V3、V4、Inception-ResNet等版本。

模型中有不少重复的Inception模块,除此之外还有两个中间的输出层保障前面的预测结果不会太差,这个被证明有正则化的作用。

inception的命名来源是《盗梦空间》里面的台词"we need to go deeper"。

MobileNet

出现原因

MobileNet出现的原因是前面提到的CNN网络往往计算量都非常大,不适合在计算能力低的手机等移动设备运行。因此逐步提出了对计算性能要求更低的MobileNet。

基本原理
Normal Convolution

普通卷积每个卷积核的channel数跟输入channel数对齐,然后通过卷积核的个数控制输出channel数,原理及计算量如下图:

Depthwise Convolution

depthwise卷积是每个卷积核的channel数都是1,然后卷积核的个数跟输入的channel数一致,这样每个卷积核只处理输出的一个channel,得到一个跟输出channel数一样的输出。原理及计算量如下图:

Pointwise Convolution

Pointwise卷积其实就是1×1的Normal卷积,每个卷积核都在处理输入的一个Point。其原理和计算量如下图:

Depthwise Separable Convolution

Depthwise Separable卷积就是把输入先进行Depthwise卷积,再接一个Pointwise卷积。

值得注意的是Pointwise卷积层的输入是Depthwise卷积层输出,而Depthwise卷积层的输出channel数跟其输入channel数一致,所以这三者的channel数都是一致的。而Pointwise卷积层卷积核的个数可以控制最终输出层的channel数。

在这个操作中计算量大幅下降,在上面图中给的例子里面计算量降低为Normal卷积的31%,在论文中作者给出的计算量下降如下图右侧,典型的场景中计算量下降为普通卷积的10%

模型结构
V1版本

使用上面提到的Depthwise separable卷积,重复13次

V2版本

如上图下半部分,跟V1有两个主要区别:

1. 引入了残差连接

2. Depthwise卷积前面增加了一个Expansion层。

中间的整个模块叫做Bottleneck模块,其内容如下。Expansion层其实就是一个1×1的Normal卷积层,然后卷积核的个数大于输入的channel数实现channel数放大。然后在Pointwise卷积层再通过减小卷积核个数将channel数缩小到原来的大小。

另外在这个版本中残差块的输出是通过线性激活函数激活的,没有使用ReLU,ChatGPT的解释是避免了激活函数可能带来的信息丢失,尤其是在低维度特征中。

EfficientNet

在不同算力的设备上你可能想根据算力选择不同的模型结构,通常可以通过输入的分辨率r、模型的深度d、模型每一层的宽度d影响模型的算力要求,如何选择合适的参数,可以参考下面论文,没有展开讲。

CNN应用的建议

使用开源实现

即使对于一个相关专业的博士生来说,仅通过阅读论文来复现他们的模型效果也是很困难的。幸运的事有很多研究者会将论文的实现开源在网络上,可能是论文作者,也可能是其他研究人员。因此第一个建议就是你想使用一篇论文的模型时,可以先在网络上搜索开源实现。

迁移学习

在CNN网络的应用中,迁移学习是非常广泛的,几乎你想实现任何基于CNN的应用都应该首先尝试进行迁移学习。根据你训练集的大小可以选择不同的迁移学习方式:

比如你的数据集很小,可以使用其他人的网络结构和参数,只把输出层替换为你自己需要的,比如你想三分类则替换为一个三分类Softmax输出层,然后锁住前序所有weight,只使用反向传播更新输出层的参数;同时你可以针对每个数据集将前序固定了的层的输出计算好之后作为缓存,因为参数不变输出不变,然后使用缓存结果在多轮迭代中更新输出层参数。

如果你的数据集中等大小,可以尝试下载其他人的网络和参数,锁定前面几层的参数,训练后面几层及你自己的输出层,也可以把后面几层替换为你自己的网络结构,从头训练后面的网络结构。

如果你的数据集非常大,则可以下载别人的模型,仅使用下载的参数作为初始化,全部重新训练。

数据增强

在CV领域,模型对于训练集数据量的需求是非常大的,数据增强在CV模型训练里面是十分常用的方式去提示模型的性能。以为数据增强的方式很多,超参也很多,也是建议参考网上现成的实现。

常用手段:镜像、随机裁剪、旋转、剪切变换、局部变形等。前两个最常用。

第二类:色彩偏移,还可以使用PCA color augmentation,分析主色彩,然后对主色彩进行大幅度的调整,在AlexNet论文中有详细描述。

Computer Vision现状

当你有足够的数据的时候,你可以使用更简单成熟的算法,而不需要很多hand engineering,当你数据很少时,往往意味着你需要很多hand engineering。数据、hand engineering是两种可以提供给模型的知识来源。数据少的时候也可以使用迁移学习。

课程还给了几个提高打榜成绩的方式,但是在生产中不怎么会用到,看看就好。

Object Detection 物体识别

下图一是图像分类,可以识别图片物体是个汽车,第二个是带定位的分类,除了识别是汽车外,需要框出汽车所在位置,第三个则是识别图中所有汽车并且框起来。

带定位的分类

如下图模型,有两个输出单元,一个输出分类,一个输出框的信息,然后根据标签反向传播更新参数。

训练集label及损失函数的定义。损失函数部分简单使用L2计算损失,实际可以根据输出类型,比如softmax使用交叉熵

地标检测

可以识别比如人脸的关键点,人体的关键点,训练数据也需要标注好每个关键点的坐标。

滑动窗口检测

普通滑动窗口

即对于输入图片使用不同大小的窗口,滑动着对图片的每个部分进行识别。

优点是你训练模型的时候只需要训练整个图片都是汽车或不是汽车的图片进行分类判断即可,需要的训练数据可以更少,模型可以更简单。缺点是计算量太大,每个框出来的图都要输入一遍模型做判断。在神经网络出现之前,人们通常使用简单的线性分类器进行分类,这种情况输入大量图片的计算量也不算太大,但是在CNN网络中输入大量的图片进行运算成本还是太高。

滑动窗口检测卷积实现

首先,按照下图将CNN网络最后的FC层替换为1×1卷积层,这里的替换是等价的。然后softmax输出层也替换为卷积层,这里不是等价的。

然后如何对输入图片进行滑动窗口检测呢?下面真的是个巨聪明的做法,至少我看到这里觉得好聪明!因为窗口滑动过程中是会有重叠的,重叠部分的计算其实也是重复的,为了充分减少重复计算如下图:

假设你的检测窗口大小事14×14×3,如果你在16×16×3的图片上正常流程是分别抠出14×14×3的区域执行最上面一行的计算。但是实际上你不需要这么做,而是直接对16×16×3的图片进行第一行后续的操作得到扩展范围的结果即可,比如第二行最终输出2×2×4,第三行最终输出8×8×4,其中每个格子就代表了最左侧图片的子图的检测结果。这样大幅减少了重复计算。

R-CNN

Region with CNN,主要使用了候选区域(Region Proposal)的思想。对图片先使用图像分割算法处理,然后针对分割结果中候选区域进行检测,而不是像滑动窗口每个都检测,基本相当于对滑动串口检测加了剪枝策略,对于有物体概率大的区域进行检测。

但是R-CNN还是很慢,后来出现了 Fast R-CNN的预测过程进行优化,其在R-CNN的基础上使用了上一节提到的滑动窗口卷积实现进行加速,大大提高了预测效率。

但是Fast R-CNN还存在的问题是使用图像分割算法然后选取候选区域的过程还是很慢,又出现了Faster R-CNN算法,使用CNN网络来进行候选区域选取,而不是传统的图像分割算法,这提高了候选区域选取速度。

但是R-CNN类的算法整体还是比下面要讲的YOLO算法慢很多。

YOLO算法

简介

滑动窗口方式除了计算量比较大,还有个问题就是窗口是离散的,所以大概率不会有窗口完美框到待识别物体,并且可能物体本身也不是正方形,因此单纯的滑动窗口无法准确给出边界。

YOLO(you only look once)算法解决了上面问题,并且替换了滑动窗口。它将图像等分成n×n的格子,以下图为例3×3,然后识别3类物体,标签为8维数据,P_c每个格子是否有Object,有的话中心点坐标b_x、b_y,框的高度b_h,框的宽度b_w,softmax结果3个,然后训练。

所以对于上面例子一张图片的输出为3×3×8的Volume,可以设计一个CNN网络输出这个维度。这个算法一个格子只能有一个物体,并且一个物体只归属于一个格子,按照物体中心来判断,当然也可以放大n,减少一个格子出现多个物体的概率。

由于训练及推理过程中所有格子都是通过同一个CNN模型进行,而不是给每个格子设计一个模型,即所有格子是共享模型及模型参数,推理时也是所有格子并行推理即可,非常高效。

需要注意的是中心点是必定落在当前格子的,框的高宽必大于0也可以大于1,论文还是用了其他参数和方法来控制输出值的范围。

Intersection over union

即交集面积除以并集面积,用来计算重合占比,可以用来评估两个框的相似程度,进而评估识别的准确度。按我理解是用来计算定位的损失函数的,这里还没说怎么用!

Non-Max Suppression 非极值抑制

是为了解决模型的结果中多个检测框认为识别到了物体的问题,比如下图,由于格子相对物体比较小,如下图可能真正中心点附近几个格子都会认为自己检测到了中心。

训练过程中可以对认为检测到物体的格子计算IoU,选择概率最大的格子作为最终输出,其他格子被抑制。

如何做到的呢?以只识别一类物体为例,执行如下流程逐步输出所有识别到物体的格子。如果是多类物体识别的,则针对每一类内执行下述操作即可。

丢弃所有 p_c ≤ 0.5 的格子
while 还有 p_c ≠ 0 的格子
    选取 p_c 值最大的格子G_max作为一个识别到物体的格子,并不再参与后续计算
    丢弃所有跟G_max的IoU值≥0.5的其他格子(即大概率是识别重复的)

Anchor Boxes 锚定框

前面的算法中每个格子只能识别一个物体,但是如果一个框内有两个物体,如何识别。锚定框可以用来解决这个问题。如下图,输出维度变为原先的两倍,比如竖向的框放在前8维,横向的框放在后八位,进行训练及推理。

上面的方式解决不了一个格子出现多余2个物体的情况,也处理不了一个格子的两个物体朝向一致的情况。不过当格子数很多的时候,这种情况的概率是很低的。

另外还可以使用hand engineering或者kmeans聚类算法针对你数据集中的物体的框的形状设置,得到一组框(比如针对竖向的人,横向的车,方形的某物体等),用于训练和推理。

完整的YOLO算法
训练集

3×3代表图片切分的格子数,×2代表锚定框数量,×8代表每个锚定框的向量维度。

前向传播

前行传播输出维度跟训练集一致,输出之后针对每个预测类别进行一次非极值抑制输出最终预测值。

Semantic Segmentation 语义分割

相比物体识别是判断图中是否有某类物体并框出所在位置,语义分割是将图片的每个像素是什么进行判断。

语音分割的一个使用场景是在自动驾驶领域,用来判断一个区域是否是可以行驶的。也可以用于医学中的器官识别、肿瘤识别等。

U-Net

 语义分割的输出是类似下图,对每个像素进行归类。

在之前的物体识别模型中,随着前向传播,整体趋势是降低输出的width和height,增加channel数,最后接FC层和Softmax层输出n×1维张量。但是在语义分割模型中,将FC和Softmax层去掉,采用跟前序层相反的过程,即增大width和height,降低channel数,最后输出每个像素的类别。

模型结构

其中矩形的宽度代表channel数,高度代表height。其中利用了Transpose Convolution增加输出的height和width,使用skip connection合并low level特征和hige level特征。

CNN应用例子

人脸识别

Siamese Network

人脸识别是one-shot learning问题,比如公司闸机的人脸识别系统,你在训练时可能针对每位员工只有一张照片,你的训练集是很少的。此外你不能使用Softmax作为输出层,因为它要求神经元数量跟员工人数一致,而有人新入职,你不肯能重新训练。

针对前面的问题采用的方式是学习一个相似函数,分别输入两张图片,输出两张图片的特征向量,然后计算两个特征向量的差异度,当差异度比较小则认为是同一个人。这可以使用Siamese Network(孪生网络)进行。

模型训练

训练的目标是相同人差异小,不同人差异大,因此训练集中每个元素应该是个三元组,Anchor/Positive/Negative,为了增大人不同时的差异,可以增加一个Margin系数α。

损失函数如下,通过梯度下降更新模型参数即可,训练集需要比较大,之后可以应用到数据很少的系统。

也可以使用下面的两个网络

训练集选择

不能随机选择作为负样本,因为随机选择时A跟N之间差别很大,A跟P本身很近,很容易满足我们的相似度差异大的要求。

应当进行难例挖掘,即选择迷惑性较大的负样本进行训练,更好的学习参数。

另一种网络实现方式

上下部分共享网络参数,最后接一个SIGMOD输出层判断是否是同一个人。此外对于底库中的数据可以提前infer出特征向量缓存后在线上直接输入到SIGMOD输出层。

Neural Style Transfer 

基于神经网络的风格迁移。即将一种风格应用于输入图片中。

可视化CNN网络在学习什么

因为CNN网络每一层的每个格子,都是根据前一层的一部分格子计算得到的,因此可以反向得到每一层的每个格子对应了原始图像的哪一部分图像块。

然后通过输入图像找到使得某一层某一个格子激活值非常大的情况,反向找到是哪些图像块,这些图像块会具有相同的性质。

重复执行,可以得到所有神经元的识别类型。

训练

在风格迁移的训练过程中,使用预训练好的模型,并冻结模型参数,梯度下降更新的不是模型参数,而是输出的图像。

损失函数由两部分组成,一部分是生成图像与与输入图像的损失,一部分是生成图像与风格图像之间的损失,其中α和β是超参。

随机初始化生成图像,然后最小化成本函数,会逐步收敛到最终输出图。

Content 损失函数

选定预训练模型的其中一个隐藏层,计算Content图和生成图预测过程中在这一层的每个神经元的输出,计算两者L2差异作为Content损失函数。一般选取不太深也不太浅的隐藏层。

Style 损失函数

使用Gram矩阵来描述一个隐藏层不同channel之间的相关性,将两个channel对应位置的激活值相乘并累加得到一个相关性值G^{[l]}_{kk^{'}},然后n_c个channel之间两两得到一个相关性,形成一个相关性矩阵G^{[l]}。对输入图片和style分别输入并计算G^{[l]}并计算L2距离(除以几个常数)作为第l层的损失函数。

进一步选取多个隐藏层,每层有一个权重超参,多个层累加之后得到最终的损失函数。

  • 浅层(靠近输入):捕捉细节纹理、颜色、边缘等局部风格信息。

  • 深层(靠近输出):捕捉更大范围的结构、形状和全局风格信息。

  • 不同层的风格损失可以分配不同的权重。例如,浅层的权重可以较大,以强调细节纹理;深层的权重可以较小,以弱化全局结构的影响。

CNN在1D或3D场景的泛化

对于1D场景,如下图跟2D图片场景基本一样,对于一维数组,使用n*1*1的卷积核,多个卷积核进行卷积降低宽度,增大channel数即可。

对于3D场景,基本类似,如下图,其实就是改变卷积核的shape。


http://www.kler.cn/a/560542.html

相关文章:

  • 2016年下半年试题二:论软件设计模式及其应用
  • Java 进阶面试指南
  • 【7days-golang/gee-web/day02】设计Context-学习笔记
  • 前端学习—HTML
  • 九九乘法表 matlab
  • JPA与存储过程的完美结合
  • Unity Mirror 从入门到入神(一)
  • Java Set实现类面试题
  • 【linux】文件与目录命令 - awk
  • PHP MySQL 创建数据库
  • 机器学习数学通关指南——微分中值定理和积分中值定理
  • 塔能物联运维助力智慧隧道安全升级——城市交通保障新力量
  • std::thread的同步机制
  • 计算机视觉算法实战——跌倒检测(主页有源码)
  • 山东大学软件学院nosql实验三
  • 一个Flutter跨4端开发的案例
  • 流媒体网络协议全解析:从实时传输到自适应流,如何选择最优方案?
  • 【C++设计模式】工厂方法设计模式:深入解析从基础到进阶
  • 云电脑接入DeepSeek?探讨ToDesk云电脑、海马云、顺网云的AI潜能
  • API技术深度解析:构建高效、安全与可扩展的接口服务