神经网络(Neural Network)是机器学习众多算法中的一种,其原理是模仿人脑内神经元之间信息的处理方式,希望借此完成回归模型和分类模型所难以实现的非线性预测。简单来说,你可以将神经网络看作一个复杂的多层复合函数,输入数据通过多层函数的嵌套计算从而求得预测结果。换个角度也可以把多层神经网络理解为将输入数据做多层特征变换的过程。
神经网络通常由三部分组成:输入层
、隐藏层
、输出层
。其中隐藏层可以有多层,各层间的单元通过参数$\theta$和激活函数$\sigma$相连接,结构如下图所示:
- $a_i^{(l)}$表示第l层的i号元素
- $\theta_{ij}^{(l)}$表示连接第l+1层i号元素和第l层j号元素间的参数
各层之间的连接关系可表示为:
上一节我们构建了一个简单的全连接神经元网络,并且了解了各层之间是通过怎样的连接方式相互关联起来的。那么我们如何来训练这个神经元网络呢?与线性回归模型和分类模型类似,我们先从损失函数入手。
- $L$表示神经网络总层数
- $S_l$表示第l层元素个数(不包括偏差元素$a_0^{(l)}$)
- $\delta_i^{(l)}$表示第l层i号元素与真实值再此节点的差值
- $x^{(i)},\hat{y^{(i)}}$表示一组训练输入和输出
神经网络中经常使用的损失函数仍然为交叉熵函数(CrossEntropy),它可以视为范化的逻辑回归损失函数,表示为:
如果使用正则化处理,则还需增加正则化项:
注意和之前正则化操作强调的相同:在正则化项中不包括$\theta_{i0}^{(l)}$这些连接偏差项的参数。
反向传播是一种用于神经网络快捷求解参数梯度的方法。由于连接了很多层参数,我们在计算梯度时需要多次采用链式法则,这里用神经网络中的某一部分来举例如何计算梯度:
不难看出,只要我们求出$\frac{\partial J}{\partial z’}$和$\frac{\partial J}{\partial z’’}$就能算出损失函数对$\theta_1$的梯度。除了顺序使用递归求解外,如果我们从输出层开始反向逐层计算梯度,则可直接求解,这便是反向传播。
梯度计算
综上所述,我们可以得出反向传播求解梯度的步骤为:
- 设置输入层:
- 前向传播逐层计算元素值:
- 计算输出层差值:
- 反向传播计算$\delta^{(L-1)},\delta^{(L-2)},\dots,\delta^{(2)}$:
- 计算梯度(计算时要去除l+1层的偏差差值$\delta_0^{(l+1)}$):
验证梯度
我们在使用反向传播计算梯度后,如果要检验梯度计算是否正确,可以采用以下的数值计算方法(即根据梯度的定义):
参数初始化
在回归问题和分类问题的梯度计算中,我们通常将参数初始化为0,但我们却不能在神经网络中沿用从做法。原因很简单,如果我们初始化参数为相同的值,那么在进行反向传播后每层参数的更新也都是相同的,这样神经网络就被降维成了一系列节点使神经网络失效,为了打破这种参数更新的对称性,我们要随机初始化参数矩阵,即:
其中随机范围$\epsilon$也可以根据参数连接的两层元素个数选取,公式为:
如果没有激活函数在各隐藏层之间进行参数传递时,无论你有多少层,其输出都是输入的线性组合,而这样就失去了使用神经网络的意义。于是需要我们在神经网络中需要引入一些非线性函数作为激励函数从而可以使神经网络的效果逼近任何可能的函数。
梯度消失问题 (Gradient Vanishing)
在神经网络中常用的激活函数有很多,当然sigmoid
函数也是其中一位,但在深层的神经网络中使用sigmoid函数会使神经网络的训练效果大打折扣,原因在于反向传播过程中,sigmoid函数的导数最大为0.25,这样嵌套多层到达最后一层(也即输入层)时的梯度会很趋近于0,这样在第一层的参数学习速率会尤其缓慢,但在最后几层的参数却会以大步长更新并很快收敛。如何处理这样畸形的梯度更新问题呢?方法很简单,就是放弃sigmoid函数,换用其他激活函数。
ReLU (Rectified Linear Unit)
ReLU是当今在搭建人工神经网络时最常使用的激活函数,其表达式为:
同时还有ReLU函数的变种Leaky ReLU
和PReLU
(如上图),他们的出现都是为了解决在DeadRelu问题,但没有研究证明这两个变种的使用效果优于ReLU
激活函数。
与传统的sigmoid激活函数相比,其优势主要体现在:
- 简化计算过程,降低计算成本;
- 避免了梯度消失问题
- 收敛速度更快
但也要注意在使用ReLU做激活函数的过程中,不能将梯度下降中的学习速率$\eta$设置的太高,否则可能导致DeadReLU问题,即有一些神经元可能永远无法被激活,导致其对应参数永远无法更新。
Maxout
maxout激活函数是一种可学习的激活函数,可以视为ReLU函数的范化。其表达式为:
其中k是需要手动设置的参数,表示每次取最大值的节点节点组个数,下图表示了使用maxout激活函数(k=2)的神经网络:
它的强大之处在于其拟和能力,理论上只要隐藏层神经元足够多,maxout函数就可以模拟任何凸函数。不过其具体使用中的效果还待日后实践后补充。
在神经网络训练完成后,我们还需要使用一些函数来对模型的训练效果做出评价。
交叉验证 (Cross Validation)
在神经网络中使用的交叉验证方法与回归问题中所使用的方法步骤基本相同,即将所有训练数据随机分成三组(比例可以相应调整):
- 第一组为训练集(60%),其主要作用是训练参数,损失函数为$J_{train}(\theta)$
- 第二组为交叉验证集(20%),其主要作用是调整模型中的一些超参数(如正则化参数$\lambda$等)以选择合适的模型或诊断模型存在的问题,损失函数为$J_{CV}(\theta)$
- 第三组为测试集(20%),其主要作用是测试并验证模型训练效果,损失函数为$J_{test}(\theta)$
分类问题评价函数 (Score Function)
在分类问题中的预测结果通常有四种状态:
- TP(真阳性):正样本被正确预测为正样本
- FP(假阳性):负样本被错误预测为正样本
- TN(真阴性):负样本被正确预测为负样本
- FN(假阴性):正样本被错误预测为负样本
我们可以使用预测精确度来评价模型的预测效果,即:
但在二分类问题中,我们的负样本通常远远大于正样本数。举个例子,我们有一组是否患有癌症的数据,其中99.5%可能都是为患癌症的样本。如果我们构建一个函数无论输入所有输出都为0,即不患癌症,这样的预测准确率为99.5%,很显然用精确度来评价模型好坏已经没有意义。现实生活中这样的数据偏斜问题(skewed data)是很常见的,于是出现了一种新的评价模型的方法:
- 在使用时要将数量较少的样本设置为正样本(y=1)
Precision
为模型寻找正样本的准确度Recall
为模型寻找正样本的全面性- $\beta$用以平衡Precision和Recall的权重
- $\beta < 1$ Precision权重更高
- $\beta = 1$ 两者权重相同
- $\beta > 1$ Recall权重更高
- 可以通过调节$h_{\theta}(x)$映射到y=1的阈值来改变F_Score
根据上述分析,我们可以列出神经网络的训练步骤:
- 寻找合适的网络架构(包括隐藏层数、隐藏层节点数、层间连接方式、损失函数、激活函数、梯度优化器等);
- 随机初始化参数矩阵;
- 前向传播计算每层节点的值;
- 计算损失函数$J(\theta)$;
- 反向传播计算每层参数梯度$\frac{\partial}{\partial \theta_{ij}^{(l)}}J(\theta)$;
- 用数值计算方法检验梯度计算是否正确;
- 使用梯度下降或其他优化算法最小化$J(\theta)$;
- 使用评价函数检验模型训练效果。
诊断误差来源
神经网络中的误差来源通常来自两个方面:偏斜(bias)和方差(varience),偏斜误差通常导致欠拟合(underfit)、而方差误差则导致过拟合(overfit)。这两种误差在神经网络中的表现有所不同:
- error from bias (underfitting)
- error from varience (overfitting)
拿正则化参数变化对训练集和交叉验证集损失函数的影响为例:
- 当$\lambda$很小时,训练集损失函数远大于验证集损失函数属于过拟合问题;
- 当$\lambda$很大时,训练集损失函数约等于验证集损失函数且都远大于0,故属于欠拟合问题。
绘制学习曲线
为了更易于区分是两种误差中的哪一种对我们的训练模型产生影响,我们可以采用绘制学习曲线的方法:
- 以训练样本数量为横轴,损失值为纵轴建立坐标系;
- 计算损失值时所使用的损失函数不包括正则化项;
- m=i时,训练使用的i个样本的要随机选取;
- 在计算包含i个训练样本的损失值时,可以通过测试多次取平均值的方式得到较为可靠的损失值。
当神经网络出现欠拟合或过拟合问题时,学习曲线也会有两种完全不同的表现:
- 欠拟合:随着m增大,训练集和验证集损失函数十分接近且远高于期望损失值;
- 过拟合:随着m增大,训练集和验证集损失函数以极慢的速度相互靠近,当m达到最大时仍有较大差距。
改进模型
- 针对欠拟合问题
- 引入更多的特征
- 改进模型(隐藏层数、隐藏节点数、激活函数、损失函数)
- 减小正则化参数$\lambda$
- 针对过拟合问题
- 获取更多的训练数据
- 筛减特征量
- 增大正则化参数$\lambda$
- Dropout(见后文)
Dropout
Dropout是一种应用于复杂前馈模型中防止过拟合的手段,它是通过在每个训练批次中随机删除p%的节点以阻止特征检测器的共同作用(相互关联的依赖关系)来提升神经网络的性能,以此得到范化性更强,不太依赖某些局部特征的训练结果。
Dropout的实现步骤大致分为以下几部分:
- 随机生成dropout向量$r_j^{(l)} \sim Bernoulli(p)$控制每层被删除的节点
- $\widetilde{a}^{(l)}=a^{(l)}*r^{(l)}$
- $a^{(l+1)}=\sigma(\theta^{(l)}*\widetilde{a}^{(l)})$
- 在使用模型测试时,则不需要再进行dropout,取而代之的是对于每个参数都要乘上(1-p)以保证其后继节点的数学期望值不变,即:$\theta_{test}^{(l)}=(1-p)\theta^{(l)}$
向量$r^{(l)}$中p%的元素为0,对应于该层被忽略的元素