jyroy

卷积神经网络

卷积神经网络(Convolutional Neural Network,CNN)又叫卷积网络(Convolutional Network),是一种专门用来处理具有类似网格结构的数据的神经网络。卷积神经网络一词中的卷积是一种特殊的线性运算。卷积网络是指那些至少在网络的一层中使用卷积运算来代替一般的矩阵乘法的神经网络。

卷积神经网络的出现,极大的缓解了全连接神经网络中存在的一个典型的问题:数据的波形被忽视了!众所周知,全连接神经网络在进行数据输入的时候,需要将一个二维或者三维的数据展平为一维的数据。而我们知道在计算机中图形是一个三维的数据,因为需要存储一些类似 RGB 各个通道之间关联性的空间信息,所以三维形状中可能隐藏有值得提取的本质模式。而全连接展平会导致形状的忽视。因此需要利用卷积神经网络来保持形状的不变。

典型应用场景:图像识别、语音识别等。

典型结构如下图所示:

为什么要使用卷积神经网络?

对于计算机视觉来说,每一个图像是由一个个像素点构成,每个像素点有三个通道,分别代表RGB三种颜色(不计算透明度),我们以手写识别的数据集MNIST举例,每个图像的是一个长宽均为28,channel为1的单色图像,如果使用全连接的网络结构,即,网络中的神经与相邻层上的每个神经元均连接,那就意味着我们的网络有\(28×28 =784\)个神经元(RGB3色的话还要3),hidden层如果使用了15个神经元,需要的参数个数(w和b)就有:\(28×28×15×10 + 15 + 10=117625\)个,这个数量级到现在为止也是一个很恐怖的数量级,一次反向传播计算量都是巨大的,这还只是一个单色的28像素大小的图片,如果我们使用更大的像素,计算量可想而知。

上面说到传统的网络需要大量的参数,但是这些参数是否重复了呢,例如,我们识别一个人,只要看到他的眼睛,鼻子,嘴,还有脸基本上就知道这个人是谁了,只是用这些局部的特征就能做做判断了,并不需要所有的特征。 另外一点就是我们上面说的可以有效提取了输入图像的平移不变特征,就好像我们看到了这是个眼睛,这个眼镜在左边还是在右边他都是眼睛,这就是平移不变性。 我们通过卷积的计算操作来提取图像局部的特征,每一层都会计算出一些局部特征,这些局部特征再汇总到下一层,这样一层一层的传递下去,特征由小变大,最后在通过这些局部的特征对图片进行处理,这样大大提高了计算效率,也提高了准确度。

神经网络中的各层

  • 输入层(Input Layer):主要是对原始的图像数据进行预处理
  • 卷积层(Convolution Layers):可以看作是输入样本和卷积核的内积运算。从前一层提取移位不变特征。即当输入数据是图像的时候,卷积层会以3维数据的形式接收输入数据,并同样以3维数据的形式输出至下一层。因此,在CNN中,可以(有可能)正确理解图像等具有形状的数据。注:卷积层的输入和输出数据通常又被称为特征图(Feature Map)。卷积层的输入数据称为输入特征图(Input Feature Map),输出数据称为输出特征图(Output Feature Map)。
  • 池化层(Pooling Layers):作用是减小卷积层产生的特征图尺寸。将前一层的多个单元的激活组合为一个单元。池化是缩小高、长方向上的空间的运算,通常减小一半。
  • 全连接层(Fully Connected Layers):收集空间扩散信息
  • 输出层(Output Layer):选择类

卷积操作

卷积运算相当于图像处理中的“滤波器运算”。卷积运算会对输入数据应用滤波器(Filter)

假设,一个初始的图像大小为 \(J*K\), 同时有 \(L\) 个管道,如下图所示:

我们应用一个 \(M*N\) 大小的滤波器。图中绿色的格子就是一个 3*3 的滤波器。卷积运算的整个操作为:将各个位置上滤波器的元素和输入的对应元素相乘,然后再求和(整个操作也被称为乘积累加运算),然后,将这个结果保存到输出的对应位置。

将计算过程公式化,公式如下:

\[Z_{j,k} = b + \sum_{m=0}^{M-1}\sum_{n=0}^{N-1}K_{m,n}V_{j+m,k+n} \]

其中 \(K_{m,n}\) 表示滤波器的 \(m,n\) 位置的数据,\(V_{j+m,k+n}\) 表示图像的 \(j+m,k+n\) 位置的数据,\(b\) 是偏置项。

新生成的卷积层大小,用公式表示为:

\[(J+1-M) * (K+1-N) \]

将计算过程图像化,如下图所示:

其中,滤波器的参数就是权重(Weights),同时还有有个偏置项(Bias),这个偏置项会被加到滤波器最后的输出上。

权重共享

我们可以根据上图看到,对一层中的每一个 \(M×N\) 块输入应用相同的权值,计算卷积层中的下一个隐藏单元,这就是个权重共享(Weight Sharing)的概念。

填充

