神经网络练习四-ex4
回了趟家,一路上来回的重复播放李健的几首歌,现在充电完成,准备工作!
Ex4:神经网络学习
在这一练习中,我们将把神经网络中的后向传播算法应用到手写识别中,在上一个练习中,通过实现神经网络的前馈传播,并用它来预测和写入数字与我们提供的权重(weights)。 在本练习中,我们将实现反向传播算法来学习神经网络的参数。
数据的图形化
这是与上一个练习中使用的数据集相同的数据集。 ex3data1.mat中有5000个训练示例,其中每个训练示例是数字的20像素×20像素灰度图像。 每个像素由表示该位置的灰度强度的浮点数表示。 20×20像素的网格被展开为400维向量,这些训练样本中的每一个在我们的数据矩阵X中成为一行。这给我们一个5000×400矩阵X,其中每行是训练样例 手写数字图像。
模型表述
我们的神经网络如下图所示。它有3层{输入层,隐层和输出层。 回想一下,我们的输入是数字图像的像素值。 由于图像的尺寸为20×20,所以给出了400个输入层单位(不计算总是输出+1的额外偏置单位)。 训练数据将通过ex4.m脚本加载到变量X和y中。
% Load saved matrices from file
load('ex4weights.mat');
% The matrices Theta1 and Theta2 will now be in your workspace
% Theta1 has size 25 x 401
% Theta2 has size 10 x 26
前馈(Feedback)和代价函数
前面的小节中总结了关于无正则化下的神经网络代价函数:
% 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));
Jtemp = zeros(m,1);
for i = 1:m
temp = zeros(num_labels,1);%!!!!
temp(y(i))=1;
a1 = [ones(1,1),X(i,:)];
a2 = sigmoid(Theta1*a1');
a2 = [ones(1,size(a2,2));a2];
a3 = sigmoid(Theta2*a2);
hThetaX = a3;
Jtemp(i) = sum(-temp.*log(hThetaX) - (1-temp).*log(1-hThetaX));
end
J = 1/m*sum(Jtemp);
正则化代价函数
规则化后的代价函数计算公式为:
注意不应对bias(偏差)相对应的项进行正则化。 对于矩阵Theta1和Theta2,这对应于每个矩阵的第一列。可以先使用现有的nCostFunction.m计算非规则化成本函数J,然后再为正则化条款添加成本。代码如下:
Jtemp = zeros(m,1);
for i = 1:m
temp = zeros(num_labels,1);%!!!!
temp(y(i))=1;
a1 = [ones(1,1),X(i,:)];
a2 = sigmoid(Theta1*a1');
a2 = [ones(1,size(a2,2));a2];
a3 = sigmoid(Theta2*a2);
hThetaX = a3;
Jtemp(i) = sum(-temp.*log(hThetaX) - (1-temp).*log(1-hThetaX));
end
J = 1/m*sum(Jtemp);
J = J + lambda/2/m*(sum(sum(Theta1(:,2:end).^2))+sum(sum(Theta2(:,2:end).^2)));
后向传播算法
在这部分练习中,将利用反向传播算法来计算神经网络成本函数的梯度。需要完成nnCostFunction.m,以便为grad返回适当的值。 计算梯度后,将通过使用诸如fmincg的高级优化器来最小化成本函数\(J(\Theta)\)来训练神经网络。
首先利用反向传播算法来计算(非规则化)神经网络参数的梯度。 在通过验证了不规则情况下的渐变计算是否正确后,将实现正则化神经网络的渐变。
S形梯度
S形函数的梯度可以计算为:\(g'(z)=\frac{d}{dz}g(z)=g(z)(1-g(z))\)
这里:sigmoid(z)=g(z)=\(\frac{1}{1+e^{(-z)}}\)
随机初始化
当训练神经网络时,重要的是随机初始化参数。 随机初始化的一个有效策略是随机选择\(\Theta^{(l)}\)的值,范围在[\(-\epsilon_{init},\epsilon_{init}\)]里面。 建议用\(\epsilon=0.12\)这个参数之间的这个范围足够小巧,使机器学习更有效率。
% Randomly initialize the weights to small values
epsilon init = 0.12;
W = rand(L out, 1 + L in) * 2 * epsilon init − epsilon init;
后向传播算法
对于输出节点,我们是可以直接得出网络的激励值和实际目标值之间的误查的,由于第3层即是输出层这里我们定义其为:\(\delta_j^{(3)}\),对于相应的隐藏层你可以计算\(\delta_j^{(l)}\)基于第(l+1)层的每个节点的误差项。
详细地,这里是反向传播算法步骤:首先应该在循环中实现步骤1到4,一次处理一个示例。 具体来说,为t = 1:m实现一个for循环,并在循环中放置第1-4步,第t次迭代对第t个训练示例(\(x(t),y(t)\)进行计算)。 步骤5将累积梯度除以m,以获得神经网络成本函数的梯度。
- 1、将输入层的值\(a^{(1)}\)设置为第t个训练样本\(x^{(t)}\)。 执行前馈传递,计算层2和3的激励值(\(z^{(2)};a^{(2)};z^{(3)};a^{(3)}\))。请注意,您需要添加+1项确保层\(a^{(1)}\)和\(a^{(2)}\)的激励量也包括偏置单元。 在Octave / MATLAB中,如果a_1是列向量,则添加一个对应于a_1 = [1; a_1]。
- 2、对于第三层中的每个输出单元k,设置:\(\delta_k^{(3)}=(a_k^{(3)}-y_k)\)其中\(y_k\in {0,1}\);用于指示当前训练样本是否属于类k(\(y_k\)= 1),或者属于不同类(\(y_k\)= 0),此时你可能会发现逻辑阵列有助于此任务的顺利执行。
-
4、使用以下公式从此示例中累加梯度。 这里注意:应该跳过或删除\(\delta_0^{(2)}\)。 在Octave / MATLAB中,去除\(\delta_0^{(2)}\)对应于delta_2 = delta_2(2:end)。
\(\Delta^{(l)}=\Delta^{(l)}+\delta^{(l+1)}(a^{(l)})^T\)
-
5、通过除以m获取(未正则化下)的神经网络代价函数梯度值:
for i = 1:m
temp = zeros(num_labels,1);%!!!!
temp(y(i))=1;
a1 = [ones(1,1),X(i,:)];
a2 = sigmoid(Theta1*a1');
a2 = [ones(1,size(a2,2));a2];
a3 = sigmoid(Theta2*a2);
delta3 = a3 - temp;
tempz2 = [0;sigmoidGradient(Theta1*a1')];
delta2 = (Theta2)'*delta3.*tempz2;
delta2 = delta2(2:end);
Theta1_grad = Theta1_grad + delta2*a1;
Theta2_grad = Theta2_grad + delta3*a2';
end
Theta1_grad = Theta1_grad/m;
Theta2_grad = Theta2_grad/m;
梯度检测
在设计的神经网络中,最小化成本函数\(J(\Theta)\) ,要对参数进行梯度检查,可以将参数\(\Theta^{(1)},\Theta^{(2)}\)“展开”为长向量\(\theta\),通过这样做,可以考虑代价函数为\(J(\theta)\),并使用 以下梯度检查程序:
假设函数\(f_i(\theta)\)等效于计算\(\frac{\partial }{\partial \theta_i}J(\theta)\),你想要检测函数\(f_i\)的输出结果是否为正确的偏微分的结果。
因此,对每一个i,你可以验证\(f_i(\theta)'\)的正确性:
这两个值相互接近的程度将取决于代价函数J。但假设\(\epsilon = 10^{-4}\),会发现上述的左侧和右侧将达到至少4个有效数字(并且通常更多)。computeNumericalGradient.m函数的代码如下:
numgrad = zeros(size(theta));
perturb = zeros(size(theta));
e = 1e-4;
for p = 1:numel(theta)
% Set perturbation vector
perturb(p) = e;
loss1 = J(theta - perturb);
loss2 = J(theta + perturb);
% Compute Numerical Gradient
numgrad(p) = (loss2 - loss1) / (2*e);
perturb(p) = 0;
end
正则化下的神经网络
在计算了后向传播算法后,需要对梯度做正则化处理,为了解决正则化问题,事实证明,在使用反向传播计算梯度后,可以将其添加为附加项。在你利用后向传播计算\(\Delta_{ij}^{(l)}\)后,你可以通过如下的方式添加正则处理:
%cost-regulartion
J = 1/m*sum(Jtemp);
J = J + lambda/2/m*(sum(sum(Theta1(:,2:end).^2))+sum(sum(Theta2(:,2:end).^2)));
%grad-regulartion
Theta1_grad = Theta1_grad + lambda/m*Theta1 ;
Theta2_grad = Theta2_grad + lambda/m*Theta2 ;
Theta1_grad(:,1) = Theta1_grad(:,1) - lambda/m*Theta1(:,1);
Theta2_grad(:,1) = Theta2_grad(:,1) - lambda/m*Theta2(:,1);
利用自带的函数优化参数
上面已经成功的实现了神经网络的代价函数与梯度的计算,我们希望通过利用fmincg函数进一步实现一组优异的参数集的计算。
% After you have completed the assignment, change the MaxIter to a larger
% value to see how more training helps.
options = optimset('MaxIter', 50);
% You should also try different values of lambda
lambda = 1;
% Create "short hand" for the cost function to be minimized
costFunction = @(p) nnCostFunction(p, ...
input_layer_size, ...
hidden_layer_size, ...
num_labels, X, y, lambda);
% Now, costFunction is a function that takes in only one argument (the
% neural network parameters)
[nn_params, cost] = fmincg(costFunction, initial_nn_params, options);
% Obtain Theta1 and Theta2 back from nn_params
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));
如果程序的运行是正确的,可以看到训练的准确率约为95.3%(由于随机初始化,这可能会变化约1%)。 通过训练神经网络进行更多迭代,可以获得更高的训练准确度。 这里可以通过尝试训练神经网络进行更多迭代(例如,将MaxIter设置为400),并改变正则化参数λ。 通过正确的学习设置,可以使神经网络完全适合训练集。
可视化隐藏层的计算结果
了解你的神经网络正在学习的一种方法是,可视化隐藏单元捕获的结果。 给定一个特定的隐藏单元,一种可视化它计算出什么的方法是找到一个将使其产生激励的输入x(即使激励值(\(a^{(l)}_i\))接近1)。 对于本例中需要训练的神经网络,注意\(\Theta^{(1)}\)的第i行是代表第i个隐藏单元的参数的401维向量。 如果我们舍弃偏差项,我们得到一个400维向量,表示从每个输入像素到隐藏单元的权重。
因此,隐藏单元捕获的“表示”的一种方法就是将这个400维矢量重新形成一个20×20的图像并显示出来。程序依然利用之前出现的displayData.m,显示的图形共25个单元,在已训练的网络中,可以发现隐藏的单元大致对应于在输入中查找笔画和其他图案的探测器(detectors)。