Programing Exercise 4:Neural Networks Learning

      本文讲的是coursera上斯坦福大学机器学习公开课(吴文达)课程第五周Neural Networks :learning 的课后作业。本文给出了作业实现的具体代码,并给出相应的注释和解释,供各位同学共同讨论学习。
        吴老师在课程里说过求解神经网络的一般顺序为:
(1)随机生成初始化theta
(2)正向传播,计算h(x)
(3)计算J值
(4)反向传播,计算误差delta,并计算J对矩阵内各个元素的偏导数
(5)利用gradient checking比较反向传播计算得到的偏导数与numerical gradient计算得到的偏导数的结果,确保反向传播准确无误
(6)利用高级优化算法,利用反向传播,得到最小化J,并得到对应的参数theta值即为所求

准备工作:
a.因为给出的sigmiod函数只能求解标量,我们要修改其代码,使其对矩阵也起作用。
修改sigmoid.m,得到以下代码:
function g = sigmoid(z)
[m,n] = size(z);
g = zeros(m,n);
for i = 1:m,
    for j = 1:n,
        g(i,j) =  1.0 ./ (1.0 + exp(-z(i,j)));%对z的每个元素求计算其逻辑值
    end
end
end
b.由于反向传播时需要求sigmoid函数的偏导数,需要修改sigmoidGradient.m,要求它对矩阵也有作用。
油微积分可证,g(z)的偏导数为:g(z)*(1-g(z)),具体原因需要参考微积分求导相关章节。
修改sigmoidGradient.m,得到以下代码:
function g = sigmoidGradient(z)
g = zeros(size(z));
g = sigmoid(z) .* (1 - sigmoid(z));%因为要使函数对矩阵也起作用,所以用点乘
end

下面按照本文头部给出的步骤的实现代码:
1.随机生成初始化theta
<strong>function W = randInitializeWeights(L_in, L_out)
epsilon_init = 0.12;
W = rand(L_out,L_in + 1) * 2 * epsilon_init - epsilon_init;%由于theta需要添加对应偏置单元的列,所以L_in + 1
end
</strong>
2.正向传播,计算h(x)
3.计算代价值J
这两部分的代码可以合成为一部分:
%未进行正则化操作
X = [ones(m,1) X];
for i = 1:m,                                                               %m为训练样本数,利用for遍历
    z2 = Theta1 * X(i,:)';                                                 %对第i个训练样本正向传播得到输出h(x),即为a3
    a2 = sigmoid(z2);
    a2 = [1; a2];
    z3 = Theta2 * a2;
    a3 = sigmoid(z3);
    J = J + sum(log(1 - a3)) + log(a3(y(i,:))) - log(1 - a3(y(i,:)));      %由于输出为10维向量,而y的值是1-10的数字,所以可以用y的值指示a3那些元素加,哪些不加
end                                                                        %a3(y(i,:))及指示训练样本对应的a3的元素
J = -1/m * J;
%正则化操作
temp = 0;                                                                  
for i = 1:hidden_layer_size,                                               %对Theta1除了第一列(与偏置神经元对应的那列)元素的平方求和                                        
    for j = 2:(input_layer_size + 1),
        temp = temp + Theta1(i,j)^2;
    end
end

for i = 1:num_labels,                                                       %对Theta2除了第一列(与偏置神经元对应的那列)元素的平方求和 
    for j = 2:(hidden_layer_size + 1),
        temp = temp + Theta2(i,j)^2;
    end
end

J = J + lambda/(2*m)*temp;
4.反向传播,计算偏导数
%利用反向传播法求取偏导数值,实际上这个循环可以和计算J值得循环合为一个,为了代码清晰,所以分开写了
delta3 = zeros(num_labels,1);                                              %反向传播,输出层的误差
delta2  = zeros(size(Theta1));                                             %反向传播,隐藏层的误差;输入层不计算误差
for i = 1:m,                                                               %m为训练样本数,利用for遍历
    a1 = X(i,:)';
    z2 = Theta1 * a1;                                                      %对第i个训练样本正向传播得到输出h(x),即为a3
    a2 = sigmoid(z2);
    a2 = [1; a2];
    z3 = Theta2 * a2;
    a3 = sigmoid(z3);
    delta3 = a3;                                                           %反向传播,计算得偏导数
    delta3(y(i,:)) = delta3(y(i,:)) - 1;
    delta2 = Theta2' * delta3 .*[1;sigmoidGradient(z2)];
    delta2 = delta2(2:end);
    Theta2_grad = Theta2_grad + delta3 * a2';
    Theta1_grad = Theta1_grad + delta2 * a1';
