해당 내용은 Coursera의 딥러닝 특화과정(Deep Learning Specialization)의 첫 번째 강의 Neural Networks and Deep Learning를 듣고 정리한 내용입니다. (Week 4)
4주차 첫 번째 실습은 딥러닝을 순서대로 구현해보는 것입니다.
여기서 ouput layer를 제외한 layer에서 activation function을 ReLU함수를 사용하고, output layer에서만 sigmoid 함수를 사용할 것이고, 이번 실습에서는 2-layer와 L-layer를 위한 함수를 각각 구현할 것입니다.
시작하기에 앞서 표기법을 정리하겠습니다.
- 위첨자 은 layer를 의미합니다. 은 L번째 layer의 activation을 의미합니다.
- 위첨자 은 sample을 의미합니다.
- 아래첨자 i는 번째 vector를 의미합니다. 라면 l번째 layer의 i번째 activation 입니다.
1. Packages
이번 실습에서 사용되는 패키지는 다음과 같습니다.
- numpy is the main package for scientific computing with Python.
- matplotlib is a library to plot graphs in Python.
- dnn_utils provides some necessary functions for this notebook.
- testCases provides some test cases to assess the correctness of your functions
- np.random.seed(1) is used to keep all the random function calls consistent. It will help us grade your work. Please don't change the seed.
dnn_utils에서 FP와 BP에서 사용되는 activation 함수를 제공하고 있으며, 아래와 같이 정의되어 있습니다.
import numpy as np def sigmoid(Z): """ Implements the sigmoid activation in numpy Arguments: Z -- numpy array of any shape Returns: A -- output of sigmoid(z), same shape as Z cache -- returns Z as well, useful during backpropagation """ A = 1/(1+np.exp(-Z)) cache = Z return A, cache def relu(Z): """ Implement the RELU function. Arguments: Z -- Output of the linear layer, of any shape Returns: A -- Post-activation parameter, of the same shape as Z cache -- a python dictionary containing "A" ; stored for computing the backward pass efficiently """ A = np.maximum(0,Z) assert(A.shape == Z.shape) cache = Z return A, cache def relu_backward(dA, cache): """ Implement the backward propagation for a single RELU unit. Arguments: dA -- post-activation gradient, of any shape cache -- 'Z' where we store for computing backward propagation efficiently Returns: dZ -- Gradient of the cost with respect to Z """ Z = cache dZ = np.array(dA, copy=True) # just converting dz to a correct object. # When z <= 0, you should set dz to 0 as well. dZ[Z <= 0] = 0 assert (dZ.shape == Z.shape) return dZ def sigmoid_backward(dA, cache): """ Implement the backward propagation for a single SIGMOID unit. Arguments: dA -- post-activation gradient, of any shape cache -- 'Z' where we store for computing backward propagation efficiently Returns: dZ -- Gradient of the cost with respect to Z """ Z = cache s = 1/(1+np.exp(-Z)) dZ = dA * s * (1-s) assert (dZ.shape == Z.shape) return dZ
그리고 적용되는 라이브러리 입니다.
import numpy as np import h5py import matplotlib.pyplot as plt from testCases_v4a import * from dnn_utils_v2 import sigmoid, sigmoid_backward, relu, relu_backward %matplotlib inline plt.rcParams['figure.figsize'] = (5.0, 4.0) # set default size of plots plt.rcParams['image.interpolation'] = 'nearest' plt.rcParams['image.cmap'] = 'gray' %load_ext autoreload %autoreload 2 np.random.seed(1)
2. Outline of the Assignment
시작하기에 앞서 오늘 실습의 개요를 살펴보겠습니다.
NN을 구현하기 위해서, 우리는 이번에 Helper function들을 정의할 것이고, 이 정의된 함수들은 다음 과제에서 2-layer NN과 L-layer NN을 구현하는데 사용할 것입니다.
구현되는 함수는 다음과 같습니다.
- L-layer의 파라미터 초기화 함수
- Forward Propagation 수행 함수
- Cost 계산 함수
- Backward Propagation 수행 함수
- 파라미터 업데이트 함수

