由於深度學習(deep learning)在近年來蓬勃發展,使得神經網路(Neural Network)熱門起來。它已經被用於解決各式各樣的問題。本文章將以二元分類神經網路(binary classification neural network)來詳細介紹 neural network。
Table of Contents
神經網路(Neural Networks)
神經網路(Neural networks)是由大量的神經元(neurons)連結而成。一個 neural network 由三個 layer 組成,分別為接收資料的輸入層(input layer)、輸出結果的輸出層(output layer),以及中間大量 neurons 鏈結組成數層的隱藏層(hidden layer),如下圖。
Input layer 和 output layer 都是各只有一層,而 hidden layer 則是可以數層。此外,當我們說下圖中是一個三層的 neural network,是指 hidden layer 中 layer 的數量再加上一層 output layer,而 input layer 不計算在內。

每一個 neuron 中會有輸入向量 、向量的權重(wights)
,一個純量的偏差(bias)
b,以及一個非線性函數(non-linear function)g。所以,一個 neuron 的功能是,求得 與
的內積再加上
b 後得到 z,然後再將 z 帶入 g 後得到一個輸出值 a。而這個輸出值 a 會成為下一層中 neurons 的其中一個輸入值 xi。這個 non-linear function g 稱為激勵函數(activation function),而 a 稱為 activation value。

所以,數個至大量的 neurons 組成一層,該層取得前一層的輸出值作為輸入值,經由該層內的 neurons 運算後的輸出值會作為下一層的輸入值。這樣子一層一層地連接起來成為包含大量 neurons 的 hidden layer。
激勵函數(Activation Functions)
如果一個 neuron 不包含 non-linear function 的話,也就是它只有做線性運算,即使將大量的 neurons 鍵結起來,那也只是一個多元線性回歸(multiple linear regression)。Linear regression models 是無法解決現實世界中複雜的問題,但是複雜的問題可以用 non-linear functions 來找出近似值。在 neuron 中的 non-linear function 稱為激勵函數(activation functions)。
以下我們將介紹四個常用的 activation functions。我們也會介紹它的導數求法,因為之後在 backpropagation 中,我們會需要求出它們的導數。
Sigmoid 函數與其導數
Sigmoid 函數將輸入值轉換成一個介於 0 到 1 之間的值,如下圖。它常常被用在 binary classification 的 neural network 裡的 output layer。我們可以將這個輸出值看作是一個機率。例如,我們想要一個 neural network 可以判斷一張圖片裡是不是貓。1 表示是貓,而 0 表示不是貓。當 output layer 輸出值大於 0.5 時,則預測為 1;當小於等於 0.5 時,則預測為 0。

Sigmoid 函數的實作如下。
def sigmoid(Z):
"""
Implements the sigmoid function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the sigmoid function
Returns
-------
A: (ndarray of same shape as Z) or (scalar) - output from the sigmoid function
"""
A = 1 / (1 + np.exp(-Z))
return ASigmoid 函數的導數如下。
Sigmoid 函數的導數實作如下。
def sigmoid_derivative(Z):
"""
Implements the derivative of the sigmoid function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the sigmoid function
Returns
--------
dZ: (ndarray of the same shape as Z) or (scalar) - derivative of the sigmoid function with respect to Z
"""
g = 1 / (1 + np.exp(-Z))
dZ = g * (1 - g)
return dZTanh 函數與其導數
Tanh 函數和 sigmoid 函數很像,但是 tanh 函數的輸出值是介於 -1 到 1 之間,如下圖。