end 
Theta2_grad = 1/m * Theta2_grad + lambda/m * Theta2;                       %正则化,修正梯度值
Theta2_grad(:,1) = Theta2_grad(:,1) - lambda/m * Theta2(:,1);              %由于不惩罚偏执单元对应的列,所以把他减掉
Theta1_grad = 1/m * Theta1_grad + lambda/m * Theta1;                       %同理修改Theta1_grad
Theta1_grad(:,1) = Theta1_grad(:,1) - lambda/m * Theta1(:,1);
5.利用gradient checking比较反向传播计算得到的偏导数与numerical gradient计算得到的偏导数的结果,确保反向传播准确无误
这部分的实现代码已经在checkNNGradients.m中给出
6.利用高级优化算法,利用反向传播,得到最小化J,并得到对应的参数theta值即为所求
这部分代码在ex4.m中已经给出,具体为:
costFunction = @(p) nnCostFunction(p, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, X, y, lambda);


综上6个步骤,后两个步骤已经给出,前4个步骤可以合为一个文件nnCostFunction.m:

function [J grad] = nnCostFunction(nn_params, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, ...
                                   X, y, lambda)

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));

%计算J值
X = [ones(m,1) X];
for i = 1:m,                                                               %m为训练样本数,利用for遍历
    z2 = Theta1 * X(i,:)';                                                 %对第i个训练样本正向传播得到输出h(x),即为a3
    a2 = sigmoid(z2);
    a2 = [1; a2];
    z3 = Theta2 * a2;
    a3 = sigmoid(z3);
    J = J + sum(log(1 - a3)) + log(a3(y(i,:))) - log(1 - a3(y(i,:)));      %由于输出为10维向量,而y的值是1-10的数字,所以可以用y的值指示a3那些元素加,哪些不加
end                                                                        %a3(y(i,:))及指示训练样本对应的a3的元素
J = -1/m * J;

temp = 0;
for i = 1:hidden_layer_size,                                               %对Theta1除了第一列(与偏置神经元对应的那列)元素的平方求和                                        
    for j = 2:(input_layer_size + 1),
        temp = temp + Theta1(i,j)^2;
    end
end

for i = 1:num_labels,                                                       %对Theta2除了第一列(与偏置神经元对应的那列)元素的平方求和 
    for j = 2:(hidden_layer_size + 1),
        temp = temp + Theta2(i,j)^2;
    end
end

J = J + lambda/(2*m)*temp;

%利用反向传播法求取偏导数值,实际上这个循环可以和计算J值得循环合为一个,为了代码清晰,所以分开写了
delta3 = zeros(num_labels,1);                                              %反向传播,输出层的误差
delta2  = zeros(size(Theta1));                                             %反向传播,隐藏层的误差;输入层不计算误差
for i = 1:m,                                                               %m为训练样本数,利用for遍历
    a1 = X(i,:)';
    z2 = Theta1 * a1;                                                      %对第i个训练样本正向传播得到输出h(x),即为a3
    a2 = sigmoid(z2);
    a2 = [1; a2];
    z3 = Theta2 * a2;
    a3 = sigmoid(z3);
    delta3 = a3;                                                           %反向传播,计算得偏导数
    delta3(y(i,:)) = delta3(y(i,:)) - 1;
    delta2 = Theta2' * delta3 .*[1;sigmoidGradient(z2)];
    delta2 = delta2(2:end);
    Theta2_grad = Theta2_grad + delta3 * a2';
    Theta1_grad = Theta1_grad + delta2 * a1';
end 
Theta2_grad = 1/m * Theta2_grad + lambda/m * Theta2;                       %正则化,修正梯度值
Theta2_grad(:,1) = Theta2_grad(:,1) - lambda/m * Theta2(:,1);              %由于不惩罚偏执单元对应的列,所以把他减掉
Theta1_grad = 1/m * Theta1_grad + lambda/m * Theta1;                       %同理修改Theta1_grad
Theta1_grad(:,1) = Theta1_grad(:,1) - lambda/m * Theta1(:,1);
% =========================================================================

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

end




你可能感兴趣的:(机器学习,斯坦福大学,Networks,Neural,吴文达,神经网络在字符识别中的应用,Lear)