위와 같은 과정을 거치게 됩니다. L-1 layer(hidden layers)에서는 ReLU함수를 activation function으로 사용했고, 마지막 ouput layer에서는 Sigmoid 함수를 사용한 것을 볼 수 있습니다.
3. Initialization
3.1 2-layer Neural Network
2-layer NN의 파라미터 초기화를 위한 함수를 구현해봅시다.
model의 구조는 Linear -> ReLU -> Linear -> Sigmoid 로 구성되며, 우리는 numpy의 random.randn(shape)*0.01로 랜덤하게 초기화할 수 있습니다. 이전의 실습과 마찬가지로 파라미터 b는 0으로 초기화해도 무관합니다.
# GRADED FUNCTION: initialize_parameters def initialize_parameters(n_x, n_h, n_y): """ Argument: n_x -- size of the input layer n_h -- size of the hidden layer n_y -- size of the output layer Returns: parameters -- python dictionary containing your parameters: W1 -- weight matrix of shape (n_h, n_x) b1 -- bias vector of shape (n_h, 1) W2 -- weight matrix of shape (n_y, n_h) b2 -- bias vector of shape (n_y, 1) """ np.random.seed(1) ### START CODE HERE ### (≈ 4 lines of code) W1 = np.random.randn(n_h, n_x) * 0.01 b1 = np.zeros((n_h, 1)) W2 = np.random.randn(n_y, n_h) * 0.01 b2 = np.zeros((n_y, 1)) ### END CODE HERE ### assert(W1.shape == (n_h, n_x)) assert(b1.shape == (n_h, 1)) assert(W2.shape == (n_y, n_h)) assert(b2.shape == (n_y, 1)) parameters = {"W1": W1, "b1": b1, "W2": W2, "b2": b2} return parameters
3.2 L-layer Neural Network
다음으로 L-layer를 가진 NN을 구현해봅시다.
L-layer의 파라미터를 초기화하는 것은 조금 더 복잡한데, 각 layer에서의 파라미터 W와 b의 차원을 맞추어 주어야하기 때문입니다. 이 l-layer의 unit 갯수라는 것을 기억하면서 이전 실습이었던 고양이 판별기를 예시로 살펴보면 다음과 같이 각 layer의 파라미터 차원을 구할 수 있습니다.(입력 X의 차원이 (12288, 209)(with m = 209 examples))