Tanh 函數的實作如下。
def tanh(Z):
"""
Implements the tanh function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the tanh function
Returns
-------
A: (ndarray of same shape as Z) or (scalar) - output from the tanh function
"""
A = (np.exp(Z) - np.exp(-Z)) / (np.exp(Z) + np.exp(-Z))
return ATanh 函數的導數求解如下:
Tanh 函數的導數實作如下。
def tanh_derivative(Z):
"""
Implements the derivative of the tanh function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the tanh function
Returns
-------
dZ: (ndarray of the same shape as Z) or (scalar) - derivative of the tanh function with respect to Z
"""
g = (np.exp(Z) - np.exp(-Z)) / (np.exp(Z) + np.exp(-Z))
dZ = 1 - g ** 2
return dZReLU 函數與其導數
ReLU (rectified linear unit) 函數是廣泛地被使用在 neural networks。當 z 小於等於 0 時,輸出 0;當 z 大於 0 時,輸出 z。可以看出 ReLU 的執行效率非常地快。

ReLU 的函數實作如下。
def relu(Z):
"""
Implements the ReLU function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the ReLU function
Returns
-------
A: (ndarray of same shape as Z) or (scalar) - output from the ReLU function
"""
A = np.maximum(0, Z)
return AReLU 的導數如下。當 z 小於 0 時,導數為 0;當 z 大於 0 時,導數為 1;當 z 等於 0 時,導數為未定義。在實作時,慣例上,當 z 等於 0 時,導數設為 1。
ReLU 函數的導數實作如下。
def relu_derivative(Z):
"""
Implements the derivative of the ReLU function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the ReLU function
Returns
-------
dZ: (ndarray of the same shape as Z) or (scalar) - derivative of the ReLU function with respect to Z
"""
dZ = np.array(Z, copy=True)
dZ[Z < 0] = 0
return dZLeaky ReLU 函數與其導數
Leaky ReLU 函數是 ReLU 函數的一種變形。當 z 小於 0 時,輸出 ,其中
是一個介於 0 到 1 之間的值。

Leaky ReLU 函數的實作如下。
def leaky_relu(Z, negative_slope=0.01):
"""
Implements the leaky ReLU function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the leaky ReLU function
negative_slope: (float) - the slope for negative values
Returns
-------
A: (ndarray of same shape as Z) or (scalar) - output from the leaky ReLU function
"""
A = np.maximum(0, Z) + negative_slope * np.minimum(0, Z)
return ALeaky ReLU 的導數如下。當 z 小於 0 時,導數為 ;當
z 大於 0 時,導數為 1;當 z 等於 0 時,導數為未定義。在實作時,慣例上,當 z 等於 0 時,導數設為 1。
Leaky ReLU 函數的導數實作如下。
def leaky_relu_derivative(Z, negative_slope=0.01):
"""
Implements the derivative of the leaky ReLU function.
Parameters
----------
Z: (ndarray of any shape) or (scalar) - input to the leaky ReLU function
negative_slope: (float) - the slope for negative values
Returns
-------
dZ: (ndarray of the same shape as Z) or (scalar) - derivative of the leaky ReLU function with respect to Z
"""
dZ = np.array(Z, copy=True)
dZ[Z < 0] = negative_slope
return dZ二元分類(Binary Classification)
下圖是一個 binary classification 的 neural network。因為它是一個 binary classification,所以它的 output layer 裡的 activation function 是 sigmoid function 。我們可以看到圖中有相當多的變數。每一層有自己的 activation function,而每一個 neuron 有自己的參數
w 和 b。因此,將這些變數向量化,使用矩陣來表示的話,可以大大地簡化式子,如圖中的黃色部分。

所以,每一層的向量化後的公式以及陣列的維度如下。
| Layer | Shape of W | Shape of X | Shape of b | Shape of Z | Shape of A |
|---|---|---|---|---|---|
| 1 | |||||
| 2 | |||||
| L |
梯度下降(Gradient Descent)
Neural network 的 gradient descent 如下。由於每一層都有對應的參數 W 和 b,因此我們必須要計算 J 對每一層 W 和 b 的偏導數。
Gradient descent 的流程如下圖。
- 初始化所有參數
W和 b。 - 計算
,並儲存過程中的
W、b、A、和Z。因為在下一步計算偏導數時,我們會需要這些值。這部分是 forward propagation。 - 計算 J 對每一個
W和b的偏導數。這部分是 backward propagation。 - 更新所有參數
W和b。 - 反覆執行步驟 2 至 4,共執行
num_iterations次。

