这次打算以代码为主线,适当补充。
for c = 1:num_labels
initial_theta = zeros(n + 1, 1);
% Set options for fminunc
options = optimset('GradObj', 'on', 'MaxIter', 50);
[theta] = fmincg (@(t)(lrCostFunction(t, X, (y == c), lambda)), ...
initial_theta, options);
all_theta(c, :) = theta';
end
上面的意思就是采用梯度下降的方法,迭代50次(调用50次lrCostFunction
计算 θnew ),那么这个lrCostFunction
又是何方神圣呢?下面就是lrcostFunction的主要内容
h = sigmoid(X * theta);
J = 1/m * ( -y' * log(h) -(1-y)' * log(1-h)) + lambda/(2*m) * (theta(2:end)' * theta(2:end));
G = theta;
G(1) = 0;
grad = 1/m * X' * (h-y) + lambda/m .* G;
这里 X⊂Rm×(n+1),m是sample的个数,n是feature的个数, `y == c`就是分别对这几个数进行逻辑回归,训练各自的 θ 。 写成数学公式就和之前单个的一样,不再赘述。
得到了 θall ,就能用来进行分类了,代码如下:
pre = X * all_theta';
[maxVal, p] = max(pre, [], 2);
在训练集上的准确率94.96%。
什么是神经网络?
像这个样子,有好几层,中间是隐藏层,输入层,输出层。
目前看到的例子是用于分类,那么它和逻辑回归有什么异同点呢?
我是这么理解的,可以认为是一群逻辑回归,一层一层的。比如对于逻辑回归,计算出 a(2)1 就完事了,但是神经网络的话这只是其中的一小步。 比如上图中a^{(2)}=g(x^T\Theta), 这里x=$$\begin{bmatrix} x_0 \ x_1\x_2\x_3\end{bmatrix}$, $\Theta=\begin{bmatrix} \vdots & \cdots & \vdots \\end{bmatrix}$不写了好麻烦,$\Theta$就是个大矩阵$\subset R^{{(l1+1)} \times {(l2)}}$。
下面按照算法步骤讲解,首先要做的就是随机初始化 Θ ,这里是 Θ(1),Θ(2) ,为什么要做随机初始化呢?因为初始化全0的话最后一层就是全0,所以就“都一样”了。下面的W就是我们的 Θ
function W = randInitializeWeights(L_in, L_out)
W = zeros(L_out, 1 + L_in);
epsilon_init = 0.12;
W = rand(L_out, 1 + L_in) * 2 * epsilon_init - epsilon_init;
end
这里将 Θ(1),Θ(2) 都放到一个列向量里,方便传递参数,之后再“恢复原样”就行了。
initial_nn_params = [initial_Theta1(:) ; initial_Theta2(:)];
下面是核心步骤:
options = optimset('MaxIter', 50);
costFunction = @(p) nnCostFunction(p, ...
input_layer_size, ...
hidden_layer_size, ...
num_labels, X, y, lambda);
[nn_params, cost] = fmincg(costFunction, initial_nn_params, options);
首先设置终止条件,迭代50次,接着定义nnCostFunction
这里面计算了 Θ 的梯度以及 J ,fmincg
传入了三个参数,意思是利用初始的 Θ 迭代50次(调用costFunction
50次,这里迭代次数为终止条件,所以 Θ 梯度的计算尤为重要)。接下来看看nnCostFunction
里面究竟是什么,其实它的目的就是计算 Θ(Θ(1),Θ(1)的梯度,以及J)
%% 首先把Theta1, Theta2还原
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));
%% 样本个数m
% 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
a1 = [ones(m, 1) X];
z2 = a1 * Theta1';
a2 = sigmoid(z2);
a2 = [ones(m, 1) a2];
z3 = a2 * Theta2';
a3 = sigmoid(z3);
Y = zeros(m, num_labels);
for i = 1:num_labels
Y(find(y == i), i) = 1;
end
% 计算J
Jc = (-Y .* log(a3) - (1-Y) .* log(1-a3));
J = 1/m .* sum( Jc(:) );
% 加上正则项
theta1 = Theta1;theta1(:,1) = 0;t1 = theta1 .^ 2;
theta2 = Theta2;theta2(:,1) = 0;t2 = theta2 .^ 2;
Regu = lambda/(2*m) * (sum(t1(:)) + sum(t2(:)));
J = J + Regu;
%% 反向传播,为了计算Theta1, Theta2 的梯度
for t=1:m
delta3 = a3(t,:) - Y(t,:);
delta2 = delta3 * Theta2(:,2:end) .* sigmoidGradient(z2(t,:));
Theta2_grad = Theta2_grad + delta3' * a2(t,:);
Theta1_grad = Theta1_grad + delta2' * a1(t,:);
end
% 加上正则项
Theta1(:,1) = 0;Theta2(:,1) = 0;
Theta1_grad = 1/m .* Theta1_grad + lambda/m .* Theta1;
Theta2_grad = 1/m .* Theta2_grad + lambda/m .* Theta2;
% =========================================================================
% Unroll gradients
grad = [Theta1_grad(:) ; Theta2_grad(:)];
end
至此,正向(计算 J )&反向传播(计算 gradΘ )就搞定了。
还剩下一点没说,梯度检测,梯度检测就是为了看看 Θ 对不对,比如先迭代一次,会算出一个 Θ,J ,然后利用 J 算出数值梯度,并与求导算出的梯度对比。
贴上代码看看:
nn_params = [Theta1(:) ; Theta2(:)];
costFunc = @(p) nnCostFunction(p, input_layer_size, hidden_layer_size, ...
num_labels, X, y, lambda);
[cost, grad] = costFunc(nn_params);
numgrad = computeNumericalGradient(costFunc, nn_params);
disp([numgrad grad]);
如果误差很小,那么就可以安心的training了!实际training的时候一般要注释掉check的步骤,减少不必要的计算时间。
至此,关于数字识别的逻辑回归&神经网络算梳理复习完了,还是看着代码有一种更踏实的感觉,嘿嘿。