# GRADED FUNCTION: initialize_parameters_deep def initialize_parameters_deep(layer_dims): """ Arguments: layer_dims -- python array (list) containing the dimensions of each layer in our network Returns: parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL": Wl -- weight matrix of shape (layer_dims[l], layer_dims[l-1]) bl -- bias vector of shape (layer_dims[l], 1) """ np.random.seed(3) parameters = {} L = len(layer_dims) # number of layers in the network for l in range(1, L): ### START CODE HERE ### (≈ 2 lines of code) parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1])*0.01 parameters['b' + str(l)] = np.zeros((layer_dims[l], 1)) ### END CODE HERE ### assert(parameters['W' + str(l)].shape == (layer_dims[l], layer_dims[l-1])) assert(parameters['b' + str(l)].shape == (layer_dims[l], 1)) return parameters
코드는 위와 같이 구현할 수 있습니다. 매개변수로 받는 layer_dim은 각 layer의 크기를 담고 있는 list입니다.
만약, layer_dim이 [5,4,3]으로 주어진다면,
- W1 = (4, 5)
- b1 = (4, 1)
- W2 = (3, 4)
- b2 = (3, 1)
로 파라미터의 차원이 결정됩니다. layer_dim의 맨 처음 요소는 input layer의 크기 입니다.
4. Forward propagation module
4.1 Linear Forward
파라미터를 초기화했고, 다음으로 FP를 진행해야 합니다.
순서대로 다음의 3개의 값을 계산하는 함수를 만들어봅시다.
1. Linear
2. Linear -> activation (activation은 ReLU 또는 Sigmoid임)
3. [Linear -> ReLU] x (L-1) -> Linear -> Sigmoid (FP 전체과정)
Linear Forward는 다음과 같이 구할 수 있습니다.
여기서 입니다.
# GRADED FUNCTION: linear_forward def linear_forward(A, W, b): """ Implement the linear part of a layer's forward propagation. Arguments: A -- activations from previous layer (or input data): (size of previous layer, number of examples) W -- weights matrix: numpy array of shape (size of current layer, size of previous layer) b -- bias vector, numpy array of shape (size of the current layer, 1) Returns: Z -- the input of the activation function, also called pre-activation parameter cache -- a python tuple containing "A", "W" and "b" ; stored for computing the backward pass efficiently """ ### START CODE HERE ### (≈ 1 line of code) Z = np.dot(W, A) + b ### END CODE HERE ### assert(Z.shape == (W.shape[0], A.shape[1])) cache = (A, W, b) return Z, cache
4.2 Linear-Activation Forward
activation 계산에 두 가지 종류의 함수 Sigmoid와 ReLU가 사용됩니다.
- Sigmoid: 로 계산되고, 이 함수는 Activation 값과 Z를 포함하는 cache를 반환합니다(cache는 BP에서 재사용됩니다).
A, activation_cache = sigmoid(Z)
- ReLU : 로 계산됩니다. 마찬가지로 Activation값과 cache를 반환합니다.
A, activation_cache = relu(Z)
중간의 hidden layer는 ReLU로 계산해야되고, 마지막 ouput layer는 sigmoid로 계산해야되기 때문에, 어떤 함수가 사용되는지 알려주는 역할을 하는 activation을 매개변수로 입력받습니다.
# GRADED FUNCTION: linear_activation_forward def linear_activation_forward(A_prev, W, b, activation): """ Implement the forward propagation for the LINEAR->ACTIVATION layer Arguments: A_prev -- activations from previous layer (or input data): (size of previous layer, number of examples) W -- weights matrix: numpy array of shape (size of current layer, size of previous layer) b -- bias vector, numpy array of shape (size of the current layer, 1) activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu" Returns: A -- the output of the activation function, also called the post-activation value cache -- a python tuple containing "linear_cache" and "activation_cache"; stored for computing the backward pass efficiently """ if activation == "sigmoid": # Inputs: "A_prev, W, b". Outputs: "A, activation_cache". ### START CODE HERE ### (≈ 2 lines of code) Z, linear_cache = linear_forward(A_prev, W, b) A, activation_cache = sigmoid(Z) ### END CODE HERE ### elif activation == "relu": # Inputs: "A_prev, W, b". Outputs: "A, activation_cache". ### START CODE HERE ### (≈ 2 lines of code) Z, linear_cache = linear_forward(A_prev, W, b) A, activation_cache = relu(Z) ### END CODE HERE ### assert (A.shape == (W.shape[0], A_prev.shape[1])) cache = (linear_cache, activation_cache) return A, cache
4.3 L-layer Model
모든 layer에서의 FP를 진행하기 위한 함수를 구현합니다. 예측값인 AL(), 그리고 각 layer의 cache를 저장한 caches를 반환합니다.
[Linear -> ReLU] x (L-1) -> Linear -> Sigmoid
AL 변수는 다음을 의미합니다.
# GRADED FUNCTION: L_model_forward def L_model_forward(X, parameters): """ Implement forward propagation for the [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID computation Arguments: X -- data, numpy array of shape (input size, number of examples) parameters -- output of initialize_parameters_deep() Returns: AL -- last post-activation value caches -- list of caches containing: every cache of linear_activation_forward() (there are L-1 of them, indexed from 0 to L-1) """ caches = [] A = X L = len(parameters) // 2 # number of layers in the neural network # Implement [LINEAR -> RELU]*(L-1). Add "cache" to the "caches" list. for l in range(1, L): A_prev = A ### START CODE HERE ### (≈ 2 lines of code) A, cache = linear_activation_forward(A_prev, parameters['W'+str(l)], parameters['b'+str(l)], activation = "relu") caches.append(cache) ### END CODE HERE ### # Implement LINEAR -> SIGMOID. Add "cache" to the "caches" list. ### START CODE HERE ### (≈ 2 lines of code) AL, cache = linear_activation_forward(A, parameters['W'+str(L)], parameters['b'+str(L)], activation = "sigmoid") caches.append(cache) ### END CODE HERE ### assert(AL.shape == (1,X.shape[1])) return AL, caches
5. Cost Function
Cost J를 계산하는 함수입니다. 아래의 식으로 계산됩니다.
# GRADED FUNCTION: compute_cost def compute_cost(AL, Y): """ Implement the cost function defined by equation (7). Arguments: AL -- probability vector corresponding to your label predictions, shape (1, number of examples) Y -- true "label" vector (for example: containing 0 if non-cat, 1 if cat), shape (1, number of examples) Returns: cost -- cross-entropy cost """ m = Y.shape[1] # Compute loss from aL and y. ### START CODE HERE ### (≈ 1 lines of code) cost = (-1 / m) * np.sum(Y * np.log(AL) + (1 - Y) * np.log(1 - AL), axis = 1) ### END CODE HERE ### cost = np.squeeze(cost) # To make sure your cost's shape is what we expect (e.g. this turns [[17]] into 17). assert(cost.shape == ()) return cost
6. Backward propagation module
FP에서 구한 결과를 사용해서 BP를 진행해서 Cost Function의 gradient를 구합니다.

