吴恩达机器学习练习4:神经网络学习(损失函数和正则化)

BP神经网络其信号是前向传播的,而在其计算其误差时是反向传播的。
本练习小节是执行反向传播算法并且将其应用到手写数字的识别当中。
1、神经网路
在前面的训练中,可以根据提供了权重值来预测手写数字。在本练习中将使用反向传播算法来为神经网络学习权重参数。
(1)数据的可视化

load('ex4data1.mat');
m = size(X, 1);
% Randomly select 100 data points to display
sel = randperm(size(X, 1));
sel = sel(1:100);
displayData(X(sel, :));

导入手写字体数字,可以得到矩形X和矢量y,其中矩阵X的维度为5000×400,其中5000表示为5000个样本,400表示为每个样本的像素为20×20,矢量y为X中每个样本对应的标签值(y = 1-10)。
吴恩达机器学习练习4:神经网络学习(损失函数和正则化)
m = size(X, 1)为样本的个数;
sel = randperm(size(X, 1))将数据1-m随机进行排序,返回为矢量;
sel = sel(1:100)选取随机序列中的前100的值;
displayData(X(sel, ????)显示样本中第sel个样本图像。
吴恩达机器学习练习4:神经网络学习(损失函数和正则化)
(2)神经网络模型
本小节使用的神经网络如下:

吴恩达机器学习练习4:神经网络学习(损失函数和正则化)

input_layer_size  = 400;  % 20x20 Input Images of Digits
hidden_layer_size = 25;   % 25 hidden units
num_labels = 10;          % 10 labels, from 1 to 10   
                          % (note that we have mapped "0" to label 10)

由于每个样本有400个特征参数,则神经网路的输入层神经个数为400,其输入分类为10个,则设置其输出神经个数为10。

设置其隐藏层神经元个数为25。
(3)损失函数
在没有进行正则化前,神经网络的损失函数为:
吴恩达机器学习练习4:神经网络学习(损失函数和正则化)

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

nn_params为矢量化的权重参数Theta1和Theta2;input_layer_size为神经网络输入神经元个数;hidden_layer_size为神经网络隐藏层神经元个数;num_labels为分类数量,即输出神经元个数;X为输入训练数据;y为样本对应的标签;lambda为正则化参数。
先尝试计算第一个样本的损失值:

%使用X的第一个样本计算J
y_label = zeros(num_labels,1);
a1 = [1;X(1,:)'];%第一层神经网络激励,401*1
z2 = Theta1*a1;%第二层神经网络,25*1
a2 = [1;sigmoid(z2)];%第二层神经网络激励,26*1
z3 = Theta2*a2;%第三层神经网络,10*1
a3 = sigmoid(z3);%第三层神经网络激励,10*1
h = a3;%神经网络的输出,10*1
y_label(y(1)) = 1;%第一个数据的标签值,10*1
%通过公式计算出第一个样本数据的损失值
J = (y_label'*log(h)+(1-y_label)'*log(1-h))*(-1);

其结果为:

J =

    0.0345

使用for循环将所有样本的损失值计算出来并求平均:

%使用for循环计算所有样本的损失并求平均
for i = 1:m
    y_label = zeros(num_labels,1);
    a1 = [1;X(i,:)'];%第一层神经网络激励,401*1
    z2 = Theta1*a1;%第二层神经网络,25*1
    a2 = [1;sigmoid(z2)];%第二层神经网络激励,26*1
    z3 = Theta2*a2;%第三层神经网络,10*1
    a3 = sigmoid(z3);%第三层神经网络激励,10*1
    h = a3;%神经网络的输出,10*1
    y_label(y(i)) = 1;%第i个数据的标签值,10*1
    %通过公式计算出第一个样本数据的损失值
    J = J+(y_label'*log(h)+(1-y_label)'*log(1-h))*(-1);
end
J = J/m;

运行程序有:

Feedforward Using Neural Network ...
Cost at parameters (loaded from ex4weights): 0.287629 
(this value should be about 0.287629)

说明使用for循环求损失值代码是正确的。
下面将程序改写成矢量的形式,提高其运行速率。

%改写成矢量计算形式
X = [ones(m,1),X];%对输入样本增加第一列,值为1。5000*401
A1 = X';%第一层激励,输入矩阵。401*5000。
Z2 = Theta1*A1;%第二层神经网络,25*5000
A2 = [ones(1,m);sigmoid(Z2)];%第二层神经网络激励,26*5000
Z3 = Theta2*A2;%第三层神经网络,10*5000
p = sigmoid(Z3);%神经网络的输出,10*5000
y_label = zeros(10,m);%重新改写矩阵y,与神经网络的输出为同一形式
for i = 1:m
    y_label(y(i),i) = 1;
end
J = sum(sum(y_label.*log(p)+(1-y_label).*log(1-p)))/(-m);

其运算结果也为正确。
可以将代码精简为:

y_label = zeros(10,m);%重新改写矩阵y,与神经网络的输出为同一形式
for i = 1:m
    y_label(y(i),i) = 1;
end

J = sum(sum(y_label.*log(sigmoid(Theta2*[ones(1,m);...
    sigmoid(Theta1*[ones(m,1),X]')]))+...
    (1-y_label).*log(1-sigmoid(Theta2*[ones(1,m);...
    sigmoid(Theta1*[ones(m,1),X]')]))))/(-m);

其运行结果为:

Feedforward Using Neural Network ...
Cost at parameters (loaded from ex4weights): 0.287629 
(this value should be about 0.287629)

(4)正则化损失函数

正则化损失函数的公式为:
吴恩达机器学习练习4:神经网络学习(损失函数和正则化)
注意:此处公式有误,对Theta1求和时应该是k=2-401,对Theta2求和时应该是k=2-26。
修改和增加代码:

T1_squ = sum(Theta1.*Theta1);
T2_squ = sum(Theta2.*Theta2);
%正则化损失函数
J = sum(sum(y_label.*log(sigmoid(Theta2*[ones(1,m);...
    sigmoid(Theta1*[ones(m,1),X]')]))+...
    (1-y_label).*log(1-sigmoid(Theta2*[ones(1,m);...
    sigmoid(Theta1*[ones(m,1),X]')]))))/(-m)+...
    (sum(T1_squ(2:end))+sum(T2_squ(2:end)))*lambda/(2*m);

其运行结果为:

Checking Cost Function (w/ Regularization) ... 
Cost at parameters (loaded from ex4weights): 0.383770 
(this value should be about 0.383770)
Program paused. Press enter to continue.

附上nnCostFunction的所有源代码:

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

%使用X的第一个样本计算J
%y_label = zeros(num_labels,1);
%a1 = [1;X(1,:)'];%第一层神经网络激励,401*1
%z2 = Theta1*a1;%第二层神经网络,25*1
%a2 = [1;sigmoid(z2)];%第二层神经网络激励,26*1
%z3 = Theta2*a2;%第三层神经网络,10*1
%a3 = sigmoid(z3);%第三层神经网络激励,10*1
%h = a3;%神经网络的输出,10*1
%y_label(y(1)) = 1;%第一个数据的标签值,10*1
%通过公式计算出第一个样本数据的损失值
%J = (y_label'*log(h)+(1-y_label)'*log(1-h))*(-1);

%使用for循环计算所有样本的损失并求平均
%for i = 1:m
%    y_label = zeros(num_labels,1);
%    a1 = [1;X(i,:)'];%第一层神经网络激励,401*1
%    z2 = Theta1*a1;%第二层神经网络,25*1
%    a2 = [1;sigmoid(z2)];%第二层神经网络激励,26*1
%    z3 = Theta2*a2;%第三层神经网络,10*1
%    a3 = sigmoid(z3);%第三层神经网络激励,10*1
%    h = a3;%神经网络的输出,10*1
%    y_label(y(i)) = 1;%第i个数据的标签值,10*1
    %通过公式计算出第一个样本数据的损失值
%    J = J+(y_label'*log(h)+(1-y_label)'*log(1-h))*(-1);
%end
%J = J/m;

%改写成矢量计算形式:
%X = [ones(m,1),X];%对输入样本增加第一列,值为1。5000*401
%A1 = X';%第一层激励,输入矩阵。401*5000。
%Z2 = Theta1*A1;%第二层神经网络,25*5000
%A2 = [ones(1,m);sigmoid(Z2)];%第二层神经网络激励,26*5000
%Z3 = Theta2*A2;%第三层神经网络,10*5000
%p = sigmoid(Z3);%神经网络的输出,10*5000
%y_label = zeros(10,m);%重新改写矩阵y,与神经网络的输出为同一形式
%for i = 1:m
%    y_label(y(i),i) = 1;
%end
%J = sum(sum(y_label.*log(p)+(1-y_label).*log(1-p)))/(-m);

%精简矢量化运算代码如下:
y_label = zeros(10,m);%重新改写矩阵y,与神经网络的输出为同一形式
for i = 1:m
    y_label(y(i),i) = 1;
end
%未正则化的损失函数
%J = sum(sum(y_label.*log(sigmoid(Theta2*[ones(1,m);...
%    sigmoid(Theta1*[ones(m,1),X]')]))+...
%    (1-y_label).*log(1-sigmoid(Theta2*[ones(1,m);...
%    sigmoid(Theta1*[ones(m,1),X]')]))))/(-m);
T1_squ = sum(Theta1.*Theta1);
T2_squ = sum(Theta2.*Theta2);
%正则化损失函数
J = sum(sum(y_label.*log(sigmoid(Theta2*[ones(1,m);...
    sigmoid(Theta1*[ones(m,1),X]')]))+...
    (1-y_label).*log(1-sigmoid(Theta2*[ones(1,m);...
    sigmoid(Theta1*[ones(m,1),X]')]))))/(-m)+...
    (sum(T1_squ(2:end))+sum(T2_squ(2:end)))*lambda/(2*m);
% -------------------------------------------------------------

% =========================================================================

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

end