【机器学习】神经网络(二)——反向传播算法

前言:前篇博文神经网络(一)中我们使用了神经网络模型来识别0-9十个数字。但是,模型参数都是教程已经给好的,我们还没有知道这些参数是怎么训练出来的。本文就会介绍神经网络的学习算法——反向传播算法(Back Propagation, BP)。

一、反向传播算法
反向传播算法是目前用来训练人工神经网络的最常用且最有效的算法。其主要思想是:
(1)将训练集数据输入到神经网络的输入层,经过隐藏层,最后达到输出层并输出结果,这是神经网络的前向传播过程;
(2)由于神经网络的输出结果与实际结果有误差,则计算估计值与实际值之间的误差,并将该误差从输出层向隐藏层反向传播,直至传播到输入层;
(3)在反向传播的过程中,根据误差调整各种参数的值;不断迭代上述过程,直至收敛。

二、算法推导
1.神经网络的代价函数(cost function):

J(Θ)=1m[i=1mk=1Ky(i)klog(hΘ(x(i)))k+(1y(i)k)log(1(hΘ(x(i)))k)]+λ2l=1L1i=1sl+1j=1sl+1(Θ(l)ji)2

前一项的目的是使所有单元的误差和最小(采用对数损失函数),后一项是正则化项,旨在控制模型复杂度,防止过拟合;
对比Logistic回归:
J(θ)=1m[i=1my(i)loghθ(x(i))+(1y(i))log(1hθ(x(i)))]+λ2mj=1nθ2j

实际上,神经网络中有 K 个Logistic回归模型并行计算。

2.前向传播(forward propagation)
这里写图片描述
其实也就是通过神经网络,从输入参数到输出结果的计算过程(只计算一次):

a(1)=x
z(2)=Θ(1)a(1)
a(2)=g(z(2))
z(3)=Θ(2)a(2)
a(3)=g(z(3))
z(4)=Θ(3)a(3)
a(4)=hΘ(x)=g(z(4))

其中 g() 是sigmoid函数。

3.反向传播(backpropagation)
我们的目标是最小化cost function,于是使用梯度下降法对参数 Θ 进行更新:

Θ(l)ij=Θ(l)ijαW(l)ijJ(Θ)

其中关键步骤是计算偏导数。我们现在来讲一下反向传播算法,它是计算偏导数的一种有效方法。
反向传播算法的思路如下:给定一个样例 (x,y) ,我们首先进行“前向传播”运算,计算出网络中所有的激活值,包括 hΘ(x) 的输出值。之后,针对第 l 层的每一个节点 i ,我们计算出其“残差“: δ(l)i 。该残差表明了该节点对最终输出值的残差产生了多少影响。对于最终的输出节点,我们可以直接算出网络产生的激活值与实际值之间的差距,我们将这个差距定义 δ(nl)i (第 nl 层表示输出层)。例如对于下面的4层神经网络:
这里写图片描述
对于输出层来说,残差的计算比较容易:
δ(4)=a(4)yj

对于隐藏层来说,计算就较为复杂:
δ(3)=(Θ(3))Tδ(4).g(z(3))
δ(2)=(Θ(2))Tδ(3).g(z(2))

其中, g(z)=g(z).(1g(z))
详细的推导可参见: http://deeplearning.stanford.edu/wiki/index.php/%E5%8F%8D%E5%90%91%E4%BC%A0%E5%AF%BC%E7%AE%97%E6%B3%95
现在我们把前向传播和后向传播作一个比较:
这里写图片描述
表面上反向传播是在逐层计算每个神经元的误差,其实 δ(l)i 本质上是代价函数 J(Θ) 对加权和 z z=(Θ(l))Tx )的求导结果。引用一下课程中的解释:
这里写图片描述
反向传播完整的伪代码如下:
这里写图片描述
利用反向传播算法计算梯度Matlab代码如下:

function [J grad] = nnCostFunction(nn_params, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, ...
                                   X, y, lambda)
% Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
% for our 2 layer neural network
Theta1 = reshape(nn_params(1:hidden_layer_size * (input_layer_size + 1)), ...
                 hidden_layer_size, (input_layer_size + 1));