FP와 마찬가지로 3단계의 BP 함수를 구현해봅시다.
1. Linear backward
2. Linear->Activation backward(마찬가지로 activation은 sigmoid와 ReLU 두 가지입니다.)
3. [Linear -> ReLU] x (L-1) -> Linear -> Sigmoid backward(전체 BP 모델)
6-1 Linear backward
하나의 layer 에서, linear part는 입니다.
이 함수에서는 이미 계산된 미분항 를 가지고, 를 구합니다.

output은 다음과 같이 계산됩니다.
# GRADED FUNCTION: linear_backward def linear_backward(dZ, cache): """ Implement the linear portion of backward propagation for a single layer (layer l) Arguments: dZ -- Gradient of the cost with respect to the linear output (of current layer l) cache -- tuple of values (A_prev, W, b) coming from the forward propagation in the current layer Returns: dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev dW -- Gradient of the cost with respect to W (current layer l), same shape as W db -- Gradient of the cost with respect to b (current layer l), same shape as b """ A_prev, W, b = cache m = A_prev.shape[1] ### START CODE HERE ### (≈ 3 lines of code) dW = (1/m)*np.dot(dZ, A_prev.T) db = (1/m)*np.sum(dZ, axis = 1, keepdims = True) dA_prev = np.dot(W.T, dZ) ### END CODE HERE ### assert (dA_prev.shape == A_prev.shape) assert (dW.shape == W.shape) assert (db.shape == b.shape) return dA_prev, dW, db
6.2 Linear-Activation backward
6.1 함수를 수행하기 전 dZ를 구하고, 6.1 함수를 수행하는 linear-activation backward 함수를 구현합니다.
여기서 제공되는 sigmoid_backward와 relu_backward 함수를 사용해서 dZ를 구하게 됩니다.
는 activation 함수, 는 activation의 도함수입니다.
# GRADED FUNCTION: linear_activation_backward def linear_activation_backward(dA, cache, activation): """ Implement the backward propagation for the LINEAR->ACTIVATION layer. Arguments: dA -- post-activation gradient for current layer l cache -- tuple of values (linear_cache, activation_cache) we store for computing backward propagation efficiently activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu" Returns: dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev dW -- Gradient of the cost with respect to W (current layer l), same shape as W db -- Gradient of the cost with respect to b (current layer l), same shape as b """ linear_cache, activation_cache = cache if activation == "relu": ### START CODE HERE ### (≈ 2 lines of code) dZ = relu_backward(dA, activation_cache) dA_prev, dW, db = linear_backward(dZ, linear_cache) ### END CODE HERE ### elif activation == "sigmoid": ### START CODE HERE ### (≈ 2 lines of code) dZ = sigmoid_backward(dA, activation_cache) dA_prev, dW, db = linear_backward(dZ, linear_cache) ### END CODE HERE ### return dA_prev, dW, db
6.3 L-model Backward
이제, 전체 network에서의 backward 함수를 구현합니다. L_model_forward 함수를 통해서, 매 iteration마다 (X, W, b, z)를 저장한 cache를 기억하면서, BP module에서 cache를 사용해 gradient를 계산합니다.

