zl程序教程

您现在的位置是:首页 >  大数据

当前栏目

07 | 卷积神经网络:给你的模型一双可以看到世界的眼睛

神经网络 模型 可以 世界 卷积 07 看到 眼睛
2023-09-14 09:14:45 时间

欢迎来到深度学习的 07 讲,上一讲我说到了线性回归模型,它可以帮助我们解决房价预测等回归、拟合的问题,我们也可以对回归方程 f(x)输出加一个 Sigmoid 函数,使其也能应用在分类问题上。

但现实中除了分类问题还有很多不同的场景,会用到图像算法、文本算法、音视频算法等等等。今天,就让我来带你学习卷积神经网络(Convolutional Neural Networks,以下简称 CNN)。希望通过这一讲,让你对图像算法方面中最基础和核心的环节有一个清晰的认识,并知晓算法是如何感知图像的内容的。

从标志说起
我们所处的是一个缤纷多彩的世界,天上飞的鸟、地上跑的车,还有路上行色匆匆的人,这些都是通过我们的眼睛去感知的。更为神奇的,在我们眼睛和大脑的配合下,整个感知过程极快,甚至不需思考就可以很轻松地指出哪一个是车,哪一个是人。

我们能如此快速地对眼睛看到的事物做出反馈,这与两个行为密不可分:学习和推论。

我们以拉勾和拉勾教育的标志为例,如下图:

仔细想想,我们判断这些标志属于哪个品牌时,是不是主要通过以下 3 种方式:

  1. 文字。如果有文字,我们可以直接通过文字认出品牌。在识别文字的过程中,文字的颜色并不太会干扰我们的认知。我们更倾向于辨别文字的形状,而这一行为也不会受到语言的干扰,无论它是中文还是英文。其中更神奇的是,无论这些文字如何变化,我们似乎都能识别,比如 Lagou 和拉勾。
  2. 图形。如果没有文字,我们依然能通过标志的线条或者轮廓来识别。
  3. 图形细节。如果是比较相似的图标,我们也能通过标志的纹理和细节进行区分。

我们可以把这一过程抽象一下:

  • 定位。这一步对于人眼来说是一个很自然的过程,因为当你去识别图标的时候,你就已经把你的目光放在了图标上。虽然这个行为不是很难,但是很重要。
  • 看线条。有没有文字,形状是方的圆的,还是长的短的等等。
  • 看细节。纹理、颜色、方向等,比如拉勾和拉勾教育不同的颜色。

假如我们不知道拉勾的标志,那我们是怎么学习的呢?同样的道理,识别文字、记住名字,并且记住可以抓住眼球的特征,我们可以从 logo、拉勾和绿色等因素来判断这是拉勾的标志。

卷积神经网络的几个重要概念


我们知道了人类识别物体的方法,那我们是否可以将这个识别的过程转化为程序能执行的方式,从而达到通过算法识别物体的目的呢?

答案是肯定的,这个方法就是卷积神经网络。可以说只要是做图像的人,都绕不开它,因为它实在是太经典、太实用了。卷积神经网络在人脸识别、智慧医疗、工业检测等方面有着广泛的应用,极大地缩减了人力物力的投入,并有着比人类更高效精确的表现。

接下来,为帮助你更好地理解卷积神经网络,我们先来看几个与之相关的概念,它们分别是卷积、激活函数、池化和感受野。

1.卷积

卷积神经网络,顾名思义,其核心就在于“卷积”。

卷积在卷积神经网络中的主要作用是提取图片的特征同时保留原来图片中各个像素的相对位置(空间)关系
举个例子,假设我们有 1 个 5x5 的矩阵 A 和 1 个 3x3 的矩阵 B,我们将这两个矩阵进行卷积操作(这有点类似点乘计算),操作过程如下图:

绿色的矩阵 B 在蓝色的矩阵 A 上从左到右,从上到下,每次滑动的步进值是 1 个格,也就是黄色的虚线框往右滑动 1 格,直到这一行的结尾。然后在下一行从头开始,直到最后得到一个完整的矩阵 C。

