# [筆記] 機器學習 神經網路 - 進階學習
### 代價函數與反向傳播算法 ( Backpropagation )
符號定義 :
* `L` : 神經網路中擁有的層數
* `sₗ` : 第 l 層中的單元個數
* `K` : 輸出的單元 ( 分類 ) 個數
#### 神經網路的代價函數

* 神經網路中的代價函數
* 過往的正規化就是計算輸出層中的每個單元
* 而在神經網路則是機算整個網路中的所有單元
* 此 i ( 後者 ) 並非訓練資料的 i ( 前者 )
#### 反向傳播算法 ( Backpropagation )
* 是一種訓練神經網路的常見方法,與梯度下降結合使用,目的是找到一組能最大限度地減小誤差的權重
* 演算法 :
假設有訓練資料 `{(x⁽¹⁾, y⁽¹⁾) ... (x⁽ᵐ⁾, y⁽ᵐ⁾)}`
設定 `Δᵢⱼ⁽ˡ⁾ := 0 ( for all l, i, j )`
`For i = 1 to m : `
1. 設定 `a⁽¹⁾ := x⁽ⁱ⁾`
2. 計算 `a⁽ˡ⁾ ( for l = 2, 3, ..., L )`
3. 使用 `y⁽ⁱ⁾` 計算 `δ⁽ᴸ⁾ = a⁽ᴸ⁾ - y⁽ⁱ⁾`,L 代表 output layer
4. 使用 `δ⁽ˡ⁾ = ((Θ⁽ˡ⁾)ᵀδ⁽ˡ⁺¹⁾) .* a⁽ˡ⁾ .* (1 - a⁽ˡ⁾)` 計算 `δ⁽ᴸ⁻¹⁾, δ⁽ᴸ⁻²⁾, ..., δ⁽²⁾`
* 激勵函數的導函數 : `g'(z⁽ˡ⁾) = a⁽ˡ⁾ .* (1 - a⁽ˡ⁾)`
5. `Δᵢⱼ⁽ˡ⁾ := Δᵢⱼ⁽ˡ⁾ + aⱼ⁽ˡ⁾δᵢ⁽ˡ⁺¹⁾`,向量化 : `Δ⁽ˡ⁾ := Δ⁽ˡ⁾ + δ⁽ˡ⁺¹⁾(a⁽ˡ⁾)ᵀ`
* `Dᵢⱼ⁽ˡ⁾ := (1 / m)(Δᵢⱼ⁽ˡ⁾ + λΘᵢⱼ⁽ˡ⁾)`,if j ≠ 0
* `Dᵢⱼ⁽ˡ⁾ := (1 / m) Δᵢⱼ⁽ˡ⁾`,if j = 0
* 
```Octave=
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));
m = size(X, 1);
J = 0;
Theta1_grad = zeros(size(Theta1));
Theta2_grad = zeros(size(Theta2));
% 前向傳播算法
a1 = [ones(m, 1) X];
z2 = a1 * Theta1';
a2 = sigmoid(z2);
a2 = [ones(size(a2, 1), 1) a2];
z3 = a2 * Theta2';
a3 = sigmoid(z3);
h = a3;
yVec = zeros(m, num_labels);
for i = 1:m
yVec(i, y(i)) = 1;
endfor
J = 1 / m * sum(sum(-1 * yVec .* log(h) - (1 - yVec) .* log(1 - h)));
regularator = (lambda / (2 * m)) * (sum(sum(Theta1(:, 2:end) .^ 2)) + sum(sum(Theta2(:, 2:end) .^ 2)));
J = J + regularator;
% 反向傳播算法
for t = 1:m
% 設定 a1 為 X
a1 = [1; X(t,:)'];
% 計算 aL ( L = 1, 2, ..., L )
z2 = Theta1 * a1;
a2 = [1; sigmoid(z2)];
z3 = Theta2 * a2;
a3 = sigmoid(z3);
% 計算 deltaL
yy = ([1:num_labels] == y(t))';
delta3 = a3 - yy;
% 計算 delta ( L - 1, L - 2, ..., 2 )
delta2 = (Theta2' * delta3) .* [1; sigmoidGradient(z2)];
delta2 = delta2(2:end); % 不計算 bias unit
% 計算 big delta
Theta1_grad = Theta1_grad + delta2 * a1';
Theta2_grad = Theta2_grad + delta3 * a2';
endfor
Theta1_grad = (1/m) * Theta1_grad + (lambda/m) * [zeros(size(Theta1, 1), 1) Theta1(:,2:end)];
Theta2_grad = (1/m) * Theta2_grad + (lambda/m) * [zeros(size(Theta2, 1), 1) Theta2(:,2:end)];
% 展開
grad = [Theta1_grad(:) ; Theta2_grad(:)];
end
```
* 在 Octave 上的前向傳播與反向傳播
```Octave=
function g = sigmoidGradient(z)
g = zeros(size(z));
g = sigmoid(z) .* (1 - sigmoid(z));
end
```
* 在 Octave 上的激勵函數的導函數
* `δⱼ⁽ˡ⁾` 就是 `aⱼ⁽ˡ⁾` 的偏差,也是代價函數的導數
* 

