1. 程式人生 > >(三)Multi-class Classification and Neural Networks[多分類問題和神經網路]

(三)Multi-class Classification and Neural Networks[多分類問題和神經網路]

這次打算以程式碼為主線,適當補充。

問題:

手寫數字識別。
這裡寫圖片描述

方法一:邏輯迴歸

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;

這裡XRm×(n+1),msample

nfeature`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這裡面計算了Θ的梯度以及Jfmincg傳入了三個引數,意思是利用初始的Θ迭代50次(呼叫costFunction50次,這裡迭代次數為終止條件,所以Θ梯度的計算尤為重要)。接下來看看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Θ)就搞定了。

梯度檢測Gradient checking

還剩下一點沒說,梯度檢測,梯度檢測就是為了看看Θ對不對,比如先迭代一次,會算出一個Θ,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的步驟,減少不必要的計算時間。

至此,關於數字識別的邏輯迴歸&神經網路算梳理複習完了,還是看著程式碼有一種更踏實的感覺,嘿嘿。