这周和上周的作业一样,数据集还是5000个 20x20 像素的手写字母,这周会用到反向传播的算法。一共5段需要提交的代码,如果上周的内容弄清楚了,跟着教程的话作业是不太难的(虽然课程视频内容很烧脑)。
Feedforward and cost function 30
Regularized cost function 15
Sigmoid gradient 5
Neural net gradient function (backpropagation) 40
Regularized gradient 10
Feedforward and cost function
因为多元分类的神经网络中 y 是一个横向的向量,包含了从0到9的标签。但是如果我们要训练一个神经网络,需要将 y 转化为一个只有0和1的矩阵。例如 x1 的标签为5,那 y 的第一行只有第五个元素是1,其他都是0. 我们可以用一个 for loop 完成(可能还有我不知道的更简洁的方法)。
Recode y
Y = zeros(m,num_labels);for i = 1:m Y(i, y(i)) = 1;end
接着就是构建我们的神经网络模型了,和上周的内容差不多。只不过因为神经网络的公式不同,我们需要多加一个sum. 这个公式不难理解,代价函数求的是预测值和实际结果的平方差,
神经网络的正则化有些复杂,乍一看一个Σ套一个Σ,不过在 matlab 里写的时候就是一个sum套一个sum, 注意括号和公式就可以了(如果结果不对多半是公式写错了哈哈)。
神经网络代价函数公式
ex4 中的代价函数(Theta1 Theta2 分开来写更明了)
对于前半部分这两个Σ, K 为该神经网络输出层的标签数(本作用中为10),m 为样本数,我们先把一个样本的10个标签的代价求和,再将 m 个样本的代价求和。
作业提示强调不要把 bias 算进去,之前在逻辑回归里是没有算 theta 的第一个元素,这里是跳过 Theta1 和 Theta2 的第一列。
% add bias a1 = [ones(m,1) X]; % hidden layer z2 = a1 * Theta1'; % add bias a2 = [ones(m,1) sigmoid(z2)]; z3 = a2 * Theta2'; a3 = sigmoid(z3); cost = 1/m * sum(sum(-Y .* log(a3) - (1-Y) .* log(1-a3))); % skip 1st column of Theta1 and Theta2 regCost = lambda/(2*m) * (sum(sum(Theta1(:,2:end) .^ 2))+ sum(sum(Theta2(:, 2:end) .^ 2))); J = cost + regCost;
Sigmoid gradient
这个比较简单。我犯的一个错误是用了 * . 虽然在主函数里得到了正确的结果0.25,但是提交的时候出现了问题。所以提交前用 test code 测试一下会比较好(这些 test code 真的很有用)。这里应该用 .* 因为是矩阵元素相乘。
g = sigmoid(z) .* (1-sigmoid(z));
Backpropogation Algorithm
作业里的 checkNNGradients.m 是一段单独的程序,里面的Theta1, Theta2 和之前的不一样,专门用来检测偏导数计算是否正确。其实怎么写公式里面都有,但是......
偏导数计算步骤
但是我在电脑前坐了2个多小时一直在写这段代码!(可能是自己智商比较捉急吧...)因为矩阵的 size 总是傻傻分不清,就不停在报错说 Matrix dimensions must agree...
计算的时候可以用 size 函数看矩阵大小
我的代码有点冗余...
% ==== Part 2 ==== Delta1 = zeros(size(Theta1)); Delta2 = zeros(size(Theta2));for i=1:m a_1 = X(i,:)'; % add bias a_1 = [1; a_1]; % hidden layer z_2 = a_1' * Theta1'; a_2 = sigmoid(z_2); % add bias a_2 = [1; a_2']; % output layer z_3 = a_2' * Theta2'; a_3 = sigmoid(z_3); % compute the error delta_3 = a_3 - Y(i, :); % compute error on the second layer tmp = (Theta2' * delta_3')'; delta_2 = tmp(2:end).* sigmoidGradient(z_2); Delta2 = Delta2 + delta_3' * a_2'; Delta1 = Delta1 + delta_2' * a_1'; end Theta1_grad = 1/m * Delta1; Theta2_grad = 1/m * Delta2;
写完后执行一下 checkNNGradients
, 可以看到偏导数确实很相近:
Gradient checking result
Regularized Neural Networks
最后这步也是比较简单的,注意点就是不要把 bias 算进去。 Theta1 和 Theta2 是两个矩阵,这里也是无视他们的第一列。我尽量写得短一点。
% ==== Part 3 ==== % regularize Theta1_grad Theta1_grad(:,2:end) = Theta1_grad(:,2:end) + lambda / m * Theta1(:, 2:end); % regualize Theta2_grad Theta2_grad(:,2:end) = Theta2_grad(:,2:end) + lambda / m * Theta2(:, 2:end);
Learning parameters
到这里就完成作业啦!运行下面的代码用 fmincg 返回 Theta 值,可以看到正确率大概为95.68%!
options = optimset('MaxIter', 50);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, ~] = 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)); pred = predict(Theta1, Theta2, X); fprintf('\nTraining Set Accuracy: %f\n', mean(double(pred == y)) * 100);
Learning parameters using fmincg
还有最后的显示隐藏层也很有趣呢!
Display hidden layer
作者:郭舒简
链接:https://www.jianshu.com/p/9b5c606ad2b0