神經網路應該由若干神經元組成。
前面的每一個神經元都會給到一個引數,將傳遞的所有引數看作一個向量 \(\vec x\),那麼此神經元的淨輸入為:
其中 \(\omega\) 稱為權重向量。
這裡認為 \(x\) 是行向量,而 \(\omega\) 是列向量。
神經元還有一個啟用函式 \(f(\cdot)\):
稱為函式的活性值
一般來說,我們使用 Logistic 函式,即 \(\sigma(x) = \frac 1 {1 + exp(-x)}\) 作為啟用函式。
啟用函式
啟用函式有很多很多種,一般來說要滿足以下幾點:
- 連續且可導的非線性函式。
- 函式本身和其導數要儘可能簡單。
- 值域要在一個合適的區間內
這裡列舉幾種常見的函式。
Sigmoid 型
指一類兩端飽和的 S 型曲線。
飽和:
\(\lim_\limits{x \to -\infty} f'(x) = 0\) 稱為左飽和,\(\lim_\limits{x \to \infty} f'(x) = 0\) 稱為右飽和。
同時滿足則稱為兩端飽和。
常見的 Sigmoid 型函式有 Logistic
和 Tanh
。
Logistic
函式
其導數:
Tanh
函式
其可以看作縮放平移後的 \(\sigma\),因為:
自然其導數:
實際上我們可以透過近似的方法去擬合這個函式,畢竟 \(e^x\) 也不是那麼好算的。
Hard-Logistic
和Hard-Tanh
函式
或者利用 \(\min, \max\) 簡化:
類似的:
ReLU
也就是 Rectified Linear Unit,線性修正單元,定義為:
也就是 \({\rm ReLU}(x) = \max(x, 0)\)
當然,因為可能出現 死亡 ReLU 問題
,所以一般有如下變形:
如果 \(\gamma = 0\) 則退化為 \(\rm ReLU\) 函式,如果 \(\gamma < 1\),那麼也可以寫為:
另一個變形是:
還有一個則是:
Swish 函式
這是一種自控門函式:
網路結構
網路結構分三種:
- 前饋網路
- 記憶網路
- 圖網路
這裡先講述前饋網路。
這是一個前饋網路的示意圖,其中第一層為輸入層,最後一層為輸出層。
而中間的那些層稱為隱藏層。隱藏層可以有多個,而這裡只畫出了一個。
每一層有若干神經元,而兩層間的神經元兩兩相連。
現在我們定義一些符號:
- \(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)\),前饋神經網路透過如下演算法進行傳播:
引數學習
引數學習可能略有點複雜,證明過程我懶得寫成 \(\LaTeX\),這裡就省略了。
我們利用反向傳播演算法進行學習,其步驟如下:
- 選取一個資料,計算 \(a^{(l)}\) 和 \(z^{(l)}\)。
- 反向傳播每一層的誤差 \(\delta^{(l)}\)
- 計算每一層的偏導數,更新引數
顯然的是 \(\delta^{(L)} = a^{(L)} - y\)
經過一番神秘的推導,我們可以得到:
其中 \(\cdot\) 表示元素一一相乘。
而計算偏導數的公式也不難:
也就是引數更新方式為:
但是值得注意的是,一般我們都會將 \(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