填充(Padding):在进行卷积操作之前,有时候要向周围填入固定的数据,比如用数值 0 进行填充,也叫零填充(Zero Padding)。填充的宽度用字母 P 表示。

应用填充之后,卷积层的输出大小将会和卷积之前的层一样,如图所示,其中粉色的 6 * 7 的格子是原始的图像尺寸,我们在周围加上一圈数值为 0 的格子,用白色表示:

以步幅 1 进行卷积,如下图所示:

最终我们得到了一个大小为 6 * 7 的蓝色的卷积层,和原始图像的尺寸相同,如下图所示:

这主要也是为了避免卷积过程中过一个典型的问题:如果每次进行卷积运算都会缩小空间,那么在某个时刻输出大小就有可能变成 1,导致无法再应用卷积运算。为了避免这个情况的出现,就需要使用填充。

新的卷积层尺寸,公式为:

\[(J + 2P + 1 - M) * (K + 2P + 1 - N) \]

步幅

步幅(Stribe):应用滤波器间隔的位置称为步幅。在上面的例子中,采用的步幅为 1。

我们可以将步幅改为 2,那么卷积过程就会变成下图所示的:

可以看到应用滤波器的窗口的间隔变成了 2 个元素,如下图所示:

最终,新的一层的尺寸变为一个 3 * 4 的层。用公式表示为(设步长为\(s\)):

\[(1+\frac{(J-M)}{s}) * (1+\frac{(K-N)}{s}) \]

至此,结合卷积、填充、步幅相关的公式,我们可以得出最完整的卷积层尺寸计算公式:

\[(1+\frac{(J + 2P -M)}{s}) * (1+\frac{(K + 2P -N)}{s}) \]

3 维数据的卷积运算

在一开始,虽然我们假设了一个初始的图像大小为 \(J*K\), 同时有 \(L\) 个管道。但是我们讨论的卷积操作一直只在单层上进行的,下面我们就讨论一下在 3 维情况下的卷积运算。

当在通道方向上有多个特征图的时候,会按照通道进行输入数据滤波器的卷积运算,并将结果相加,从而得到输出。计算步骤如下图所示:

至此,我们的公式可以总结为:

\[Z^i_{j,k} = b^i + \sum_l\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}K^i_{m,n}V^l_{j+m,k+n} \]

其中 \(K^i_{m,n}\) 表示第 \(i\) 个滤波器的 \(m,n\) 位置的数据,\(V^l_{j+m,k+n}\) 表示图像第 \(l\) 层通道的 \(j+m,k+n\) 位置的数据,\(b^i\) 是第 \(i\) 个偏置项。根据之前的权重共享的概念,我们可以知道,每一层通道是共用一个滤波器的相同权重的,同样也共用相同的偏置。

激活函数

因为卷积层的操作也是一个线性的操作,所以我们会像处理深度前馈网络一样利用激活函数来引入非线性。

池化

池化(Pooling)是缩小高、长方向上的空间的运算。通过减少卷积层之间的连接,降低运算复杂程度。和卷积操作类似的地方是,池化操作同样要引入一个 Filter,通过不断地滑动,使滤器覆盖的区域进行合并,只保留一个值。

最典型的池化过程叫做最大池化(Max Pooling),如下图所示,这是一个单层的池化:

图中所示的步骤是,我们划定一个 \(2 * 2\) 的窗口,找出这个窗口中最大的那个值,作为下一层的数据,这个窗口以步幅 2 进行移动。一般来说,池化的窗口大小会和步幅设定成相同的值。

除了 Max Pooling 之外,还有 Average Pooling/Mean Pooling,即平均池化,是计算目标区域的平均值。在图像识别领域中,主要使用 Max Pooling。

重叠池化

重叠池化(Overlapping Pooling)是 AlexNet 中的一个概念。Overlapping Pooling 的概念和 No-Overlapping Pooling的概念相对。

  • Overlapping Pooling:当步幅小于窗口宽度,会使池化窗口产生重叠区域。可以提升预测精度、缓解过拟合。
  • No-Overlapping Pooling:当步幅等于窗口宽度,池化窗口没有重叠区域。如上图的展示。又叫一般池化,General Pooling

池化层的特征

  • 没有要学习的参数:池化层和卷积层不同,没有要学习的参数。池化层只是从目标区域中取最大值或平均值。
  • 通道数不发生变化:经过池化运算,输入数据和输出数据的通道数不会发生变化。即计算是按照通道独立进行的。
  • 对微小的位置变化具有鲁棒性(健壮):输入数据发生微小偏差时,池化仍会返回相同的结果。

全连接层

Full Connnected Layer,一般是作为最后的输出层使用,目的就是输出我们想要的结果,无论是分类,还是回归。

在全连接层之间的所有层中,特征都是使用矩阵表示的,所以再传入全连接层之前还需要对特征进行压扁,将他这些特征变成一维的向量,如果要进行分类的话,就是用sofmax作为输出,如果要是回归的话就直接使用linear即可。

神经网络的训练方法

误差更新方法

