文章目录
前言
在初次学习深度学习的时候,必不可少的接触卷积神经网络(CNN),本篇文章就记录一下通过应用CNN来做分类时,自己遇到的一些问题和踩到的一些坑。
- 本文使用的时Pytorch 1.1.0版本,更多关于pytorch的资料可到pytorch官网和pytorch中文网查找资料,有CUDA 10.1版本来进行GPU加速(搞深度学习没GPU基本完蛋)。
CNN原理
CNN网络是一种用来处理局部和整体相关性的计算网络结构,被应用在图像识别(CV)、自然语言处理(NLP)甚至是语音识别领域,因为图像数据具有显著的局部与整体关系,其在图像识别领域的应用获得了巨大的成功。
其实说白了CNN能在CV领域取得巨大成功最重要的就两个点:
-
局部连接
局部连接的思想,是受启发于生物学里的视觉系统结构,视觉皮层的神经元就是仅用局部接受信息。对于二维图像,局部像素关联性较强。这种局部连接保证了训练后的滤波器能够对局部特征有最强的响应,使神经网络可以提取数据的局部特征; 下图是一个很经典的图示,左边是全连接,右边是局部连接。 -
权值共享
权值共享,即计算同一深度的神经元时采用的卷积核参数是共享的。
- 总结:这里就体现了卷积神经网络的奇妙之处,使用少量的参数,却依然能有非常出色的性能。上述仅仅是提取图像一种特征的过程。如果要多提取出一些特征,可以增加多个卷积核,不同的卷积核能够得到图像不同尺度下的特征,称之为特征图(feature map)。
CNN的结构
以图像分类任务为例,CNN一般包含5种类型的网络层次结构:
- 输入层(input)。一般输入层都是对原始图像经过数据的预处理之后的像素矩阵,往往数据预处理的越好,结果就越好。
- 卷积层(conv2d)。局部连接,权值共享,说白了就是做特征提取,卷积层越深提取到的特征就越丰富,分类也就越准确。
- 批归一化层(Batch Normalization)。虽然我们对输入数据进行了归一化处理,但是输入数据经过 σ(WX+b) 这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大。如果我们能在网络的中间也进行归一化处理,是否对网络的训练起到改进作用呢?答案是肯定的。
- **层(ReLU)。**层(Activation Layer)负责对卷积层抽取的特征进行**,由于卷积操作是由输入矩阵与卷积核矩阵进行相差的线性变化关系,需要**层对其进行非线性的映射。**层主要由**函数组成,即在卷积层输出结果的基础上嵌套一个非线性函数,让输出的特征图具有非线性关系。
- 池化层(Pooling)。作用是对感受域内的特征进行筛选,提取区域内最具代表性的特征,能够有效地降低输出特征尺度,进而减少模型所需要的参数量。
- 全连接(FC)。负责对卷积神经网络学习提取到的特征进行汇总,将多维的特征输入映射为二维的特征输出通常低维度特征对应任务的学习目标。
CNN卷积层的基本参数
卷积层中需要用到卷积核(滤波器或特征检测器)与图像特征矩阵进行点乘运算,利用卷积核与对应的特征感受域进行划窗式运算时,需要设定卷积核对应的大小、步长、个数以及填充的方式。以最简单的例子进行说明:输入信号的形式为,表示batch size,表示channel个数,分别表示特征图的高和宽。
-
in_channels
(int) – 输入信号的通道 -
out_channels
(int) – 卷积产生的通道 -
kerner_size
(int or tuple) - 卷积核的尺寸 -
stride
(int or tuple, optional) - 卷积步长 -
padding
(int or tuple, optional) - 输入的每一条边补充0的层数 -
dilation
(int or tuple, optional) – 卷积核元素之间的间距 -
groups
(int, optional) – 从输入通道到输出通道的阻塞连接数 -
bias
(bool, optional) - 如果bias=True,添加偏置
参数存在如下关系:
input:
output:
采坑问题系列
1.深度学习为什么要“深”?
前提:在一定范围内。
- 深度神经网络是一种特征递进式的学习算法,浅层的神经元直接从输入数据中学习一些低层次的简单特征,例如边缘、纹理等。而深层的特征则基于已学习到的浅层特征继续学习更高级的特征,从计算机的角度学习深层的语义信息。
- 在神经元数量相同的情况下,深层网络结构具有更大容量,分层组合带来的是指数级的表达空间,能够组合成更多不同类型的子结构,这样可以更容易地学习和表示各种特征。同时也意味着由**函数带来的非线性变换的嵌套层数更多,就能构造更复杂的映射关系。
2.CNN不适合那些问题?
这个问题可是困惑了我好久好久。最终得出一个结论:理论指导实践
CNN之所以这么火,在其原理上:局部连接,权值共享,所以多用于图像领域,效果显著。原因就在于这“局部连接”,图片是像素你可以理解是相互独立的,没有关联的。而像文本、音频等数据是相互依赖的,解决思路更多的是RNN(TextCNN,LSTM等)。
由于本人一开始跟风,上来就直接来了个ResNet-18来对截面数据进行分类,结果准确率低得可伶……
综上:CNN跟适合CV领域,RNN跟适合NLP领域,机器学习更适合日志数据领域。
3.batch size的设置与网络的关系?
批次大小指定一次性流入模型的数据样本个数,根据任务和计算性能限制判断实际取值,在一些图像任务中往往由于计算性能和存储容量限制只能选取较小的值。在相同迭代次数的前提下,数值越大模型越稳定,泛化能力越强,损失值曲线越平滑,模型也更快地收敛。
- 内存利用率提高了,大矩阵乘法的并行化效率提高。
- 跑完一次 epoch(全数据集)所需的迭代次数减少,对于相同数据量的处理速度进一步加快。
- 在一定范围内,一般来说 Batch_Size 越大,其确定的下降方向越准,引起训练震荡越小,损失值曲线越平滑,模型也更快地收敛。
4.kernel_size的大小设置?
卷积核的大小定义了卷积的感受野,在早期的卷积神经网络中(如AlexNet、ZFNet),用到了一些较大的卷积核(11×11和7×7),这种大卷积核反而会导致计算量大幅增加,不利于训练更深层的模型,相应的计算性能也会降低。后来的卷积神经网络(VGG、GoogLeNet等),发现通过堆叠2个3×3卷积核可以获得与5×5卷积核相同的感受视野,同时参数量会更少,因此可以认为,在大多数情况下通过堆叠较小的卷积核比直接采用单个更大的卷积核会更加有效。
但是,这并不是表示更大的卷积核就没有作用,在某些领域应用卷积神经网络时仍然可以采用较大的卷积核。譬如在自然语言处理领域,由于文本内容不像图像数据可以对特征进行很深层的抽象,往往在该领域的特征提取只需要较浅层的神经网络即可。在将卷积神经网络应用在自然语言处理领域时,通常都是较为浅层的卷积层组成,但是文本特征有时又需要有较广的感受域让模型能够组合更多的特征(如词组和字符),此时直接采用较大的卷积核将是更好的选择。
总结:
- kernel_size不是越大越好,对于CV领域,应采取较小卷积核(3x3)来加深网络结构;对于NLP领域,较大卷积核效果更佳。
- 单独的1×1极小卷积核只能用作分离卷积而不能对输入的原始特征进行有效的组合,极大的卷积核通常会组合过多的无意义特征从而浪费了大量的计算资源。
5.每层卷积是否只能用一种尺寸的卷积核?
事实上,同一层特征图可以分别使用多个不同尺寸的卷积核,以获得不同尺度的特征,得到的新特征可以看作不同感受域提取的特征组合,相比于单一卷积核会有更强的表达能力。
6.为什么要用Batch Normalization(BN)?
批归一化(BN)算法的优点:
- 减少了人为选择参数。在某些情况下可以取消 dropout 和 L2 正则项参数,或者采取更小的 L2 正则项约束参数;
- 减少了对学习率的要求。现在我们可以使用初始很大的学习率或者选择了较小的学习率,算法也能够快速训练收敛;
- 可以不再使用局部响应归一化。BN 本身就是归一化网络(局部响应归一化在 AlexNet 网络中存在)
- 破坏原来的数据分布,一定程度上缓解过拟合(防止每批训练中某一个样本经常被挑选到,文献说这个可以提高 1% 的精度)。
- 减少梯度消失,加快收敛速度,提高训练精度。
7.为什么需要非线性**函数?
- **函数对模型学习、理解非常复杂和非线性的函数具有重要作用。
- **函数可以引入非线性因素。如果不使用**函数,则输出信号仅是一个简单的线性函数。线性函数一个一级多项式,线性方程的复杂度有限,从数据中学习复杂函数映射的能力很小。没有**函数,神经网络将无法学习和模拟其他复杂类型的数据,例如图像、视频、音频、语音等。
- 使用非线性**函数 ,以便使网络更加强大,增加它的能力,使它可以学习复杂的事物,复杂的表单数据,以及表示输入输出之间非线性的复杂的任意函数映射。使用非线性**函数,能够从输入输出之间生成非线性映射。
8.如何选择**函数?
选择一个适合的**函数并不容易,需要考虑很多因素,通常的做法是,如果不确定哪一个**函数效果更好,可以把它们都试试,然后在验证集或者测试集上进行评价。然后看哪一种表现的更好,就去使用它。
以下是常见的选择情况:
- 如果输出是 0、1 值(二分类问题),则输出层选择 sigmoid 函数,然后其它的所有单元都选择 Relu 函数。
- 如果在隐藏层上不确定使用哪个**函数,那么通常会使用 Relu **函数。有时,也会使用 tanh **函数,但 Relu
的一个优点是:当是负值的时候,导数等于 0。 - sigmoid **函数:除了输出层是一个二分类问题基本不会用它。
- tanh **函数:tanh 是非常优秀的,几乎适合所有场合。
- ReLu **函数:最常用的默认函数,如果不确定用哪个**函数,就使用 ReLu 或者 Leaky ReLu(GANs),再去尝试其他的**函数。如果遇到了一些死的神经元,我们可以使用 Leaky ReLU 函数。
9.池化层的作用和设置?
池化操作通常也叫做子采样或降采样,在构建卷积神经网络时,往往会用在卷积层之后,通过池化来降低卷积层输出的特征维度,有效减少网络参数的同时还可以防止过拟合现象。
池化操作可以降低图像维度的原因,本质上是因为图像具有一种“静态性”的属性,这个意思是说在一个图像区域有用的特征极有可能在另一个区域同样有用。因此,为了描述一个大的图像,很直观的想法就是对不同位置的特征进行聚合统计。例如,可以计算图像在固定区域上特征的平均值 (或最大值)来代表这个区域的特征。
注意:但是池化层也不是越多越好,根据其原理可以发现,池化层增多在某种意义上会导致原始图像信息的算是,就像平均和最大并不完全能代表整体。
10.全连接层的作用?
大多数神经网络中高层网络通常会采用全连接层(Full Connected Layer),通过多对多的连接方式对特征进行全局汇总,可以有效地提取全局信息。但是全连接的方式需要大量的参数,是神经网络中最占资源的部分之一,因此可能具需要使用局部连接(Local Connected Layer),仅在局部区域范围内产生神经元连接,能够有效地减少参数量。
采坑为解决问题
1.为什么随着网络结构的加深,模型的性能为什么会提升过后降低?
2.对于网络结构的建立,conv2d、BatchNorm2d、ReLU、MaxPooling等应该如何搭配?
……
……
……