前面机器学习的主要部分已经都涉及到了,虽然SVM的部分有些烂尾,并且中间也有好多地方因为自己没弄懂而一带而过……现在也应该开始深度学习的部分了,深度学习的内容没有机器学习那么有条理,可能就是看到哪写到哪,最主要的目的还是理清自己的思路。自己也在纠结一个知识点应该细致到什么程度,是否应该多加一些发散思维。但是又怕文章太过冗长,影响整体的结构。还是随性来吧。
本文主要讲解有关深度学习的相关内容,具体包括感知机、神经网络和反向传播算法、卷积神经网络中的卷积层和池化层,以及dropout、批量正则化和激活函数等内容。
一、深度学习概述
深度学习可以看作是机器学习的一个分支,现在一提到深度学习一般指的就是深层神经网络。神经网络这个东西在很早以前就被提出了,但是由于当时计算机计算能力的限制,所以一直没能投入实际应用。深度学习有两个非常重要的特性——多层和非线性。对于这两个特性的介绍会在后面的文章中慢慢介绍到。
二、感知机
感知机(perceptron)是二分类的线性分类模型,是支持向量机和神经网络的基础。 感知机模型只有输入层和输出层两层,并且输出层只有一个神经元,所以又被称作单层(不算输入层)神经网络。由于只有一个输出节点,所以感知机一般用于处理二分类问题,输出节点的值表示数据为某一分类的概率。其网络模型如下图所示:
由图可知,第i个输入神经元与输出神经元之间的权重可以表示为$w_i$,而$w_0$表示偏置值,其对应的输入神经元的值恒为1。感知机模型的工作流程是:先将输入值与其对应的权重相乘,再加上偏置值,最后将以上步骤得到的值送入激活函数,得到最终的输出。该流程可以用以下公式表示:
$$
y(v;\theta)=f(\sum_{i=1}^D{v_iw_i+w_0})=f(w^Tv+w_0)
$$
其中$v$是输入数据,$\theta$是指$w$和$w_0$等参数,D是数据集的个数。等式最右边是矩阵的表示形式。
感知机有个缺陷就是它无法解决异或问题,但是当为感知机增加一个隐藏层后就可以解决异或问题了。可以这样理解:神经网络的层数越多,学到的样本的特征越抽象,表达能力越强。当然层数过多也会带来很多问题。
所谓的激活函数就是一个“非线性函数”,常见的激活函数有:ReLU函数、Sigmoid函数和Tanh(双曲正切)函数等。它们的表达式和图像如下:
在我的理解中,激活函数的作用是它使得模型由线性模型变为非线性模型,而线性模型能够解决的问题是有限的,所以这就突破了线性模型自身的局限性。那么为什么线性模型能够解决的问题是有限的呢?如果只有全连接层时,一个输入数据经过两次线性传播(输入数据乘以权重),则有:
$$
y_1=xW_1
$$
$$
y_2=y_1W_2
$$
$$
y_2=(xW_1)W_2=x(W_1W_2)=xW$$
其中有$W_1W_2=W
$,也就是说只通过线性变换,任意层的全连接神经网络和单层神经网络在表达能力上没有任何区别,即线性模型的组合仍然是线性模型。
当然也可以对感知机进行进一步的拓展,将其输出节点个数改为多个,这样就可以处理多分类问题了。不过此时在任意两个输入和输出节点之间都要有一个权重值,并且激活函数改为采用sotfmax函数,其定义如下:
$$
s(z_k)=\frac{exp(z_k)}{\sum_{l=1}^K{exp(z_l)}}
$$
其中$z_k=w_k^Tv+w_{k0}$,$w_k$是第k个输入节点的权重向量,$v$是输入数据,$w_{k0}$是第k个输入节点的偏置值。
三、神经网络和反向传播算法
当前的神经网络通常有多层,不仅有输入和输出层,还有若干个隐藏层(在输入和输出层中间的层)。神经网络的学习中有两个基本问题:一是网络结构的学习,二是网络参数的学习。第一个问题尚未解决,而第二个问题通常采用反向传播(backpropagation, BP)算法来解决。反向传播算法由正向传播过程和反向传播过程组成。前向传递输入信号直至输出产生误差,反向传播误差信息更新权重矩阵。下面就来较为详细的介绍一下反向传播算法。
1. 反向传播算法
(1). 符号定义
首先给出一些定义:
$w_{ij}^l$ 表示第$l$层第$i$个神经元连接到第$(l-1)$层的第$j$个神经元之间的权重;
$b_i^l$ 表示第$l$层第$i$个神经元的偏置值;
$z_j^l$ 表示第$l$层第$j$个神经元激活前的值,即:
$$
z_j^l=\sum_j{w_{ij}^{l} a_j^{l-1}+b_j^{l-1}}
$$
$a_j^l$ 表示第$l$层第$j$个神经元激活后的值,即:
$$
a_j^l=\sigma(z_j^l)
$$
其中$\sigma$表示激活函数。
由于上一层的输出是下一层的输入,所以$z_j^l$和$a_j^l$没有用输入输出的字样,而是换了一种表达方式。
(2). 前向传播
上图中是一个三层的神经网络的示意图,其中标注了部分符号。前向传播过程就是先将上一层的输入乘以上一次层与本层的权重,再加上偏置,最后送入激活函数。不断重复该过程,数据就会从输入层一层层的传递到输出层,得到最后的结果。
(3). 损失函数和梯度下降
为了计算预测值与真实值之间的误差,以便更新网络的参数,还需要定义损失函数,这里就假设损失函数为最常用的平方损失函数:
$$
C=\frac{1}{2n}\sum_{i=1}^n||y_i-a_i^L||^2
$$
其中n是输出层神经元个数;L是神经网络的层数;y是样本的真实值;$a^L$是第L层的输出,也就是神经网络的预测值。
然后要做的就是用梯度下降算法,根据误差函数对每个权重$w_{ij}^l$的偏导数,来更新神经网络的权重,即:
$$
w_{ij}^l=w_{ij}^l-\eta\frac{\partial C}{\partial w_{ij}^l}
$$
其中$\eta$是学习率,更新时最关键的就是求解偏导数,下面来详细看一下。
(4). 链式求导法则
因为在前向传播时有:
$$
C=\frac{1}{2n}\sum_{i=1}^n||y_i-a_i^L||^2
$$
$$
a_j^l=\sigma(z_j^l)
$$
$$
z_j^l=\sum_j{w_{ij}^{l} a_j^{l-1}+b_j^{l-1}}
$$
三个式子的顺序与前向传播相比是倒着写的,从上到下分别是损失函数、激活函数、乘以权重并且加偏置。
由以上三个式子可知,损失函数对于输出层与前一层之间权重的偏导为:
$$
\frac{\partial C}{\partial w_{ij}^L}=\frac{\partial C}{\partial a_j^L}\cdot\frac{\partial a_j^L}{\partial z_j^L}\cdot\frac{\partial z_j^L}{\partial w_{ij}^L}\quad\quad\quad公式(1)
$$
而损失函数对于倒数第二层和倒数第三层之间权重的偏导为:
$$
\frac{\partial C}{\partial w_{ij}^{L-1}}=\sum_k\frac{\partial C}{\partial a_k^{L}}\cdot\frac{\partial a_k^L}{\partial z_k^L}\cdot\frac{\partial z_k^L}{\partial a_j^{L-1}}\cdot\frac{\partial a_j^{L-1}}{\partial z_j^{L-1}}\cdot\frac{\partial z_j^{L-1}}{\partial w_{ij}^{L-1}}\quad\quad\quad公式(2)
$$
给出了公式,再来解释一下公式是什么意思。对上图而言公式(1)就是求损失函数对$w_{ij}^3$的偏导,对于$\frac{\partial C}{\partial w_{12}^3}$来说只有$C\rightarrow G\rightarrow 损失函数$ 这一条路径与$w_{12}^3$有关,其中C是第2层的第1个神经元,G是第3层的第2个神经元,对应w的下标1和2。而公式(2)是求损失函数对$w_{ij}^2$的偏导,对于$\frac{\partial C}{\partial w_{23}^3}$来说,有$B\rightarrow E\rightarrow F\rightarrow 损失函数$ 和$B\rightarrow E\rightarrow G\rightarrow 损失函数$ 这两条路径,其中B是第1层的第2个神经元,E是第3层的第3个神经元,与w的下标2和3对应;而F和G是第3层的神经元的所有可能的情况。
(5). 引入$\delta$
从第4部分的链式求导公式可以看出,虽然求导的方法很简单,但是当神经网络层数较多时会导致公式过于冗长。并且损失函数对较高层(靠近输出层的层)的权重的导数在求解对较低层权重的导数时还会用得到,这就导致了大量重复的运算,这无疑会造成时间的浪费。为了避免重复运行,所以又引入了$\delta_j^l$,它表示第$l$层的第$j$个神经元产生的误差,其定义为:
$$
\delta_j^l=\frac{\partial C}{\partial z_j^l}
$$
公式1:最后一层神经网络产生的误差:
$$
\delta^L=\nabla_{a^L}C\odot\sigma^`(z^L)
$$
推导过程:
$$
\because\delta_j^L=\frac{\partial C}{\partial z_j^L}=\frac{\partial C}{\partial a_j^L}\cdot\frac{\partial a_j^L}{\partial z_j^L}
$$
$$
\therefore\delta^L=\frac{\partial C}{\partial a^L}\odot\frac{\partial a^L}{\partial z^L}
$$
其中 C是关于$a^L$的损失函数,$a^L$是关于$z^L$的激活函数,$\odot$是矩阵之间点对点的乘法运算符号,所以上式又可以写为:
$$
\delta^L=\nabla_{a^L}C\odot\sigma^`(z^L)
$$
公式2:每一层神经网络产生的误差:
$$
\sigma^l=((w^{l+1})^T\sigma^{l+1})\odot\sigma^`{(z_j^l)}
$$
推导过程:
$$
\because\delta_j^l=\frac{\partial C}{\partial z_j^l}=\sum_k\frac{\partial C}{\partial z_k^{l+1}}\cdot\frac{\partial z_k^{l+1}}{\partial a_j^l}\cdot\frac{\partial a_j^l}{\partial z_j^l}
$$
$$
=\sum_k\sigma_k^{l+1}\cdot\frac{\partial(w_{kj}^{l+1}a_j^l+b_k^{l+1})}{\partial a_j^l}\cdot\sigma^`{(z_j^l)}
$$
$$
=\sum_k\sigma_k^{l+1}\cdot w_{kj}^{l+1}\cdot\sigma^`{(z_j^l)}
$$
$$
\therefore\sigma^l=((w^{l+1})^T\sigma^{l+1})\odot\sigma^`{(z_j^l)}
$$
公式3:权重的梯度:
$$
\frac{\partial C}{\partial w_{ij}^l}=a_j^{l-1}\sigma_i^l
$$
推导过程:
$$
\frac{\partial C}{\partial w_{ij}^l}=\frac{\partial C}{\partial z_i^l}\cdot\frac{\partial z_i^l}{\partial w_{ij}^l}=\sigma_i^l\cdot\frac{\partial (w_{ij}^la_j^{l-1}+b_i^l)}{\partial w_{ij}^l}=a_j^{l-1}\sigma_i^l
$$
公式4:偏置的梯度:
$$
\frac{\partial C}{\partial b_i^l}=\sigma_i^l
$$
推导过程:
$$
\frac{\partial C}{\partial b_i^l}=\frac{\partial C}{\partial z_i^l}\cdot\frac{\partial z_i^l}{\partial b_i^l}=\sigma_i^l\cdot\frac{\partial (w_{ij}^la_j^{l-1}+b_i^l)}{\partial b_i^l}=\sigma_i^l
$$
至此,梯度下降过程中用到的偏导数都计算出来了,并且去除了冗余的部分,进行了一系列的化简。以上就是整个反向传播算法的所有内容。反向传播部分内容参考了一文搞懂反向传播算法和反向传播算法这两篇文章。
2.梯度消失和梯度爆炸
由于反向传播过程需要计算若干梯度的乘积,当梯度过小时(<1),则结果会越乘越小,甚至会溢出为NaN(not a number,非数字),这种现象被称为梯度消失。反之,若梯度过大时(>1),结果就会越乘越大,最终溢出,这种现象被称为梯度爆炸。解决梯度消失和梯度爆炸的方法有:
(1). 使用其他函数(ReLU函数、Tanh函数、softsign函数等)来代替sigmoid函数作为激活函数;
(2). 先逐层预训练,后全局微调,从而得到初始的权重值。用预训练得到的初始化权重代替随机初始化权重。所谓预训练就是把相邻两层看作一个神经网络,然后对其进行训练,得到权重;
(3). 当梯度超过或低于一定阈值时,对其进行截断;
(4). 添加权重正则化项;
(5). 使用LSTM(长短期记忆网络)结构。
四、卷积神经网络
深层神经网络模型的输入通常是一个向量,但是对于像图像等数据来说,每个像素与相邻像素之间可能存在一定的关系,对其进行矢量化可能会破坏这种相邻像素间的信息,同时传统的神经网络在处理图片时还存在参数比较多的问题,所以又提出了卷积神经网络(convolutional neural network, CNN)。在CNN中又引入了卷积(convolution)和池化(pooling)的操作。
CNN有两个特点,一是局部连接性,它是指每次的卷积操作只作用在图片的一小块区域上,而不是整张图片上;二是权值共享,它是指使用相同的卷积核在图片的不同区域做卷积操作。
1. 卷积层
卷积核通常是一个长和宽都为奇数的方阵,卷积层的作用是通过卷积核矩阵提取图像的局部特征。上图是对单通道图像(灰度图像)进行卷积和最大池化操作的过程。标有kernel的3*3矩阵是卷积核,在卷积过程中卷积核要与图片中每个3*3大小的矩阵的对应元素相乘并求和,从而得到一个值。比如卷积核与图片矩阵(蓝色)最左上角的3*3矩阵的对应元素相乘相加:1*0+0*0+0*1+0*0+1*1+1*0+0*1+0*0+1*0=1,也就是红色矩阵最左上角的“1”。下图是一个卷积操作的完整示意图:
除了相乘相加,还可以设置偏置值(bias),即相乘相加操作后得到的数再加上偏置值作为一次卷积的结果。大家会发现在上图中经过卷积操作后,结果图比原图变小了,为了使图片保持大小不变,还可以在图片的外围补一圈0,补0的宽度和高度由卷积核大小和原图大小可以确定。这个过程成为padding,其示意图如下:
在以上卷积中,每次移动的步数(stride)为1,其实也可以将其设置为其他值,如下图就是stride=2的卷积操作:
如果是三通道(RGB)的图片,则对该图片的卷积操作需要每个通道设置一个卷积核矩阵,对同一位置的卷积操作就是三个通道分别做卷积,然后将结果相加作为最终的卷积结果。经过卷积操作后图片的通道数会减少。
在某些教程中,卷积核也被称为滤波器,可以分为高通滤波器和低通滤波器两种。高通滤波器可以去掉低频信号,实现提取图像中物体边缘的功能;低通滤波器可以去掉高频信号,实现去噪声和模糊化图像的功能。下面就利用这两种滤波器分别对上图进行处理。上图分别是原图、高通滤波器处理结果、低通滤波器处理结果。
上图是一个高通滤波器,大家会发现它一共有9个元素,且元素之和为0。当它和图像做卷积操作时,如果某个元素值与周围元素值相差很小时,则卷积的结果会趋近于0(黑色);如果某个元素值与周围元素值相差很大时,则卷积后与原图的值会相差很大。而图像中物体的边缘通常是与周围的元素值相差很大的,所以高通滤波器可以提取图像中物体的边缘。
上图是一个低通滤波器,大家会发现所有的值都相等,并且元素值之和为1。如果图像中的某一个像素点和周围像素点的差值大的话,那么这个点就会被周围的像素给同化。从而可以去掉图像中的噪声,或者说让图片变得更模糊。
关于滤波器的这部分内容,主要参考了白话文讲计算机视觉之滤波器一文。
2. 池化层
池化操作有很多种,最常用的有最大池化和平均值池化两种,它的本质是下采样。
上图中标有max的2*2矩阵就是池化所用到的矩阵,你会发现它里面没有值,实际上只需要指定池化的大小即可。当池化大小为2*2时,就需要在图片中每个不重叠的2*2大小的矩阵的四个元素中取最大值,然后用该最大值代替原来2*2的矩阵。经过这个操作,图片会缩小一半,这样既保存了图像中关键的特征,又使得图片变小,有利于减少处理时间。
pooling操作具有平移不变性和大小不变性,这是指当物体的位置和大小发生改变时,仍能提取其特征。
3. 全连接层
全连接层(fully connected layers, FC)就是把相邻两层之间的任意两个神经元之间都连接起来,每条连接边上都有一个权重。全连接可以表示为$y=wx$,其中$w_{ij}$是前一层第$i$个神经元和后一层第$j$个神经元之间的权重。
由于全连接层的使用会导致神经网络的参数急剧上升,所以要尽量少用甚至不用全连接层。除此之外,全连接层的输入和输出神经元个数都是固定的,所以当原始输入的大小不合适时,就需要用其他方法来进行调整。
在卷积神经网络中,卷积层和池化层往往是相互结合来对图像进行处理的,通常一个卷积操作后面会跟上一个池化层,然后再对池化后的图像进行激活函数的处理,重复该过程若干次后再加入几个全连接层,最后经过损失函数得到最后的结果。
五、其他优化方法
1. Dropout
Dropout的思想就是在每次训练的迭代过程中,以某个概率$p$随机地让神经网络中的部分神经元灭活(可以看作是删除这些节点)。它减少了神经元之间的相互依赖,可以被看作是训练多个结构不同但是参数共享的神经网络,有效的避免了过拟合。需要注意的是,在建立好模型,用测试集进行测试的时候,不再采用dropout的方式,而是保留全部神经元进行测试。
2. Batch Normalization
在神经网络的训练过程中,网络的参数每经过一层,其分布就会发生一次变化,这就是所谓的“Internal Covariate Shift”问题。换句话说,参数的整体分布会逐渐靠近激活函数的左右两端,这就使得梯度会过大或过小,从而导致梯度消失或梯度爆炸。进一步导致了训练收敛慢、时间长。
而Batch Normalization(批量正则化、批量标准化)就是对每一个mini-batch的数据进行减去均值,再除以方差,也就是使其满足均值为0,方差为1的标准正态分布。实践证明,这样可以大大减少训练的时间,并且减少对dropout的依赖。
3. 数据增强
由于当样本数据较少时,模型的训练效果也会变差,所以可以采用数据增强(data augmentation)的方法来扩充样本数据。比如,对原图片做翻转、旋转、缩放、裁剪、平移和增加噪声等操作,从而生成新的样本数据。数据增强不仅扩充了样本数据,而且由于对不同大小、不同角度的图像都做了训练,所有还提高了模型的泛化能力。
- 本文作者: 俎志昂
- 本文链接: zuzhiang.cn/2019/09/01/deep-learning/
- 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!