본문 바로가기
Deep Learning

2.1 Practical Aspects of Deep Learning

by yu901 2023. 2. 28.

모델을 만들고 테스트해 보면 분산(Variance)편향(Bias)에 따라 아래 네가지 결과 중 하나가 나올 것이다.

 

Bias가 높은 문제는, Bigger Network(hidden layer 또는 node가 많은)를 사용하거나 더 길게 Train하여 해결할 수 있다.

Variance가 높은 문제는, 더 많은 Train Data를 사용하거나 모델을 정규화(Regularization)하여 해결할 수 있다.

적절한 Neural Networks Architecture를 사용하여 위 두 문제를 해결할 수도 있다.

 

 

 


 

Neural Network를 Regularization하는 방법을 알아보자.

 

 

1. Logistic regression

 

Cost function을 L1 regularization, L2 regularization 하여, 과적합을 방지할 수 있다.

대표적인 L1, L2 regularization은 \(||w||_{1}\) ( = \(\sum\limits_{j = 1}^{n_x}|w_j|\)), \(||w||_{2}^{2}\) (= \(w^{T}w\)) 각 항을 더하여 결과 값에 제한을 둔다.

 

L2 regularization: \(J(W^{[l]}, b^{[l]}) = \frac{1}{m} \sum\limits_{i = 1}^{m}L(\hat{y}^{(i)}, y^{(i)}) + \frac{\lambda}{2m}\sum\limits_{l = 1}^{L}||W^{[l]}||_{F}^{2}\)

 

\(\lambda\)는  regularization parameter로 \(\lambda\) 값을 크게하면 Cost function을 작게 하기 위해 \(W\)의 크기가 작아져야 한다.

\(||W^{[l]}||_{F}^{2}\)는 Frobenius norm으로 matrix의 Euclidian norm이다. (\(||W^{[l]}||_{F}^{2} = \sum\limits_{i = 1}^{n^{[l]}}\sum\limits_{j=1}^{n^{[l-1]}}(W_{i, j}^{[l]})^2\) )

Frobenius norm은 backward propagation에서 \(\frac{\lambda}{m}W^{[L]}\) 항이 된다.

 

\(dW^{[l]} = \frac{1}{m}dZ^{[l]}A^{[l-1]T} + \frac{\lambda}{m}W^{[L]}\)

 

# compute_cost_with_regularization
cross_entropy_cost = compute_cost(A3, Y)
L2_regularization_cost = 1/m*lambd/2*(np.sum(np.square(W1))+np.sum(np.square(W2))+np.sum(np.square(W3)))     
cost = cross_entropy_cost + L2_regularization_cost

 

# backward_propagation_with_regularization
dW3 = 1./m * np.dot(dZ3, A2.T) + lambd / m * W3

 

더보기

Logistic regression은 어떻게 과적합을 줄일까?
regularization parameter(\(\lambda\))를 크게 하여 \(||W^{[l]}||\)가 작으면, \(Z^{[l]}=W^{[l]}A^{[l-1]}+b^{[l]}\)으로 \(Z^{[l]}\)의 절대 값이 작아질 것이다. \(Z^{[l]}\)의 절대 값이 작아지면 activation function의 결과는 linear와 비슷해지면서 단순선형네트워크가 된다. 

tanh activation function

 

 

 

2. Dropout

 

layer에서 노드를 사용할 확률을 조정하는 것이다.

keep_prob=0.8은 노드가 유지될 확률이 80%, 제거될 확률이 20% 임을 의미한다.

\(a^{[3]}\)에서 80%의 노드만 사용하면, \(z^{[4]} = W^{[4]}a^{[3]}+b^{[4]}\)의 결과값이 감소할 것이다. 따라서 (a^{[3]}\)를 0.8으로 나눠서 값의 크기를 맞출 수 있다.

 

 

# forward_propagation_with_dropout
Z1 = np.dot(W1, X) + b1
A1 = relu(Z1)
D1 = np.random.rand(A1.shape[0], A1.shape[1])    # Step 1: initialize matrix D1 = np.random.rand(..., ...)
D1 = (D1 < keep_prob).astype(int)                # Step 2: convert entries of D1 to 0 or 1 (using keep_prob as the threshold)
A1 = np.multiply(A1, D1)                         # Step 3: shut down some neurons of A1
A1 = A1 / keep_prob                              # Step 4: scale the value of neurons that haven't been shut down

 

# backward_propagation_with_dropout
dZ3 = A3 - Y
dW3 = 1./m * np.dot(dZ3, A2.T)
db3 = 1./m * np.sum(dZ3, axis=1, keepdims=True)
dA2 = np.dot(W3.T, dZ3)
dA2 = dA2 * D2                     # Step 1: Apply mask D2 to shut down the same neurons as during the forward propagation
dA2 = dA2 / keep_prob              # Step 2: Scale the value of neurons that haven't been shut down

 

 

 

3. Data augmentation

 

이미지를 좌우 반전하거나, 회전하거나, 확대하거나, 찌그러뜨려서 train data를 늘릴 수 있다.

 

 

 

 

4. Early stopping

 

