機器學習筆記(3): 神經網路初步

jeefy發表於2024-06-08

神經網路應該由若干神經元組成。

前面的每一個神經元都會給到一個引數,將傳遞的所有引數看作一個向量 \(\vec x\),那麼此神經元的淨輸入為:

\[z = x \omega + b \]

其中 \(\omega\) 稱為權重向量

這裡認為 \(x\)向量,而 \(\omega\)向量。

神經元還有一個啟用函式 \(f(\cdot)\)

\[a = f(z) \]

稱為函式的活性值

一般來說,我們使用 Logistic 函式,即 \(\sigma(x) = \frac 1 {1 + exp(-x)}\) 作為啟用函式。

啟用函式

啟用函式有很多很多種,一般來說要滿足以下幾點:

  1. 連續且可導的非線性函式。
  2. 函式本身和其導數要儘可能簡單。
  3. 值域要在一個合適的區間內

這裡列舉幾種常見的函式。

Sigmoid 型

指一類兩端飽和的 S 型曲線。

飽和
\(\lim_\limits{x \to -\infty} f'(x) = 0\) 稱為左飽和,\(\lim_\limits{x \to \infty} f'(x) = 0\) 稱為右飽和。
同時滿足則稱為兩端飽和。

常見的 Sigmoid 型函式有 LogisticTanh

  • Logistic 函式

\[\sigma(x) = \frac 1 {1 + \exp(-x)} \]

其導數:

\[\sigma'(x) = \frac {\exp(-x)}{(1 + \exp(-x))^2} = \sigma(x) (1 - \sigma(x)) \]

  • Tanh 函式

\[{\rm tanh}(x) = \frac {\exp(x) - \exp(-x)}{\exp(x) + \exp(-x)} \]

其可以看作縮放平移後的 \(\sigma\),因為:

\[{\rm tanh}(x) = 2 \sigma(2x) - 1 \]

自然其導數:

\[{\rm tanh}'(x) = 4 \sigma(2x)(1 - \sigma(2x)) = \frac {4}{(\exp(x) + \exp(-x))^2} \]

實際上我們可以透過近似的方法去擬合這個函式,畢竟 \(e^x\) 也不是那麼好算的。

  • Hard-LogisticHard-Tanh 函式

\[{\rm hard-\sigma}(x) = \begin{cases} 1 & x > 2 \\ \frac x 4 + \frac 1 2 & x \in [-2, 2] \\ 0 & x < 2 \end{cases} \]

或者利用 \(\min, \max\) 簡化:

\[{\rm hard-\sigma}(x) = \max(\min(\frac x 4 + \frac 1 2, 1), 0) \]

類似的:

\[{\rm hard-tanh}(x) = \max(\min(x, 1), -1) \]

ReLU

也就是 Rectified Linear Unit,線性修正單元,定義為:

\[{\rm ReLU}(x) = \begin{cases} x & x \ge 0 \\ 0 & x < 0 \end{cases} \]

也就是 \({\rm ReLU}(x) = \max(x, 0)\)

當然,因為可能出現 死亡 ReLU 問題,所以一般有如下變形:

\[{\rm PReLU}(x) = \begin{cases} x & x \ge 0 \\ \gamma x & x < 0 \end{cases} \]

如果 \(\gamma = 0\) 則退化為 \(\rm ReLU\) 函式,如果 \(\gamma < 1\),那麼也可以寫為:

\[{\rm LeakyLU(x)} = \max(x, \gamma x) \]

另一個變形是:

\[{\rm ELU}(x) = \begin{cases} x & x \ge 0 \\ \gamma(\exp(x) - 1) & x < 0 \end{cases} \]

還有一個則是:

\[{\rm Softplus}(x) = \log(1 + \exp(x)) \]

Swish 函式

這是一種自控門函式:

\[{\rm swish}(x) = x \sigma(\beta x) \]

網路結構

網路結構分三種:

  • 前饋網路
  • 記憶網路
  • 圖網路

這裡先講述前饋網路

這是一個前饋網路的示意圖,其中第一層為輸入層,最後一層為輸出層。

而中間的那些層稱為隱藏層。隱藏層可以有多個,而這裡只畫出了一個。

每一層有若干神經元,而兩層間的神經元兩兩相連。

現在我們定義一些符號:

  • \(L\) 表示總層數,注意這裡輸入層為第 \(0\) 層,不計入其中;輸出層為第 \(L\) 層。
  • \(M_l\) 表示第 \(l\) 層的神經元數量。
  • \(f_l(\cdot)\) 表示第 \(l\) 層的啟用函式。
  • \(W^{(l)} \in \mathbb{R}^{M_l \times M_{l - 1}}\) 表示第 \(l - 1\) 層到第 \(l\) 層的權重矩陣(若干權重向量組成)。
  • \(b^{(l)} \in \mathbb{R}^{M_l}\) 表示第 \(l\) 層的偏置。
  • \(z^{(l)} \in \mathbb{R}^{M_l}\) 表示淨輸入。
  • \(a^{(l)} \in \mathbb{R}^{M_l}\) 表示輸出。

對於一組資料 \((\vec x, y)\),前饋神經網路透過如下演算法進行傳播:

\[\begin{aligned} z^{(l)} &= W^{(l)} a^{(l - 1)} + b^{(l)} \\ a^{(l)} &= f_l(z^{(l)}) \end{aligned} \]

引數學習

引數學習可能略有點複雜,證明過程我懶得寫成 \(\LaTeX\),這裡就省略了。

我們利用反向傳播演算法進行學習,其步驟如下:

  • 選取一個資料,計算 \(a^{(l)}\)\(z^{(l)}\)
  • 反向傳播每一層的誤差 \(\delta^{(l)}\)
  • 計算每一層的偏導數,更新引數

顯然的是 \(\delta^{(L)} = a^{(L)} - y\)

經過一番神秘的推導,我們可以得到:

\[\delta^{(l)} = f_l'\left(z^{(l)}\right) \cdot \left( \left( W^{(l + 1)} \right)^T \delta^{(l + 1)} \right) \in \mathbb{R}^{M_l} \]

其中 \(\cdot\) 表示元素一一相乘。

而計算偏導數的公式也不難:

\[\begin{aligned} \frac {\partial}{\partial W^{(l)}} R(W) &= \delta^{(l)} \left( a^{(l - 1)} \right)^T \\ \frac {\partial}{\partial b^{(l)}} R(W) &= \delta^{(l)} \end{aligned} \]

也就是引數更新方式為:

\[\begin{aligned} W^{(l)} &\leftarrow W^{(l)} - \alpha \left( \delta^{(l)} \left( a^{(l - 1)} \right)^T + \lambda W^{(l)} \right) \\ b^{(l)} &\leftarrow b^{(l)} - \alpha \delta^{(l)} \end{aligned} \]

但是值得注意的是,一般我們都會將 \(W^{(l)}\) 的第一列作為 \(b^{(l)}\),也就是不分開,所以在程式碼實現上要好生注意!

這是吳恩達機器學習 ex4 的部分程式碼:

function [J grad] = nnCostFunction(nn_params, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, ...
                                   X, y, lambda)

% Theta1 25 x 401
% Theta2 10 x 26

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));
                 
temp1 = Theta1;
temp2 = Theta2;
temp1(:, 1) = 0;
temp2(:, 1) = 0;

m = size(X, 1);
         
J = 0;
Theta1_grad = zeros(size(Theta1));
Theta2_grad = zeros(size(Theta2));

% forward propagation
A2 = sigmoid([ones(m, 1) X] * Theta1'); % m x 25
A3 = sigmoid([ones(m, 1) A2] * Theta2'); % m x 10

% caculate cost
Y = zeros(m, num_labels);
for i = 1:m
	Y(i, y(i)) = 1;
end
J -= sum(sum( log(A3) .* Y + log(1 - A3) .* (1 - Y) ));
J += lambda / 2 * (sum(sum(temp1 .* temp1)) + sum(sum(temp2 .* temp2)));
J /= m;

% Back Propagation

D1 = zeros(size(Theta1));
D2 = zeros(size(Theta2));

for i = 1:m
	a1 = X(i, :); % 1 x 400
	a2 = A2(i, :); % 1 x 25
	a3 = A3(i, :); % 1 x 10
	y = Y(i, :); % 1 x 10
	d3 = (a3 - y)'; % 10 x 1
	d2 = (Theta2' * d3) .* [1 a2]' .* (1 - [1 a2])'; % 26 x 1
	d2 = d2(2:end) ; % 25 x 1
	
	D1 += d2 * [1 a1];
	D2 += d3 * [1 a2];
end

Theta1_grad = (D1 + lambda * temp1) / m;
Theta2_grad = (D2 + lambda * temp2) / m;

% Unroll gradients
grad = [Theta1_grad(:) ; Theta2_grad(:)];

end

相關文章