本文通過以下8個部分來詳細解釋神經網絡的一些基本概念:
- 模型表示(Model Representation)
- 模型的數學表示(Model Representation Mathematics)
- 激活函數(Activation Functions)
- 偏置節點(Bias Node)
- 損失函數(Cost Function)
- 前向傳播計算(Forward Propagation Calculation)
- 反向傳播算法(Backpropagation Algorithm)
- 代碼實現(Code Implementation)
1. 模型表示(Model Representation)
人工神經網絡是受構成動物大腦的生物神經網絡啓發而產生的計算系統。這類系統通過實例來“學習”並執行任務,通常不需要編寫任何特定於任務的規則。
神經網絡由三層構成:
- 輸入層:神經網絡的原始數據;
- 隱藏層:輸入層和輸出層之間的中間層,所有計算都在這裏完成;
- 輸出層:對給定的輸入產生結果。
上圖中有3個黃色的圓圈,表示輸入層,通常記作向量。有4個藍色和4個綠色的圓圈表示隱藏層,這些圓圈代表了“激活(activation)”節點,通常表示爲或。紅色的圓圈是輸出層或預測值(可以有多個輸出)。
每個節點與下一層的每個節點連接,每個連接(黑色箭頭)具有特定的權值(weight)。權值可以看作是該節點對下一層節點的影響。如果我們查看一個節點,它將看起來像這樣:
讓我們看看頂部的藍色節點(圖1),上一層(黃色)的所有節點都與它連接,所有這些連接都表示權重(影響)。 當將黃色層中的所有節點值乘以它們的權重並彙總後,將爲頂部藍色節點提供一些值。藍色節點具有預定義的“激活”函數(Activation Function,圖2中的unit step function),該函數根據彙總值定義該節點是否被“激活”或如何“激活”。值爲1的附加節點稱爲“偏置”節點。
2. 模型的數學表示(Model Representation Mathematics)
爲了理解數學方程,將使用一個更簡單的神經網絡模型。該模型有4個輸入節點(3+1個偏置節點),含有4個節點(3+1個偏置節點)的隱藏層和一個輸出節點。
我們要偏置節點分別標記爲和,則可用向量表示爲:
權重(箭頭)通常記爲或。在這種情況下,我將記爲。輸入層和隱藏層之間的權重將表示爲3x4的矩陣。而隱藏層與輸出層之間的權值爲1x4的矩陣。
如果網絡在第層有個單元,在第層有個單元,則的維度爲。
接下來,我們要計算隱藏層的“激活”節點。爲此,我們需要將輸入向量X和第一層的權重矩陣相乘(),然後應用激活函數g。我們得到的是:
然後通過將隱藏層向量與第二層的權重矩陣相乘,我們得到假設函數的輸出:
這個例子中只包含一個隱藏層和4個節點。如果我們嘗試對具有多個隱藏層且每個層中有多個節點的神經網絡時,我們將得到以下公式:
表示含有n個節點的L個層,且第L-1層含有m個節點。
3. 激活函數(Activation Functions)
在神經網絡中,激活函數根據加權來決定給定節點是否應該被“激活”。將這個加權總和的值定義爲z。在本節中,我將解釋“階躍函數(Step Function)”和“線性函數(Linear Function)”不起作用的原因,並討論最流行的激活函數之一“Sigmoid函數”。
3.1 階躍函數(Step Function)
第一種是使用“階躍函數”(離散的輸出值),我們定義閾值和:
這個函數的缺點在於節點只能輸出0或1,如果我們想要映射多個輸出類(節點),就會遇到問題,導致我們無法正確地分類/決定。
3.2 線性函數(Linear Function)
另一種是定義“線性函數”並得到一個輸出值範圍。
然而,在神經網絡中只使用線性函數會導致輸出層爲線性函數,因此我們無法映射任何非線性數據。證明如下:
然後通過函數複合我們得到:
這依然是一個線性函數。
3.3 Sigmoid函數
Sigmoid函數是目前應用最廣泛的激活函數之一,它的方程如下式所示:
它有多種屬性,這使得它很受歡迎:
- 它是非線性的函數;
- 值在(0,1)之間;
- 在x軸上的(-2,2)之間,函數非常陡峭,這導致函數傾向於分類1或0。
由於這個屬性,它允許節點取0到1之間的任何值。最後,在有多個輸出類別的情況下,這將導致每個輸出類別的“激活”概率不同。 然後,我們將選擇“激活”(概率)值最高的那個。
4. 偏置節點(Bias Node)
使用偏置節點通常對於創建成功的學習模型至關重要。 簡而言之,偏置量允許將激活函數向左或向右移動,並有助於更好地適應數據(更好的預測函數作爲輸出)。
下面繪製了3個Sigmoid函數,你可以在其中注意到變量x與某個值相乘/相加/相減會如何影響該函數。
- 乘以x -使函數更陡峭;
- 加/減x -將函數左移/右移。
5. 損失函數(Cost Function)
首先,定義損失函數的一般公式。 此函數表示誤差之和,預測值與實際(標籤)值之間的差。
由於這是一種分類問題,y只能取離散值{0,1},它只能取其中的一個值。例如,如果我們分類狗(類1),貓(類2)和鳥(類3)的圖像。如果輸入的圖像是狗。對於dog類,輸出類的值爲1,對於其他類,輸出類的值爲0。
這意味着我們希望我們的假設滿足:
這就是爲什麼我們把假設定義爲:
這裏g是Sigmoid函數,因爲此函數f的取值範圍是(0,1)。
我們的目標是優化損失函數,因此我們需要找到最小的。 但是,Sigmoid函數是“非凸”函數(“圖像15”),這意味着存在多個局部最小值。 因此,不能保證收斂到(找到)全局最小值。 我們需要的是梯度下降算法中的“凸”函數,以便能夠找到全局最小值(使最小)。 爲了做到這一點,我們使用函數。
這就是爲什麼我們在神經網絡中使用以下損失函數:
如果標籤值y=1,則假設爲否則爲
如果我們看一下函數圖,就會非常直觀。我們先來看y=1的情況,則就像下圖所示。我們只對x軸上的(0,1)區間感興趣,因爲假設只能取這個範圍內的值("圖13 ")。
從圖中可以看到,如果y = 1並且h(x)接近值1(x軸),則損失函數的值接近0(h(x)-y爲0),因爲這是正確的預測。 否則,如果h(x)接近0,則損失函數將變爲無窮大。
在另一種情況下,y=0,損失函數是-log(1-h(x)):
從圖中我們可以看出,如果h(x)趨近於0,那麼代價也會趨近於0,因爲這也是正確的預測。
由於y(標籤值)總是等於0或1,我們可以把損失函數寫在一個方程中。
如果我們把我們的損失函數完全寫出來,加上求和,我們會得到:
這是在神經網絡的輸出層中只有一個節點的情況下。 如果我們將其推廣到多個輸出節點(多類分類),我們將得到:
方程的右側部分表示損失函數的“正則化”,通過減小的大小來防止數據“過度擬合”。
6. 前向傳播計算(Forward Propagation Calculation)
前向傳播的過程實際上是根據給定的輸入獲得神經網絡的輸出值。該算法用於計算損失值(cost value)。 它所做的是與第2節“模型的數學表示”中描述的過程相同的數學過程, 最終得到假設值“圖7”。
在得到h(x)值(假設)之後,我們使用損失函數方程(“圖21”)來計算給定輸入集的損失。
這裏我們可以看到前向傳播是如何工作的,以及神經網絡是如何產生預測的。
7. 反向傳播算法(Backpropagation Algorithm)
我們要做的是使用(權重)的最優值集合來最小化損失函數。 反向傳播是我們用來計算的導數的一種方法。
然後,該導數值在梯度下降算法(“圖像23”)中用於計算神經網絡的值,從而使損失函數最小。
反向傳播算法有5步:
- 設,用於訓練;
- 執行前向傳播並計算其他層的;
- 使用並計算最後一層的增量值;
- 向後計算每一層的值(在“反向傳播背後的數學”一節中介紹);
- 計算每一層的導數值,它表示損失函數對於層的權重係數的導數。
反向傳播是關於確定改變權值如何影響神經網絡的總體損失。
它所做的是在神經網絡中反向傳播“誤差”。 在回溯的過程中,它查找每個權重對整體“誤差”的貢獻程度。 對整體“誤差”貢獻更大的權重將具有較大的導數值,這意味着它們在計算“梯度”下降時的變化將更大。
現在我們已經知道了反向傳播算法在做什麼,我們可以更深入地研究它背後的概念和數學原理。
7.1 爲什麼要求導數?
函數在每個變量(這裏爲權重)上的導數,表示的是該函數相對於該變量的敏感度,或者更改變量如何影響函數值。
讓我們來看一個簡單的神經網絡例子:
這裏有兩個輸入節點x和y,輸出函數是計算x和y的乘積,現在我們可以計算兩個節點的導數:
相對於x的導數表示,如果x增加某個值,那麼它將使函數增加,而相對於y的導數表示,如果y值增加某個值,則它將使函數增加。
根據我們的定義,反向傳播算法是針對每個權重參數計算損失函數的導數。通過這樣做,我們確定損失函數對這些權重參數中的每一個有多敏感。 它還有助於我們確定在計算梯度下降時應改變每個權重參數的程度。 因此,最後我們獲得了最適合我們數據的模型。
7.2 反向傳播背後的數學原理(Math behind Backpropagation)
我們將以下面的神經網絡模型爲出發點,推導出方程。
在這個模型中,我們有3個輸出節點(K)和2個隱藏層。如前所述,神經網絡的損失函數爲:
我們需要的是針對每個參數計算的導數。 由於將使用矢量化實現(矩陣乘法),因此我們將省略中間部分。 同樣,我們可以省略正則化(上面等式的右部分),最後將單獨進行計算。 由於它是加法,因此可以獨立計算導數。
我們首先定義將要使用的導數規則:
現在我們定義神經網絡模型的基本方程,其中是層的符號,且是最後一層。
在我們的例子中,的值是4,因爲我們的模型中有4層。我們先來計算一下對第3層和第4層的權重的導數。
步驟(6)- Sigmoid導數
爲了解釋步驟(6),我們需要計算sigmoid函數的偏導數。
對於最後一層,我們有:
所以:
步驟(11)—擺脫求和()
同樣在最後一步(11)中,我們需要將與的轉置相乘才能擺脫求和(訓練示例爲1…m)。
- -矩陣維度:[number_of_training_examples, output_layer_size],因此這也意味着我們將擺脫第二個求和(輸出節點數爲1…K);
- -矩陣維度:[hidden_layer_size, number_of_training_examples]。
現在,我們繼續第二和第三層之間參數的求導。 對於此推導,我們可以從步驟(9)(“圖片30”)開始。 由於在函數內部,我們在計算導數時需要使用“鏈式法則”(“圖像28”上的導數規則的步驟(6))。
現在我們得到了第二層和第三層之間的參數的導數。 我們要做的是計算輸入層和第二層之間的參數導數。這樣,將重複相同的過程(方程式),因此我們可以得出一般的和導數方程。 再次,我們從步驟(3)繼續(“圖34”)。
從上面的方程式中,我們可以導出參數的方程式和參數的導數。
最後,我們得到三個矩陣,其維數與θ權重矩陣相同,併爲每個θ參數計算出導數。
7.3 添加正則項
如前所述,需要進行正則化,以防止模型過度擬合數據。 我們已經爲損失函數定義了正則項,即“圖21”中所定義方程的右側部分。
爲了增加梯度的正則化(偏導數),我們需要計算上面正則項的偏導數。
這意味着將來自每一層的所有值之和與相對於的偏導數相加。
8. 代碼實現
我們現在可以在代碼中實現所有方程,我們會計算損失和導數(使用反向傳播),所以我們可以使用它們在梯度下降算法中來爲我們的模型優化參數。
function [J grad] = nnCostFunction(nn_params, ...
input_layer_size, ...
hidden_layer_size, ...
num_labels, ...
X, y, lambda)
%NNCOSTFUNCTION Implements the neural network cost function for a two layer
%neural network which performs classification
% [J grad] = NNCOSTFUNCTON(nn_params, hidden_layer_size, num_labels, ...
% X, y, lambda) computes the cost and gradient of the neural network. The
% parameters for the neural network are "unrolled" into the vector
% nn_params and need to be converted back into the weight matrices.
%
% The returned parameter grad should be a "unrolled" vector of the
% partial derivatives of the neural network.
%
% Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
% for our 2 layer neural network
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));
% 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));
% ====================== YOUR CODE HERE ======================
% Instructions: You should complete the code by working through the
% following parts.
%
% Part 1: Feedforward the neural network and return the cost in the
% variable J. After implementing Part 1, you can verify that your
% cost function computation is correct by verifying the cost
% computed in ex4.m
%
% Part 2: Implement the backpropagation algorithm to compute the gradients
% Theta1_grad and Theta2_grad. You should return the partial derivatives of
% the cost function with respect to Theta1 and Theta2 in Theta1_grad and
% Theta2_grad, respectively. After implementing Part 2, you can check
% that your implementation is correct by running checkNNGradients
%
% Note: The vector y passed into the function is a vector of labels
% containing values from 1..K. You need to map this vector into a
% binary vector of 1's and 0's to be used with the neural network
% cost function.
%
% Hint: We recommend implementing backpropagation using a for-loop
% over the training examples if you are implementing it for the
% first time.
%
% Part 3: Implement regularization with the cost function and gradients.
%
% Hint: You can implement this around the code for
% backpropagation. That is, you can compute the gradients for
% the regularization separately and then add them to Theta1_grad
% and Theta2_grad from Part 2.
%
% recode y to Y
I = eye(num_labels);
Y = zeros(m, num_labels);
for i=1:m
Y(i, :)= I(y(i), :);
end
% feedforward
a1 = [ones(m, 1) X];
z2 = a1*Theta1';
a2 = [ones(size(z2, 1), 1) sigmoid(z2)];
z3 = a2*Theta2';
a3 = sigmoid(z3);
h = a3;
% calculte penalty
p = sum(sum(Theta1(:, 2:end).^2, 2))+sum(sum(Theta2(:, 2:end).^2, 2));
% calculate J
J = sum(sum((-Y).*log(h) - (1-Y).*log(1-h), 2))/m + lambda*p/(2*m);
% calculate sigmas
sigma3 = a3.-Y;
sigma2 = (sigma3*Theta2).*sigmoidGradient([ones(size(z2, 1), 1) z2]);
sigma2 = sigma2(:, 2:end);
% accumulate gradients
delta1 = (a1'*sigma2);
delta2 = (a2'*sigma3);
% calculate regularized gradient
r1 = (lambda/m)*[zeros(size(Theta1, 1), 1) Theta1(:, 2:end)];
r2 = (lambda/m)*[zeros(size(Theta2, 1), 1) Theta2(:, 2:end)];
Theta1_grad = delta1'./m + r1;
Theta2_grad = delta2'./m + r2;
% -------------------------------------------------------------
% =========================================================================
% Unroll gradients
grad = [Theta1_grad(:) ; Theta2_grad(:)];
end