符号定义:
表示训练集中第
个数据的第
个属性值
表示神经网络的总层数,对于上图来说,
表示第
层的神经元个数(不包括偏置单元),对于上图,
类的分类问题,神经网络模型的输出层就有
个单元,即
,用一个列向量
表示输出的结果。
在Logistic回归中,代价函数为
,但是在神经网络中输出单元为
个,
所以对于神经网络,它的代价函数为:
,比较我们可以发现,其实就是增加了一些嵌套的求和符号,因为代价函数最终为一个标量,所以我们需要将
个输出单元的值相加,对于正则化参数也是如此。
一些说明:关于公式中的
,在神经网络中,只有最后一层输出层所使用的函数能称为
,即假设函数,其公式为
,这里的
为一个列向量,表示第
层神经元的激活值,
表示第 3 层连向第 4 层的权值,为一个矩阵,从这里我们可以看出,在神经网络中,假设函数只是输出层的特殊称谓,实际上,每一层神经元都会使用一次假设函数得到假设值传给下一层神经元。
反向传播算法(Backpropagation Algorithm),又称为误差逆传播算法,使用这个算法,使得神经网络模型可以自主地计算出合适的模型参数
,使得模型收敛到一个可能的最优解中。下面来简单介绍一下这个算法。
我们已知代价函数
,那么训练目标就是
,结合梯度下降的思想,我们很容易地想到,就是要求出代价函数对于每个模型权重
的梯度,即求解
,其中
表示第
层的第
个神经元和第
层的第
个神经元之间的权重。
在模型最初的时候,要先进行一次前向传播(Forward propagation),以此获得每个节点的原始激活值。
假设我们只有一个训练样本
,则公式步骤如下:
在BP算法中我们首先要计算
,它表示第
层的第
个神经元的误差,注意和前向传播不同的是,反向传播是从输出层向左逐层计算的。以上图中的神经网络模型为例,我们直接给出计算公式(先不必纠结如何得来的公式):
由于 Sigmoid 函数有个很好的性质:
,所以就有
,通过一些数学证明,我们可以得到:
,需要注意的是,这里的等式是忽略掉正则项的。
于是我们得到算法流程:
假设我们有训练集
,这里的
其实就是大写的
, 用于计算代价函数的偏导数
to
将
赋值成
使用前向传播从左向右逐层计算所有的
使用
计算
从右往左逐步计算
,或者写成向量化
`$\begin{aligned}
D{i, j}^{(l)} &:=\frac{1}{m}\left(\Delta{i, j}^{(l)}+\lambda \Theta_{i, j}^{(l)}\right), \text { if j } \neq 0 . \
D{i, j}^{(l)} &:=\frac{1}{m} \Delta{i, j}^{(l)} \text { If } \mathrm{j}=0
\end{aligned}$`
,且
回忆前面的Logistic回归的实现方法,我们利用了一个MATLAB中内置的优化算法 fminunc 来实现自动计算梯度,函数参数如下:
function [jVal, gradient] = costFunction(theta)
optTheta = fminunc(@costFunction, initialTheta, options)
其中 @costFunction
是我们所实现的损失函数的指针,损失函数的输入为
,输出为计算得到的代价值 jVa;l
以及梯度值gradient
,initialTheta
为初始的
值,options
为函数的可选项,这里用不到,返回的 optTheta
是进行梯度下降后的
值。但问题是,这里无论输入还是输出
,以及代价函数的 gradient
都是一个一维的列向量,而在神经网络中,这些参数都是一个个矩阵,所以我们首先需要进行矩阵的展开。
假设我们有一个三层的神经网络模型,其中第一层神经元个数
,
,
,则有:
在MATLAB中可以用 (:)
的方法将一个矩阵展开成一个向量,具体可以见:机器学习:MATLAB语法 2.2 操作数据
于是我们可以:
thetaVec = [Theta1(:); Theta2(:); Theta3(:)];
Dvec = [D1(:); D2(:); D3(:)];
分别将
存入 thetaVec
中,并将
存入 Dvec
中。
同时我们可以使用:
Theta1 = reshape(thetaVec(1:110), 10, 11);
Theta2 = reshape(thetaVec(111:220), 10, 11);
Theta3 = reshape(thetaVec(221:231), 1, 11);
将
从 thetaVec
中恢复回来,
也同理。
那么整体的流程就变成下面这样了:
initialTheta
fminunc(@costFunction, initialTheta, options)
进行梯度下降
对于function [jVal, gradientVec] = costFunction(thetaVec)
:- 从 `thetaVec` 中使用 `reshape` 提取出
- 使用前向传播和反向传播算法计算
和
- 将
展开成向量存入 gradientVec
有时候可能由于一些玄学问题导致梯度计算错误,但是你却很难发现,这里介绍了一种检测方法:
如上图所示,假设我们的
函数图像是一条这样的曲线,我们可以近似估算
,其中
由于神经网络的
是多个矩阵,所以我们要展开后再进行检测,具体如下:
对应的代码如下:
epsilon = 1e-4;
for i = 1:n,
thetaPlus = theta;
thetaPlus(i) += epsilon;
thetaMinus = theta;
thetaMinus(i) -= epsilon;
gradApprox(i) = (J(thetaPlus) - J(thetaMinus))/(2*epsilon)
end;
然后将计算得到的 gradApprox
与反向传播得到的梯度值 gradientVec
逐一比较,如果误差不大则说明没错。
需要注意的是,梯度检测步骤只在模型训练开始前进行一次,开始训练模型后就不用检测了,不然会很慢。
在线性回归中我们可以将参数初始为0,但是在神经网络中不行,因为如果初始化为0,则最后无论如何进行,最终的参数都是相同的,所以我们需要用一种更加科学的方式进行参数初始化,代码如下:
If the dimensions of Theta1 is 10x11, Theta2 is 10x11 and Theta3 is 1x11.
Theta1 = rand(10,11) * (2 * INIT_EPSILON) - INIT_EPSILON;
Theta2 = rand(10,11) * (2 * INIT_EPSILON) - INIT_EPSILON;
Theta3 = rand(1,11) * (2 * INIT_EPSILON) - INIT_EPSILON;
其中 INIT_EPSILON
为自己设置的值。
计算得到