학습이 진행되는 과정을 모니터 하면서 원래 설정한 종료 시점보다 일찍 종료함으로써 overfitting을 방지하는 기법이다.

\(W\)는 학습을 하면서 증가하게 되는데, 중간정도 되는 지점에서 중지한다.

 

 

 


 

최적화 문제(Optimization problem)를 해결하는 방법을 알아보자.

(최적화 문제는 최적해 또는 근접한 값을 찾는 문제를 일컫는다.)

 

 

1. Normalizing inputs

 

입력데이터를 정규화(normalization)하여 학습 속도를 높일 수 있다.

정규화란 데이터가 정규분포를 따르도록 평균을 0, 분산을 1로 변경하는 것이다.

(\(x = \frac{x-\mu}{\sigma},  \mu=\frac{1}{m}\sum\limits_{i = 1}^{m}x^{(i)},  \sigma^2=\frac{1}{m}\sum\limits_{i = 1}^{m}(x^{(i)})^2\))

 

 

 

 

 

 

2. Weight initialization

 

Deep Nueral Network은 역전파 과정에서 입력층으로 갈수록 Gradient Vanishing / Exploding 위험이 있다. 가중치(\(W\)) 초기화를 잘하는 것 만으로 Gradient Vanishing / Exploding을 방지할 수 있다. 

Weight initialization의 원리는 이전 layer의 노드 개수(\(n^{[l-1]}\))가 많으면 다음 layer의 가중치(\(W^{[l]}\))를 작게 만들어서 적당한 결과값(\(Z^{[l]}\))이 나오도록 하는 것이다. Weight initialization 방법 중 Xavier initialization은 가중치의 표준편차를 \(\sqrt{\frac{1}{n^{[l-1]}}}\)가 되도록 하고,  He initialization 가중치의 표준편차를 \(\sqrt{\frac{2}{n^{[l-1]}}}\)가 되도록 한다.

 

 

# initialize_parameters_he
parameters['W' + str(l)] = np.sqrt(2./layers_dims[l-1]) *np.random.randn(layers_dims[l], layers_dims[l-1])
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))

 

 

 

 

3. Gradient checking

 

그동안 우리는 gradient를 one-sided difference(grad)와 같이 계산했다. 하지만 two-sided difference(gradapprox)와 같이 계산하는 것이 더 정확하다.

 \(grad = \frac{J(\theta + \varepsilon) -J(\theta)}{\varepsilon}\)

 \(gradapprox = \frac{J(\theta + \varepsilon) -J(\theta - \varepsilon)}{2  \varepsilon}\)

 

 

하지만 two-sided difference로 backward propagation 하는 것은 계산 비용이 많이 들어서, gradient 계산은 one-sided difference로 하고 two-sided difference를 사용하여 잘 계산되었는지 확인할 수 있다.

 

\(difference = \frac {\mid\mid grad - gradapprox \mid\mid_2}{\mid\mid grad \mid\mid_2 + \mid\mid gradapprox \mid\mid_2}\)

difference가 \(10^{-7}\) 보다 작으면 훌륭한 결과이고, \(10^{-5}\)는 적당한 결과이지만 다시 살펴볼 필요가 있고, \(10^{-3}\) 보다 크면 다시 점검이 필요하다는 것을 의미한다.

 

 

# gradient_check_n
def gradient_check_n(parameters, gradients, X, Y, epsilon=1e-7, print_msg=False):
    # Set-up variables
    parameters_values, _ = dictionary_to_vector(parameters)
    grad = gradients_to_vector(gradients)
    num_parameters = parameters_values.shape[0]
    J_plus = np.zeros((num_parameters, 1))
    J_minus = np.zeros((num_parameters, 1))
    gradapprox = np.zeros((num_parameters, 1))
    # Compute gradapprox
    for i in range(num_parameters):
    	# Compute J_plus[i]
        theta_plus = np.copy(parameters_values)                                          # Step 1
        theta_plus[i] = theta_plus[i] + epsilon                                          # Step 2
        J_plus[i], _ =  forward_propagation_n(X, Y, vector_to_dictionary(theta_plus))    # Step 3        
        # Compute J_minus[i]
        theta_minus = np.copy(parameters_values)                                         # Step 1
        theta_minus[i] = theta_minus[i] - epsilon                                        # Step 2        
        J_minus[i], _ =  forward_propagation_n(X, Y, vector_to_dictionary(theta_minus))  # Step 3        
        # Compute gradapprox[i]
        gradapprox[i] = (J_plus[i] - J_minus[i]) / (2*epsilon)
    # Compare gradapprox to backward propagation gradients by computing difference.
    numerator = np.linalg.norm(grad - gradapprox)                                        # Step 1
    denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox)                      # Step 2
    difference = numerator / denominator                                                 # Step 3    
    return difference

 


'Deep Learning' 카테고리의 다른 글

2.3 Hyperparameter Tuning, Batch Normalization  (0) 2023.03.27
2.2 Optimization Algorithms  (0) 2023.03.14
1.3 Deep Neural Networks  (0) 2023.02.21
1.2 Shallow Neural Networks  (0) 2023.02.14
1.1 Neural Networks Basics  (1) 2023.02.07

댓글