在这个过程中总共出现了 3 种矩阵:

  1. 原始图像的矩阵(A),这个实际上就是我们眼睛看到的世界。
  2. 3x3 矩阵(B),我们称为滤波器(filter)、(kernel),或是特征提取器、卷积核通过对滤波器矩阵设置不同的值,可以实现如边缘检测、锐化以及模糊操作等不同的操作。在实际的模型设计中,我们会使用不同尺寸的滤波器来实现不同的效果。通常我们只需要设定滤波器的尺寸和数量,卷积神经网络就可以自己通过梯度更新或反向传播得到内部的数值。使用越多的滤波器,可以提取到越多的图像特征,神经网络也有更好的性能。
  3. 卷积之后得到的矩阵(C),我们称为特征图(feature map、 activation map)。

如果我们把这个操作运用到实际图片上是什么样子呢?

如下图 4,原始图片是一张小动物,通过不断变化卷积核的内容,就可以得到不同的结果,有的是轮廓信息(第 2、3、4 行),有的是更加锐化后的信息(第 5 行),有的会变得模糊(第 6、7 行)。

讲解了卷积之后,我需要补充两个知识点:边缘填充和特征图尺寸的计算。

图 3 的计算当中,我们实际上是将滤波器和图像进行了对齐:第一步,滤波器(黄色虚线框)的第 1 个像素和图像的第 1 个像素是重叠的;最后一步,滤波器(棕色虚线框)的最后 1 个像素和图像的最后 1 个像素也是重叠的,这意味着滤波器没有超出图像。

如果我们在输入图像的周围填充一些 0,再把滤波器应用到这些边缘的点上,让滤波器“超出来”一些,这就叫作边缘填充。如下图,蓝色的矩阵是原始图像,外面一圈黄色的 0 就是边缘填充的值,虚线框内是卷积核。通过边缘填充,我们可以控制输出的特征图的大小。

那特征图的大小又跟哪些方面有关呢?

特征图尺寸的计算:假定:O=输出图像的尺寸,I=输入图像的尺寸,K=卷积层的核尺寸,S=移动步长,P=填充数。那么,输出图像尺寸的计算公式即为:O=(I-K+2P)/S+1

以上图为例,I=4,K=3,P=1,S=1(假设我们每次移动 1 格),则输出尺寸 O=(4-3+2x1)/1+1=4。

这个公式非常重要。在设计神经网络结构的时候,神经网络的每一层都是由上一层得到的,只有确保每个层的尺寸无误才能保证神经网络的正常运转。


2.激活函数


激活函数在咱们之前的篇章中已经反复出现过很多次了,它是一个极其重要的概念和结构,在这里我会更详细地介绍。

假设我们有一个简单的神经网络,如图:

这是一个 2 层的网络,其中 X1、X2 是输入,b1、b2 和 b3 是中间层的神经元,第一层是 b1 和 b2,第二层是 b3,Y 是输出。连接线的多个 w 值,对应神经元的权重。其中:

b1 的输出 = w11*X1 + w21*X2 + b1

b2 的输出 = w12*X1 + w22*X2 + b2

b3 的输出 Y = w13*b1 + w23*b2 + b3

=w13*(w11*X1 + w21X2 + b1) + w23(w12*X1 + w22*X2 + b2) + b3

=(w11*w13 + w12*w23)*X1 + (w21*w13 + w22*w23)*X2 + (b1*w13 + b2*w23 + b3)

这意味着,X1、X2 前面的系数仍然是一个常数,那么整个网络的输出就可以用 Y=X1*Wa + X2*Wb + B 来表示,折腾了一顿最后还是一个线性函数。这就相当于做了无用功,一层就能做到的事情,我用了好多层来做。

如果你有一个很大的网络,但是网络内部全都是线性单元,那这个网络一定可以被某种线性组合表示,你的隐藏层(如图 6 中的 b1、b2)就一点作用没有所以,我们需要在网络中增加非线性函数,即激活函数(也称激励函数),以便让模型有更好的学习能力,或者说更好的特征提取和组合能力,毕竟这个世界有很多问题如天气预测、人脸识别,都是线性模型无法搞定的。

激活函数的种类非常多,常见的一般有:Sigmoid、tanh、ReLU 和Leaky ReLU。

1.先来看Sigmoid 函数。

在信息科学中,由于其单增以及反函数单增等性质,Sigmoid 函数常被用作神经网络的激活函数。它可以把输入的数值约束到 0~1 的范围内,无论是多大或者是多小的数,都可以使其无限逼近于 0 或 1。如下图:

不过,现在 Sigmoid 的使用率在逐渐减少,还记得之前课程中咱们提到过的梯度消失和爆炸问题吗?Sigmoid 的导数是一个驼峰状的函数,取值范围在 0~0.25 之间,当网络层数比较多的时候,多个接近于 0 的数相乘,最后的值就趋近于 0,所以它比较容易引发梯度消失问题,最终导致模型无法正常学习。此外,Sigmoid 函数含有幂运算,在大型网络中幂运算是非常消耗时间的。

2.我们再来看tanh 函数。

tanh 是双曲函数中的一个,为双曲正切。在数学中,双曲正切“tanh”是由双曲正弦和双曲余弦这两种基本双曲函数推导而来的。tanh 函数也是一个较为常用的激活函数,其公式与图像如下所示:

可以看到,跟 Sigmoid 一样,tanh 函数求导之后的值域仍然在 0~1 之间,所以它也没有解决梯度和幂运算计算量大的问题。

3.接下来是ReLU 函数。

ReLU 函数又称修正线性单元,是一种人工神经网络中常用的激活函数。ReLU 的公式非常简单:Relu=max(0,x)。从图可以看到,当 x≤0 的时候,函数等于 0,而 x>0 的时候,函数的值就是 x

ReLU 函数看似很简单,但简单并不意味着不好用。

当输入<0 时,ReLU 函数的输​​出始终等于 0,因此函数的变化率为 0;当输入≥0 时,输​​出就是输入,因此,它的导数等于 1。

从对 ReLU 函数的描述可以看到,ReLU 函数不仅很大程度上解决了梯度消失的问题,还有着非常快的计算速度,因为它只需要判断是否大于 0 就行了。

当然,它也不是完美的,“ReLU 死区”就是一个很常见的问题,即:在训练一段时间以后,如果某个神经元在某次权重更新之后为负数,那它的激活函数就只会输出 0,这意味着这个神经元“死”掉了,以后再也不会输出其他的值了。例如,在一些特殊的情况下,比如学习率太大,就很容易导致更新后权值为负,从而引发神经元的“死亡”。

4.最后是Leaky ReLU 函数。

为了解决 ReLU 的致命缺点,脱胎于 ReLU 的 Leaky ReLU 函数应运而生,它的公式是f(x)=max(αx,x)。如下图:

α 是一个超参数,定义了 x<0 时的斜率,比如 0.01。这个小的斜率可以保证激活函数不会出现神经元“死亡”的情况。

不同的激活函数有着不同的使用场景,二分类问题我们一般采用 Sigmoid 函数,多分类问题我们采用 Softmax 函数,卷积神经网络的中间层一般会采用 ReLU 函数。当然,激活函数的种类非常多,具体使用哪一个还是要结合实际问题反复尝试。

3.池化

卷积操作之后,图片被转化成一张张特征图,数据量有时会很大,所以我们需要一种方式来尽可能地减少计算。于是便有了池化(Pooling)。池化实际上是一种降采样的形式,在尽量保留特征的同时,不断减少数据大小,从而减少参数量和计算量,比如我们压缩 1 张照片,照片大小被压缩后,其中的信息通常不会损失太多。

较为常见的池化有两种:最大池化(Max Pooling)和平均池化(Average Pooling)。

1)最大池化

顾名思义,就是在一张图内,选择一个池化尺寸,然后选择该尺寸中数值最大的像素点,添进新的特征图,直到该行为覆盖了整张图片。最大池化保留了数据内响应最大也是最强烈的值,相当于保留了照片中最重要的像素点,因此可以在减少计算的同时保留特征。

例如下图,选择池化尺寸为 2x2,在其中分别选出最大值,通过对 4 块区域的最大池化,最终构成了右边新的特征图。

2)平均池化

也叫均值池化,它是把特征图一个区域内的全部数值取平均值的池化方法。在操作上与最大池化相似,只不过最大池化是取最大值,而平均池化是取区域内数值和的平均数。

池化之后,新生成的特征图的尺寸会发生变化,假设 I=输入图像的尺寸,F=卷积层的核尺寸,S=移动步长,则输出尺寸 O=(I-F)/S+1。

以图 12 为例:I=4,F=2,虚线框处理完蓝色的部分后,会往右挪动 2 格,处理黄色的部分,因此 S=2,则 O=(4-2)/2 + 1=2。

在实际使用过程中,两种池化有不同的侧重点。
最大池化因为提取的是区域内的特征最强烈的像素,所以一般在网络的前几层使用,用来保留图像的纹理信息,
但走到深层的网络之后,特征图的尺寸越来越小,包含的语义信息逐渐增多,最大池化能够获取的信息就会减少。此时,平均池化的优势就显现出来了,在深层的网络中,它依然能够很好地保留背景和语义信息。