損失函數(Loss Function)
在 binary classification 的 neural network 裡,output layer 的 activation function 是 sigmoid function。因此,我們可以使用 sigmoid regression 的 loss function 作為 binary classification neural network 的 loss function。Sigmoid regression 是使用 cross-entropy loss 作為它的 loss function。
以下程式碼實作了 loss function。
def compute_cost(AL, Y):
"""
Computes the cross-entropy loss.
Parameters
----------
AL: (ndarray (1, number of examples)) - the output of the last layer
Y: (ndarray (1, number of examples)) - true labels
Returns
-------
cost: (float) - the cross-entropy cost
"""
m = Y.shape[1]
cost = -(1 / m) * np.sum(Y * np.log(AL) + (1 - Y) * np.log(1 - AL), axis=1, keepdims=True)
cost = np.squeeze(cost)
return cost參數初始化
我們有之前有列出所有參數 W 和 b 的維度。它們的維度與每一層 neurons 的數量相關,因此在初始化參數時,我們必須要先決定每一層 neurons 的數量。
以下程式碼中,我們使用亂數來初始化參數 W 和 b。
def initialize_parameters(layer_dims):
"""
Initializes parameters for a deep neural network.
Parameters
----------
layer_dims: (list) - the number of units of each layer in the network.
Returns
-------
(dict) with keys where 1 <= l <= len(layer_dims) - 1:
Wl: (ndarray (layer_dims[l], layer_dims[l-1])) - weight matrix for layer l
bl: (ndarray (layer_dims[l], 1)) - bias vector for layer l
"""
parameters = {}
for l in range(1, len(layer_dims)):
parameters[f'W{l}'] = np.random.randn(layer_dims[l], layer_dims[l - 1]) / np.sqrt(layer_dims[l - 1])
parameters[f'b{l}'] = np.zeros((layer_dims[l], 1))
return parameters前向傳播(Forward Propagation)
前向傳播(forward propagation)是 neural network 的 gradient descent 裡的前半段。每一層的輸出 A 會是下一層的輸入,所以 A 會被一層一層地傳遞,每一層會改變 A 的值。最後的 也就會是
。每一層都會將算出來的值儲存到 caches 裡,因為下半段的 backpropagation 將會需要它們。
我們執行完整個 gradient descent 後,會得到最終的參數 Wfinal 和 bfinal。假設我們想要用此模型來預測 Xnew,我們將輸入值 Xnew 以及參數 Wfinal 和 bfinal 代入 forward propagation,最後得到的 就是
Xnew 的預測值(prediction)。