Initializing backpropagation: 신경망에서 BP를 진행하기 위해서는 우선 BP 입력값인 dAL을 계산해야 합니다.
dAL = 은 다음과 같이 계산됩니다.
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
즉, 다음을 계산한 것입니다.
계산된 미분값은 grads에 저장되며, 인 경우에 는 grads["dW3"], grads["db3"]에 저장됩니다.
# GRADED FUNCTION: L_model_backward def L_model_backward(AL, Y, caches): """ Implement the backward propagation for the [LINEAR->RELU] * (L-1) -> LINEAR -> SIGMOID group Arguments: AL -- probability vector, output of the forward propagation (L_model_forward()) Y -- true "label" vector (containing 0 if non-cat, 1 if cat) caches -- list of caches containing: every cache of linear_activation_forward() with "relu" (it's caches[l], for l in range(L-1) i.e l = 0...L-2) the cache of linear_activation_forward() with "sigmoid" (it's caches[L-1]) Returns: grads -- A dictionary with the gradients grads["dA" + str(l)] = ... grads["dW" + str(l)] = ... grads["db" + str(l)] = ... """ grads = {} L = len(caches) # the number of layers m = AL.shape[1] Y = Y.reshape(AL.shape) # after this line, Y is the same shape as AL # Initializing the backpropagation ### START CODE HERE ### (1 line of code) dAL = -(np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) ### END CODE HERE ### # Lth layer (SIGMOID -> LINEAR) gradients. Inputs: "dAL, current_cache". Outputs: "grads["dAL-1"], grads["dWL"], grads["dbL"] ### START CODE HERE ### (approx. 2 lines) current_cache = caches[L-1] grads["dA" + str(L-1)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, activation = "sigmoid") ### END CODE HERE ### # Loop from l=L-2 to l=0 for l in reversed(range(L-1)): # lth layer: (RELU -> LINEAR) gradients. # Inputs: "grads["dA" + str(l + 1)], current_cache". Outputs: "grads["dA" + str(l)] , grads["dW" + str(l + 1)] , grads["db" + str(l + 1)] ### START CODE HERE ### (approx. 5 lines) current_cache = caches[l] dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA"+str(l+1)], current_cache, activation = "relu") grads["dA" + str(l)] = dA_prev_temp grads["dW" + str(l + 1)] = dW_temp grads["db" + str(l + 1)] = db_temp ### END CODE HERE ### return grads
6.4 Update Parameters
BP를 진행해 gradient를 구하고 gradient descent를 사용해 파라미터를 업데이트합니다.
위와 같이 갱신되며, 는 learning rate입니다. 업데이트된 파라미터값은 parameters에 저정됩니다.
# GRADED FUNCTION: update_parameters def update_parameters(parameters, grads, learning_rate): """ Update parameters using gradient descent Arguments: parameters -- python dictionary containing your parameters grads -- python dictionary containing your gradients, output of L_model_backward Returns: parameters -- python dictionary containing your updated parameters parameters["W" + str(l)] = ... parameters["b" + str(l)] = ... """ L = len(parameters) // 2 # number of layers in the neural network # Update rule for each parameter. Use a for loop. ### START CODE HERE ### (≈ 3 lines of code) for l in range(L): parameters["W" + str(l+1)] = parameters["W" + str(l+1)] - learning_rate * grads["dW" + str(l + 1)] parameters["b" + str(l+1)] = parameters["b" + str(l+1)] - learning_rate * grads["db" + str(l + 1)] ### END CODE HERE ### return parameters
NN의 구성을 단계별로 구현해보았습니다. 다음 실습에서 이 함수들을 사용해서 2주차 실습에 Logistic Regression으로 구현한 cat vs non-cat 분류기를 NN으로 구현해보도록 하겠습니다.
'Coursera 강의 > Deep Learning' 카테고리의 다른 글
[실습] Initialization 초기화 (0) | 2020.09.26 |
---|---|
[실습] Deep Neural Network for Image Classification(cat vs non-cat) (6) | 2020.09.26 |
[실습] Planar data classification with a hidden layer (9) | 2020.09.25 |
[실습] Logistic Regression with a Neural Network(can / non-cat classifier) (0) | 2020.09.24 |
Practical aspects of Deep Learning 2 (3) | 2020.09.23 |
댓글