BP神经网络结构
我们知道单层感知机(perceptron)的局限性:它无法解决异或(XOR)问题,或者其它线性不可分问题。
考虑下图的数据集的分类问题。这是一个线性不可分的问题,显然单个单层感知机无法将其正确分类。
但是我们可以通过组合三个单层感知机,来解决这个问题:
其中三个单层感知机可分别为:
需要注意的是,可以正确分类的感知机组合的选择不唯一。
从上述例子,我们可以看到:通过堆叠多个单层感知机,可以解决该线性不可分问题。这启发我们用一种新的更复杂的框架——神经网络(neural network)——来建模。它包括输入层、隐含层和输出层三部分。在一个神经网络中,输入层和输出层的层数仅为,但维度可以为任意维,即输入、输出分别既可以为标量(scaler)、又可以为向量(vector),甚至是二维矩阵(matrix)或高维数组(array)。在本章,我们仅考虑神经网络的输入、输出为向量,神经元结点的输入、输出为标量的情形。神经网络的隐含层却可以有多层(假设层数至少为)。在每一个隐含层或输出层,输入该层的元素首先进行仿射变换,再通过激活函数映射后输出。这个过程与人的神经细胞在传递神经信号的过程极为相似,故也常将每一层的每个仿射变换、激活函数的组合称为一个神经元。
仿射变换和激活函数映射在图中分别用符号和表示。如果仅从数学函数的角度看待神经网络,则可将其看做一个计算图,不同的变量结点(variable node)通过计算结点(operator node)发生相互作用,这也是Tensorflow的基本框架。
以下我们通过前文的例子来理解上述定义。
解决异或问题的多个单层感知机模型可以看做是一个具有一个隐含层的神经网络,其中激活函数为阶跃函数,即
显然单层感知机无法对回归问题建模,因为它的输出只能为0或1。为了改进这个缺陷,我们可以将激活函数替换为其他线性或非线性函数。常用的激活函数有:
激活函数 | 表达式 | 图像 |
---|---|---|
sigmoid函数 | ||
tanh函数 | ||
ReLU函数(Rectified Linear Unit) |
因此,现在我们就有了一般的神经网络结构——层级结构——每一层由多个结点组成,相邻层的结点相互全连接。输入层结点可视为将输入的标量通过恒等函数变换输出,隐含层和输出层则将输入的标量做仿射变换、激活函数变换后输出。
通过这样的神经元结构,我们能较好地理解神经网络模型,但在后续的网络权值更新过程中,计算图框架会更加适用。
损失函数和代价函数
同样的网络结构,搭配不同的仿射变换或激活函数,都能得到不同的模型。因此,我们自然会问:如何评价一个神经网络模型的效果呢?
首先,对一个给定的输入向量和真实的输出向量,我们定义模型的损失函数(loss function)来衡量模型对的预测值与的差异,这里我们假设神经网络模型的映射为。常用的损失函数如下:
损失函数 | 表达式 | 应用场景 |
---|---|---|
平方损失(square loss)函数 | 线性回归模型 | |
合页损失(hinge loss)函数 | 支持向量机模型(SVM) | |
0-1损失(0-1 loss)函数 | 用于理论分析及准确度的定义 | |
交叉熵损失(cross entropy loss)函数 | 二分类或多分类问题 | |
指数损失(exponential loss)函数 | Adaboost模型 |
其次,对于一个模型,我们更关心的是其在某个数据集上的表现而不仅局限于某个样本,因此,我们也定义了代价函数(cost function),来衡量模型在某个分布上的表现:
其中是在数据集的真实分布上求期望。在实际应用中,由于我们可能并不知道数据的真实分布,故常用数据集中每个样本点在特定损失函数下的损失平均值作为代价函数,例如均方误差(mean squared error),此时有
对于神经网络这样的参数模型,通常我们不仅考虑代价函数在数据集上的值,同时也考虑模型参数的复杂程度。因此我们希望模型优化的目标函数(objective function)定义为:
其中是正则项(regularization),它有助于防止过拟合。目标函数就是我们在实际求解神经网络参数用的准则,好的参数可以在训练集、测试集上都有较低的目标函数值。
需要注意的是,代价函数和目标函数在某些语境下并不区分,此时代价函数本身便包含正则项。
反向传播算法
在前面的章节,我们已经有了神经网络的结构与模型的评价标准,接下来,我们要考虑的是:给定数据集和神经网络结构,我们应该怎样让网络从数据中学习、更新参数?
在数值计算中,我们常用梯度下降法来求取一个函数的局部极小值,在神经网络中,我们可以将其推广,利用链式法则将误差从后往前传播,并利用梯度下降法更新相邻两层间的权重,最小化误差。
在图中,我们用表示神经网络中第层第个神经元的输入值(其中第层表示输入层),同时也是第层,用表示第层第个神经元对第层第个神经元贡献的权重,用表示第层与与第层仿射变换的偏移量,用表示第层第个神经元的输出值,为第层神经元的个数。我们也可以用向量和矩阵的形式将每个神经元结点表示为:
其中
而是分别作用在向量的每一个分量上(element-wise)。
以上图中的三层网络为例,这个网络的输出层只有一个神经元,假设我们已经计算出该结点仿射变换输出的误差,其下标表示第层第个神经元。读者也可以选择每个结点输出值的误差进行反向传播更新参数,但是选择仿射变换后的误差更加方便理解与计算,因此我们按照该约定进行反向传播算法的介绍。
- 更新隐含层与输出层之间的参数。
注意到误差与损失函数的关系:
其中表示逐元素相乘(element-wise multiplication / Hadamard product)。我们希望更新参数和,首先根据链式法则有
因此有
类似地,
因此由梯度下降法,我们可以用以下公式更新该神经网络中输出层之前的参数:
其中为人工设定的学习率,用于调整权重更新的步长。
需要注意的是,在实际数值计算中,需要先求解相应的偏导函数,再将和对应的具体数值代入得到对应的偏导数值。
- 误差反向传播。
我们希望计算出网络隐含层每个结点的误差,同样地根据链式法则计算偏导
其中
在上述计算中涉及向量的求导运算,具体的公式可以查阅Matrix Cookbook。
- 更新输入层与隐含层之间的参数。
有了隐含层的输出误差后,我们可以重复第一步的工作,利用梯度下降法再次更新误差。首先从每一个分量入手,
因此我们有
类似地,
我们可以用以下公式更新该神经网络中输出层之前的参数:
对于上述例子,至此我们已经完成了对一个训练样本上权重的一次更新。
对于更加一般的情况,假设我们有第层的输出结点误差向量,我们可以计算相应的梯度
其中相邻层误差的关系满足
在实际训练过程中,逐个训练样本更新权重的效率太低,而且不利于算法的收敛,因此在深度学习中常使用批次方法(mini-batch),权重每次更新只考虑某一小批次的训练样本,这时相应的更新方向即为各个样本更新方向的平均。
完整的反向传播算法在以下算法中给出,该算法给出了训练一个多层简单神经网络的一般流程。