以下程式碼中,linear_forward() 實作了流程中每一層的 linear forward 部分。linear_forwared() 不但回傳 Z,還會傳回 A、W、和 b 給呼叫者,由呼叫者將它們存入 caches 中。
def linear_forward(A_prev, W, b):
"""
Implements the linear part of a layer's forward propagation.
Parameters
----------
A_prev: (ndarray (size of previous layer, number of examples)) - activations from previous layer
W: (ndarray (size of current layer, size of previous layer)) - weight matrix
b: (ndarray (size of current layer, 1)) - bias vector
Returns
-------
Z: (ndarray (size of current layer, number of examples)) - the input to the activation function
cache: (tuple) - containing A_prev, W, b for backpropagation
"""
Z = W @ A_prev + b
cache = (A_prev, W, b)
return Z, cache以下程式碼中,我們實作了四種 activation functions。這些實作與文章開頭中的 activation function 實作是幾乎一樣的,差別在於這邊還會回傳 Z 給呼叫者。呼叫者會將它存入 caches 中。
def sigmoid(Z):
"""
Implements the sigmoid activation.
Parameters
----------
Z: (ndarray of any shape) - input to the activation function
Returns
-------
A: (ndarray of same shape as Z) - output of the activation function
cache: (ndarray) - returning Z for backpropagation
"""
A = 1 / (1 + np.exp(-Z))
cache = Z
return A, cache
def tanh(Z):
"""
Implements the tanh activation.
Parameters
----------
Z: (ndarray of any shape) - input to the activation function
Returns
-------
A: (ndarray of same shape as Z) - output of the activation function
cache: (ndarray) - returning Z for backpropagation
"""
A = (np.exp(Z) - np.exp(-Z)) / (np.exp(Z) + np.exp(-Z))
cache = Z
return A, cache
def relu(Z):
"""
Implements the ReLU activation.
Parameters
----------
Z: (ndarray of any shape) - input to the activation function
Returns
-------
A: (ndarray of same shape as Z) - output of the activation function
cache: (ndarray) - returning Z for backpropagation
"""
A = np.maximum(0, Z)
cache = Z
return A, cache
def leaky_relu(Z, negative_slope=0.01):
"""
Implements the Leaky ReLU activation.
Parameters
----------
Z: (ndarray of any shape) - input to the activation function
negative_slope: (float) - the slope for negative values
Returns
-------
A: (ndarray of same shape as Z) - output of the activation function
cache: (ndarray) - returning Z for backpropagation
"""
A = np.maximum(0, Z) + negative_slope * np.minimum(0, Z)
cache = Z
return A, cache以下程式碼中,linear_activation_forward() 實作了上圖中的一層,它會先呼叫 linear_forward() 來取得 Z,再將 Z 傳給一個 activation function 來取得 A。最終,將 A 和 cache 回傳給呼叫者。
def linear_activation_forward(A_prev, W, b, activation_function):
"""
Implements the forward propagation for the linear and activation layer.
Parameters
----------
A_prev: (ndarray (size of previous layer, number of examples)) - activations from previous layer
W: (ndarray (size of current layer, size of previous layer)) - weight matrix
b: (ndarray (size of current layer, 1)) - bias vector
activation_function: (str) - the activation function to be used
Returns
-------
A: (ndarray (size of current layer, number of examples)) - the output of the activation function
cache: (tuple) - containing linear_cache (A_prev, W, b) and activation_cache (Z) for backpropagation
"""
Z, linear_cache = linear_forward(A_prev, W, b)
if activation_function == 'sigmoid':
A, activation_cache = sigmoid(Z)
elif activation_function == 'tanh':
A, activation_cache = tanh(Z)
elif activation_function == 'relu':
A, activation_cache = relu(Z)
elif activation_function == 'leaky_relu':
A, activation_cache = leaky_relu(Z)
else:
raise ValueError(f'Activation function {activation_function} not supported.')
cache = (linear_cache, activation_cache)
return A, cache以下程式碼中,model_forward() 實作了整個 forward propagation。最後,它會回傳 和所有的 caches。
def model_forward(X, parameters, activation_functions):
"""
Implements forward propagation for the entire network.
Parameters
----------
X: (ndarray (input size, number of examples)) - input data
parameters: (dict) - output of initialize_parameters()
activation_functions: (list) - the activation function for each layer. The first element is unused.
Returns
-------
AL: (ndarray (output size, number of examples)) - the output of the last layer
caches: (list of tuples) - containing caches for each layer
"""
caches = []
A = X
L = len(activation_functions)
for l in range(1, L):
A_prev = A
A, cache = linear_activation_forward(A_prev, parameters[f'W{l}'], parameters[f'b{l}'], activation_functions[l])
caches.append(cache)
return A, caches反向傳播(Backpropagation or Backward Propagation)
在 gradient descent 中,我們必須要計算出 J(W, b) 對每一層的 W 和 b 的偏導數,來更新參數 W 和 b。當 neural network 有很多層的時候,計算偏導數會花費大量的時間。反向傳播(backpropagation)可以加快這些偏導數的計算速度。在計算每一層的偏導數時,都會需要下一層已經計算好的一些數值。如果,從第一層往後計算的話,有很多的數值會需要重複去算。因此,從最後一層往前算的話,每一層可以將算過的數值傳遞給前一層,前面那一層就可以直接取用,不需要再重新算過一次,如下圖。