* `δ₁⁽⁴⁾ = a₁⁽⁴⁾ - y⁽ⁱ⁾`
* `δ₂⁽³⁾ = Θ₁₂⁽³⁾ * δ₁⁽⁴⁾`
* `δ₂⁽²⁾ = Θ₁₂⁽²⁾ * δ₁⁽³⁾ + Θ₂₂⁽²⁾ * δ₂⁽³⁾`
### 實作反向傳播算法
#### 矩陣展開成向量與還原
* 在 `fminunc()` 的高級優化算法中,通常都需要傳入向量,這時我們就需要將矩陣展開成向量
```Octave=
thetaVector = [Theta1(:); Theta2(:); Theta3(:);]
deltaVector = [D1(:); D2(:); D3(:)]
```
* 假設有 Θ⁽¹⁾、Θ⁽²⁾、Θ⁽³⁾,D⁽¹⁾、D⁽²⁾、D⁽³⁾,展開成向量
```Octave=
Theta1 = reshape(thetaVector(1:110), 10, 11)
Theta2 = reshape(thetaVector(111:220), 10, 11)
Theta3 = reshape(thetaVector(221:231), 1, 11)
```
* 假設 Θ⁽¹⁾ 為 10 * 11 矩陣、Θ⁽²⁾ 為 10 * 11 矩陣、Θ⁽³⁾ 為 1 * 11 矩陣,將他們還原
##### 應用
```Octave=
創建初始化的 Θ⁽¹⁾、Θ⁽²⁾、Θ⁽³⁾
展開成向量 initialTheta
fminunc(@costFunction, initialTheta, options)
```
* 使用 `fminunc()`
```Octave=
function [jval, gradientVec] = costFunction(thetaVec)
還原 thetaVec 成 Θ⁽¹⁾、Θ⁽²⁾、Θ⁽³⁾
使用梯度下降或反向傳播算法算出 D⁽¹⁾、D⁽²⁾、D⁽³⁾ 和 J(Θ)
展開 D⁽¹⁾、D⁽²⁾、D⁽³⁾ 成向量 gradientVec
end
```
* `costFunction()` 寫法
#### 梯度驗證 ( Gradient Checking )
* 通常使用反向傳播算法可能會出現許多 BUG,梯度驗證可以確保我們的反向傳播算法是如預期地工作的
* 算式 :

* 有多個 Θ 矩陣時 :

* 通常 `ϵ` 設定為 10⁻⁴,如果太小會導致數值問題
```Octave=
epsilon = 1e-4;
for i = 1:n,
thetaPlus = theta;
thetaPlus(i) += epsilon;
thetaMinus = theta;
thetaMinus(i) -= epsilon;
gradApprox(i) = (J(thetaPlus) - J(thetaMinus))/(2*epsilon)
end;
```
* 在 Octave 上的梯度驗證
* 驗證 `gradApprox ≈ deltaVector`,確定反向傳播算法沒有錯誤後,就必須把梯度驗證關掉了,因為梯度驗證會導致程式執行非常的慢
#### 隨機初始化 ( Random Initialization )
* 在神經網路中,將 Θ 的所有權重初始化為 0 是不適合的
* 這樣會因為對稱而造成所有節點更新為相同的值
* 所以我們需要使用下面方法來隨機初始化 ( 打破對稱 Symmetry breaking ) Θ 的權重 :

* 將每個 `Θᵢⱼ⁽ˡ⁾` 初始化為 `[−ϵ, ϵ]` 之間的隨機值,此 `ϵ` 並非梯度驗證的 `ϵ`
```Octave=
%If the dimensions of Theta1 is 10x11, Theta2 is 10x11 and Theta3 is 1x11.
Theta1 = rand(10, 11) * (2 * INIT_EPSILON) - INIT_EPSILON;
Theta2 = rand(10, 11) * (2 * INIT_EPSILON) - INIT_EPSILON;
Theta3 = rand(1, 11) * (2 * INIT_EPSILON) - INIT_EPSILON;
```
* 在 Octave 上的隨機初始化概念
```Octave=
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
```
* 在 Octave 上的隨機初始化
#### 總結
1. 神經網路的布局
* input 單元數 = 特徵數 ( x )
* output 單元數 = 類別數 ( y )
* hidden 單元數通常越多越好,但計算的成本通常會隨之增加,必須取得平衡
* 潛規則 :
* 至少有一層 hidden layer
* 有兩層以上的 hidden layer,則建議每層單元數都相同
2. 訓練神經網路
1. 隨機初始化權重 ( Θ )
2. 實現向前傳播取得 hΘ(x⁽ⁱ⁾)
3. 實現代價函數
4. 實現反向傳播算法以計算導數 
```Octave=
for i = 1:m,
使用 (x⁽ⁱ⁾, y⁽ⁱ⁾) 執行向前傳播與反向傳播
取得 a⁽ˡ⁾ 與 delta d⁽ˡ⁾ ( l = 2, ..., L )
end;
```
5. 使用梯度驗證確認反向傳播算法是否正確,**確認後關閉**
6. 使用梯度下降或其他最優化功能計算代價函數最小值,並獲得 Θ 的權重
###### tags: `筆記` `機器學習` `神經網路`