4.感受野


在卷积神经网络中,感受野(Receptive Field)是指卷积神经网络每一层输出的特征图上的像素点在输入图片上映射的区域大小,也就是特征图上的一个点对应输入图上的区域。感受野跟步长、卷积核尺寸有关,计算公式如下:

其中F(i, j)表示第 i 层对第 j 层的局部感受野, stride为卷积核移动步长,kernelsize为卷积核尺寸。

通过公式,你可以将感受野简单地理解为像素能够感应的范围大小

5.卷积神经网络


了解完卷积神经网络的基本概念,我们就可以来看看到底什么是卷积神经网络了。

我们回忆一下之前提起的神经网络模型。当网络输入一个数据之后,中间要经过若干个隐藏层,如果每一个隐藏层中的每一个神经元都跟前一层的神经元相连,会产生巨大的计算量。

举个例子,在一个如图 13 的全连接网络中,m-1 层有 1000 个神经元,m 层有 1000 个神经元,那么两层之间的全连接(图中蓝色的线)的数量就是 1000x1000=100 万个,如果神经网络有更多层,那连接的数量会变得更加庞大。

那有什么办法可以让连接的数量不那么多呢?

我们可以把 m 层中的每个节点都限定一下连接数量,比如每个节点只跟 10 个 m-1 层的神经元连接,如下图(图中只画出 3 个),这样一来,连接的数量就从 1 百万个变成了 10x1000=1 万个,减少了 100 倍。这个操作,就叫作稀疏连接(Sparse Connectivity),也叫参数减少

但在实际应用中,神经元的数量成千上万,每个神经元都不一样,只减少 100 倍远远不够,我们还需要继续减少。

现在 m 层的每个神经元连接的区域是 10 个神经元,如果我们把 m 层修改一下,改为只有相同的 10 个神经元的集合 F,然后用集合 F 不断地与 m-1 层的 10 个神经元连接,如下图所示:

F 会先跟 m-1 层的 1~10 节点连接,然后往下滑动,直到最后,跟 991~1000 节点连接。

集合 F 对应的操作,叫作权值共享,F 就是权值。换句话说就是每一次的连接都是用相同的权值,共享使用。你看,通过权值共享,连接的数量是不是又进一步减少了?

参数减少、权值共享是卷积神经网络最大的两个特点。

这时候你可能会有疑问,这个跟前面说的卷积计算方法为什么不太一样?

我们把 m-1 层的“一长条”神经元,变为方形或者矩形的像素矩阵,再把集合 F 也变成类似的方形矩阵,m-1 层就成了图片,集合 F 就成了卷积核(图 3),这就变成了卷积操作构成的网络,即卷积神经网络。

了解完卷积神经网络的概念和特点,我们就可以开始组装一个卷积神经网络了。在组装的过程中,你也能够更直观地了解卷积神经网络的构造和样式。

我们现在回顾一下,手头有什么可以用的零件:

  • 卷积核:提取特征。
  • 池化:减少参数和计算量。
  • 全连接层:分类。
  • 激活函数:表示更加复杂的学习过程

活用它们,我们就能用卷积神经网络来做一个简单的分类网络了。

恰好我们手上有一张图片,我们先用若干个卷积核提取图片的特征(如刚才所说,不同的卷积核可以提取到图片不同的特征信息)。

经过卷积操作后,我们在特征图上加一个激活函数。如下图所示,我们提取了第一层的特征。

我们继续加卷积层。要加多少其实并无定论,你可以加非常多,但最后要考虑计算量、合理性等方面的因素。方便起见,这里我只安排了 2 层卷积层。

卷积操作之后不要忘了激活函数
经过这一系列操作后,我们就有了很多提取出来的特征图。下一步,我们要对这些特征图变形,让它们变成向量的形式,这可以方便我们增加一层全连接层,从而将分类的结果映射到不同的类别上。如下图所示:


至此,我们就完成了一个最基本的基于卷积神经网络构建的分类网络。当然,网络还要有损失函数和优化函数,这些内容我在《04 | 函数与优化方法:模型的自我学习(上)》中已经讲过了。关于如何构建以及优化一个能够真正解决实际问题的卷积神经网络,我会在后续进一步地讨论。