Theta2 = reshape(nn_params((1 + (hidden_layer_size * (input_layer_size + 1))):end), ...
                 num_labels, (hidden_layer_size + 1));

% Setup some useful variables
m = size(X, 1);

% You need to return the following variables correctly 
J = 0;
Theta1_grad = zeros(size(Theta1));
Theta2_grad = zeros(size(Theta2));

% forward propogation
a1 = [ones(m,1), X];
z2 = a1 * Theta1';
a2 = [ones(m,1), sigmoid(z2)];
z3 = a2 * Theta2';
a3 = sigmoid(z3);
% back propogation
for i = 1:m
    % cost for each sample
    yVec = zeros(num_labels,1);
    yVec(y(i)) = 1;
    cost = log(a3(i,:)) * (-yVec) + log(1 - a3(i,:)) * (-(1 - yVec));
    J = J + cost;

    % partial derivative for each sample
    delta3 = a3(i,:)' - yVec;
    delta2 = Theta2(:,2:end)' * delta3 .* sigmoidGradient(z2(i,:)');
    Delta2 = delta3 * a2(i,:);
    Delta1 = delta2 * a1(i,:);
    Theta2_grad = Theta2_grad + Delta2;
    Theta1_grad = Theta1_grad + Delta1;
end

J = J / m;
Theta1_grad = Theta1_grad / m;
Theta2_grad = Theta2_grad / m;

% regularization
Theta1_r = [zeros(hidden_layer_size,1), Theta1(:, 2:end)];
Theta2_r = [zeros(num_labels,1), Theta2(:, 2:end)];
J = J + (sum(sum(Theta1_r.^2)) + sum(sum(Theta2_r.^2)))*lambda/2/m;
Theta1_grad = Theta1_grad + Theta1_r*lambda/m;
Theta2_grad = Theta2_grad + Theta2_r*lambda/m;

% Unroll gradients
grad = [Theta1_grad(:) ; Theta2_grad(:)];


end

4.梯度下降
Repeat {
compute forward propagation a(l) ;
compute back propagation δ(l)
compute gradient:

Θ(l)ijJ(Θ)=1mi=1mα(l)jδ(l)i

update:
Θ(l)ij:=Θ(l)ijαΘ(l)ijJ(Θ)

}
5.梯度检查
反向传播算法作为一个有很多细节的算法在实现的时候比较复杂,可能会遇到很多细小的错误。所以如果把BP算法和梯度下降法或者其他优化算法一起运行时,可能看起来运行正常,并且代价函数可能在每次梯度下降法迭代时都会减小,但是可能最后得到的计算结果误差较高,更要命的是很难判断这个错误结果是哪些小错误导致的。

解决该问题的方法是: 梯度检查 (Gradient Checking)
梯度检查(Gradient Checking)的思想就是通过数值近似(numerically approximately)的方式计算导数近似值,从而检查导数计算是否正确。虽然数值计算方法速度很慢,但实现起来很容易,并且易于理解,所以它可以用来验证例如BP算法等快速求导算法的正确性。

如前所述,机器学习中大部分算法的核心就是代价值计算和梯度计算,因此,在实现神经网络或者其他比较复杂的模型时候,利用Gradient Checking可以检查算法是否正确实现。

梯度检查 (Gradient Checking)的核心是导数的数值计算 ,公式如下:

ΘJ(Θ)J(Θ+ϵ)J(Θϵ)2ϵ

多参数情况的公式如下:
ΘJ(Θ)J(Θ1,,Θj+ϵ,,Θn)J(Θ1,,Θjϵ,,Θn)2ϵ

实现代码如下:
这里写图片描述
其中,
DVec :利用反向传播算法得到的导数,BP是一个比较有效率的计算导数的方法;
gradApprox :利用数值近似得到的导数,数值计算法速度很慢;
如果两者相等或者近似,最多几位小数的差距,那么就可以确信所实现的BP算法是正确的。

三、总结
神经网络是一个比较复杂的模型,也是深度学习的重要基础,以后还需继续深入学习。

发布了30 篇原创文章 · 获赞 97 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/HerosOfEarth/article/details/52280805