其實 backpropagation 就是微分的連鎖率(chain rule)。
根據上圖,我們要先計算 ,然後再計算
。其中,最後一層的 activation function 是 sigmoid function
。
接著我們就可以利用上面的結果來計算 和
。
最後,所有的偏導數計算如下。
以下程式碼中,linear_backward() 實作了圖中 linear backward 的部分。
def linear_backward(dZ, cache):
"""
Implements the linear portion of backward propagation for a single layer.
Parameters
----------
dZ: (ndarray (size of current layer, number of examples)) - gradient of the cost with respect to the linear output
cache: (tuple) - containing W, A_prev, b from the forward propagation
Returns
-------
dA_prev: (ndarray (size of previous layer, number of examples)) - gradient of the cost with respect to the activation from the previous layer
dW: (ndarray (size of current layer, size of previous layer)) - gradient of the cost with respect to W
db: (ndarray (size of current layer, 1)) - gradient of the cost with respect to b
"""
A_prev, W, b = cache
dW = dZ @ A_prev.T
db = np.sum(dZ, axis=1, keepdims=True)
dA_prev = W.T @ dZ
return dA_prev, dW, db以下的程式碼實作了四個 activation functions 的微分 。它會乘上
,然後回傳
,也就是圖中的 activation backward 部分。
def sigmoid_backward(dA, cache):
"""
Implements the backward propagation for a single sigmoid unit.
Parameters
----------
dA: (ndarray of any shape) - post-activation gradient
cache: (ndarray) - Z from the forward propagation
Returns
--------
dZ: (ndarray of the same shape as A) - gradient of the cost with respect to Z
"""
Z = cache
g = 1 / (1 + np.exp(-Z))
g_prime = g * (1 - g)
dZ = dA * g_prime
return dZ
def tanh_backward(dA, cache):
"""
Implements the backward propagation for a single tanh unit.
Parameters
----------
dA: (ndarray of any shape) - post-activation gradient
cache: (ndarray) - Z from the forward propagation
Returns
-------
dZ: (ndarray of the same shape as A) - gradient of the cost with respect to Z
"""
Z = cache
g = (np.exp(Z) - np.exp(-Z)) / (np.exp(Z) + np.exp(-Z))
g_prime = 1 - g ** 2
dZ = dA * g_prime
return dZ
def relu_backward(dA, cache):
"""
Implements the backward propagation for a single ReLU unit.
Parameters
----------
dA: (ndarray of any shape) - post-activation gradient
cache: (ndarray) - Z from the forward propagation
Returns
-------
dZ: (ndarray of the same shape as A) - gradient of the cost with respect to Z
"""
Z = cache
dZ = np.array(dA, copy=True)
dZ[Z < 0] = 0
return dZ
def leaky_relu_backward(dA, cache, negative_slope=0.01):
"""
Implements the backward propagation for a single Leaky ReLU unit.
Parameters
----------
dA: (ndarray of any shape) - post-activation gradient
cache: (ndarray) - Z from the forward propagation
negative_slope: (float) - the slope for negative values
Returns
-------
dZ: (ndarray of the same shape as A) - gradient of the cost with respect to Z
"""
Z = cache
dZ = np.array(dA, copy=True)
dZ[Z < 0] = negative_slope
return dZ以下程式碼中的 linear_activation_backward() 實作了圖中一層中的部分。
def linear_activation_backward(dA, cache, activation_function):
"""
Implements the backward propagation for the linear and activation layer.
Parameters
----------
dA: (ndarray (size of current layer, number of examples)) - post-activation gradient for current layer
cache: (tuple) - containing linear_cache (A_prev, W, b) and activation_cache (Z) for backpropagation
activation_function: (str) - the activation function to be used
Returns
-------
dA_prev: (ndarray (size of previous layer, number of examples)) - gradient of the cost with respect to the activation from the previous layer
dW: (ndarray (size of current layer, size of previous layer)) - gradient of the cost with respect to W
db: (ndarray (size of current layer, 1)) - gradient of the cost with respect to b
"""
linear_cache, activation_cache = cache
if activation_function == 'sigmoid':
dZ = sigmoid_backward(dA, activation_cache)
elif activation_function == 'tanh':
dZ = tanh_backward(dA, activation_cache)
elif activation_function == 'relu':
dZ = relu_backward(dA, activation_cache)
elif activation_function == 'leaky_relu':
dZ = leaky_relu_backward(dA, activation_cache)
else:
raise ValueError(f'Activation function {activation_function} not supported.')
dA_prev, dW, db = linear_backward(dZ, linear_cache)
return dA_prev, dW, db最後,以下程式碼中的 model_backward() 實作了整個 backpropagation。
def model_backward(AL, Y, caches, activation_functions):
"""
Implements the backward propagation for the entire network.
Parameters
----------
AL: (ndarray (output size, number of examples)) - the output of the last layer
Y: (ndarray (output size, number of examples)) - true labels
caches: (list of tuples) - containing linear_cache (A_prev, W, b) and activation_cache (Z) for each layer
activation_functions: (list) - the activation function for each layer. The first element is unused.
Returns
-------
gradients: (dict) with keys where 0 <= l <= len(activation_functions) - 1:
dA{l-1}: (ndarray (size of previous layer, number of examples)) - gradient of the cost with respect to the activation for previous layer l - 1
dWl: (ndarray (size of current layer, size of previous layer)) - gradient of the cost with respect to W for layer l
dbl: (ndarray (size of current layer, 1)) - gradient of the cost with respect to b for layer l
"""
gradients = {}
L = len(activation_functions)
m = AL.shape[1]
dAL = -(1 / m) * (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
dA_prev = dAL
for l in reversed(range(1, L)):
current_cache = caches[l - 1]
dA_prev, dW, db = linear_activation_backward(dA_prev, current_cache, activation_functions[l])
gradients[f'dA{l - 1}'] = dA_prev
gradients[f'dW{l}'] = dW
gradients[f'db{l}'] = db
return gradients整合全部
執行過 backpropagation 後,會得到 J 對所有參數 W 和 b 的偏導數。然後,就可以呼叫以下的程式碼來更新所有 W 和 b。
def update_parameters(parameters, gradients, learning_rate):
"""
Updates parameters using the gradient descent update rule.
Parameters
----------
parameters: (dict) - containing the parameters
gradients: (dict) - containing the gradients
learning_rate: (float) - the learning rate
Returns
-------
params: (dict) - containing the updated parameters
"""
updated_parameters = parameters.copy()
L = len(updated_parameters) // 2
for l in range(L):
updated_parameters[f'W{l + 1}'] = parameters[f'W{l + 1}'] - learning_rate * gradients[f'dW{l + 1}']
updated_parameters[f'b{l + 1}'] = parameters[f'b{l + 1}'] - learning_rate * gradients[f'db{l + 1}']
return updated_parameters以下程式碼中的 nn_model() 實作了整個模型。它先執行 forward propagation,然後執行 backpropagation,最後更新參數。
def nn_model(X, Y, init_parameters, layer_activation_functions, learning_rate, num_iterations):
"""
Implements a neural network.
Parameters
----------
X: (ndarray (input size, number of examples)) - input data
Y: (ndarray (output size, number of examples)) - true labels
init_parameters: (dict) - the initial parameters for the network
layer_activation_functions: (list) - the activation function for each layer. The first element is unused.
learning_rate: (float) - the learning rate
num_iterations: (int) - the number of iterations
Returns
-------
parameters: (dict) - the learned parameters
costs: (list) - the costs at every 100th iteration
"""
costs = []
parameters = init_parameters.copy()
for i in range(num_iterations):
AL, caches = model_forward(X, parameters, layer_activation_functions)
cost = compute_cost(AL, Y)
gradients = model_backward(AL, Y, caches, layer_activation_functions)
parameters = update_parameters(parameters, gradients, learning_rate)
if i % 100 == 0 or i == num_iterations:
costs.append(cost)
return parameters, costs當訓練好參數後,我們可以用以下的 nn_model_predict() 來做預測。
def nn_model_predict(X, parameters, activation_functions):
"""
Predicts the output of the neural network.
Parameters
----------
X: (ndarray (input size, number of examples)) - input data
parameters: (dict) - the learned parameters
activation_functions: (list) - the activation function for each layer. The first element is unused.
Returns
-------
predictions: (ndarray (1, number of examples)) - the predicted labels
"""
probabilities, _ = model_forward(X, parameters, activation_functions)
predictions = probabilities.copy()
predictions[predictions > 0.5] = 1
predictions[predictions <= 0.5] = 0
return predictions範例
我們將藉由一個範例來展示如何使用我們的模型。首先,我們先將訓練資料 x_orig 和 y 載入。x_orig 是一個包含 100 張圖片的陣列。每一張圖片的大小是 64 x 64,而且有三個 channels。y 是一個包含 0 或 1 的陣列,1 表示圖片裡是貓,0 表示不是貓。
x_orig, y = load_data()
print(f'x_orig shape: {x_orig.shape}')
print(f'y shape: {y.shape}')
# Output
x_orig shape: ndarray(100, 64, 64, 3)
y shape: ndarray(1, 100)之前我們有列出 X 的維度是 (nh, m),所以每一張圖片是一個行向量。以下我們將 x_orig 的維度,並將數值 0 至 255 轉換成 0 至 1 的值。我們不需要轉換 y 的維度,因為 y 的維度已經和 一樣了。
x_flatten = x_orig.reshape(x_orig.shape[0], -1).T
x = x_flatten / 255.
print("x shape: " + str(x.shape))
# Output
x shape: ndarray(12288, 100)首先,我們要先決定模型的層數,以及每一層 neurons 個數。以下我們設定模型有一個 input layer、hidden layer 裡有三層、以及一個 output layer。我們還要決定每一層的 activation function,其中 layer_activation_functions[0] 對應 input layer,所以不會被使用到。
這些決定好後,我們就可以初始化所有的參數 W 和 b,然後呼叫 nn_model() 來訓練模型。最後,取得訓練好的參數。
layer_dims = [12288, 20, 7, 10, 1] init_parameters = initialize_parameters(layer_dims) layer_activation_functions = ['none', 'relu', 'relu', 'relu', 'sigmoid'] learning_rate = 0.0075 parameters, costs = nn_model(x, y, init_parameters, layer_activation_functions, learning_rate, 3000)
有了訓練好的參數後,我們就可以用來預測其他的圖片。
x_new_orig = load_new_data() x_new_flatten = x_new_orig.reshape(x_new_orig.shape[0], -1).T x_new = x_new_flatten / 255. y_new = nn_model_predict(x_new, parameters, layer_activation_functions)
多元分類(Multi-class Classification)
關於多元分類神經網路(multi-class classification neural network),請參考以下文章。
結語
Neural network 的 backpropagation 牽涉到偏導數的計算,所以較難理解。現在我們已經不需要自己實作 backpropagation,而是使用 machine learning 函式庫,如 PyTorch 或 TensorFlow。但是了解這些細節,可以使我們更加理解 neural network 的運作。
參考
- Andrew Ng, Deep Learning Specialization, Coursera.
- 西内啓,機器學習的數學基礎 : AI、深度學習打底必讀,旗標。