和深度前馈网络相同,神经网络中的参数训练也采用误差的反向传播算法。我们知道在全连接层中的更新方法是根据链式法则从上到下的逐层更新参数。那么卷积层和池化层是如何更新的呢?就是将卷积层和池化层转变成全连接层的形式来看。
如下图所示是池化层的误差传播。我们假设红色边框区域中的最大值为 \(n_{12}\),那么误差就只在 \(n_{12}\)\(n_{21}\) 之间传播,即只有这一条连接权重 \(w_{21}\) 为 1, 其他红色区域的连接权重为 0。对于蓝色区域也是同样的处理办法。

如下图所示是卷积层的误差传播。卷积层要看成只与特定单元相连接的全连接层。卷积核就相当于权重。卷积核的调整和深度前馈网络相同,也是从上层的连接权重开始逐层调整。

参数的设定方法

在构建和训练卷积神经网络的过程中,有非常多的参数需要设置,其中一部分是神经网络本身的参数,另一部分只与训练有关的参数。
与神经网络相关的主要参数如下:

  • 卷积层的卷积核大小、卷积核个数
  • 激活函数的种类
  • 池化方法的种类
  • 网络的层结构(卷积层的个数和全连接层的个数)
  • Dropout 的概率
  • 有无预处理
  • 有无归一化

与训练有关的参数如下:

  • Mini-Batch的大小
  • 学习率
  • 迭代次数
  • 有无预训练

参数的影响力对比

通常在构建和训练神经网络的时候,我们需要选择这些参数的最优组合,但是因为参数的数量过多,往往我们只能根据经验来选择。但是实际上有的参数对神经网络影响比较小,有的影响比较大。我们可以优先调整影响比较大的参数,再调整影响比较小的参数。假设我们有一个如下图所示的神经网络,应用 CIFAR-10 数据集进行训练,观察各个参数的影响。

参数的比较结果如下图所示:

从图中可以看出,卷积层的卷积核个数、激活函数的种类和输入图像的预处理对模型存在较大的影响,应该首先确定这些重要参数。其他的参数影响相对较小,只需要在设定好重要参数之后进行微调就可以。但是要注意,对于不同的数据集和问题,重要的参数情况可能有变化,需要根据实际问题和研究,这个例子只是参考作用。

典型的CNN

LeNet

原理

LeNet 是在 1998 年提出的进行手写数字识别的网络。具有连续的卷积层和池化层(准确的讲是只抽取元素的子采样层),最后经全连接层输出结果。基本结构如下:

  • C1:第一个卷积层的 5×5 的窗口从原始的 32×32 的图像中提取出 28×28 的特征数组。有6个卷积核,提取6种局部特征。
  • S2:然后再进行子抽样,将其大小减半为14×14,降低网络训练参数及模型的过拟合程度。
  • C3:第二个卷积层使用另一个 5×5 的窗口提取一个 10×10 的特征数组,使用了16个卷积核。
  • S4:第二个子采样层将其简化为 5×5。
  • C5、F6:这些激活然后通过两个全连接层进入对应数字 ‘0’ 到 ‘9’ 的10个输出单元。

和现在的 CNN 相比,LeNet 有几个不同点

  • 激活函数:LeNet中使用 Sigmoid 函数,而现在的 CNN 中主要使用 ReLU 函数。
  • 池化/子采样:LeNet 中使用子采样缩小中间数据的大小,而现在的 CNN 中 Max Pooling 是主流。

Pytorch 实现

import torch.nn as nn
class LeNet5(nn.Module):

     def __init__(self):
        super(LeNet5, self).__init__()
        # 1 input image channel, 6 output channels, 5x5 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120) # 这里论文上写的是conv,官方教程用了线性层
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = LeNet5()
print(net)

AlexNet

原理

AlexNet 是一个引发深度学习热潮的导火索。基本结构如下:

AlexNet 叠有多个卷积层和池化层,最后经由全连接层输出:

  • 5 个卷积层和 3 个全连接层
  • 利用重叠步幅的最大池化层
  • 1000 个类的 Softmax
  • 两个只在特定层进行交互的 GPU。上图中就是利用了两个 GPU,在第二个 Max Pooling 层进行交互,一直到最后进行输出两个 GPU 一直是在并行运算。

AlexNet 虽然在结构上和 LeNet 没有很大的不同,但是也有一些细节上的差异

  • AlexNet 的激活函数使用的是 ReLU
  • 使用进行局部正规化的 LRN(Local Response Normalization)层
  • 使用 Dropout

Pytorch 实现

因为 AlexNet 是 Pytorch 官方实现的,所以直接从 torchvision 包中调用

    import torchvision
    model = torchvision.models.alexnet(pretrained=False) #我们不下载预训练权重
    print(model)

各个神经网络对比


这是一个准确率和计算量之间的对比。横轴是数据量,纵轴是准确率。可以在一定程度上指导我们进行网络的选择!

面试常见问题

  • 神经网络中的权重共享是什么?
  • \(1 * 1\) 卷积的作用
  • 卷积层和池化层有什么区别
  • LeNet-5 结构
  • AlexNet 结构

分类:

技术点:

相关文章: