1. 딥러닝 학습 기술

  • Optimization(매개변수 갱신(확률적 경사하강법, 모멘텀, AdaGrad, Adam))
  • Weight Decay
  • Batch Normalization
  • 과대적합(Overfitting) / 과소적합(Underfitting)
  • 규제화(Regularization)
  • 드롭아웃(Drop out)
  • 하이퍼 파라미터
    • 학습률(Learning Rate)
    • 학습 횟수
    • 미니배치 크기

 

 

2. 최적화 방법: 매개변수 갱신

  - 확률적 경사하강법(Stochastic Gradient Descent, SGD)

  • 전체를 한번에 계산하지 않고, 확률적으로 일부 샘플을 뽑아 조금씩 나누어 학습을 시키는 과정
  • 반복할 때마다 다루는 데이터의 수가 적기 때문에 한 번에 처리하는 속도는 빠름
  • 한 번 학습할 때 필요한 메모리만 있으면 되므로 매우 큰 데이터셋에 대해서도 학습이 가능
  • 확률적이기 때문에, 배치 경사하강법보다 불안정
  • 손실함수의 최솟값에 이를 때까지 다소 위아래로 요동치면서 이동
  • 따라서, 위와 같은 문제 때문에 미니 배치 경사하강법(mini-batch gradient descent)로 학습을 진행,
    요즘에는 보통 SGD라고 하면 미니 배치 경사하강법을 의미하기도 함
  • 배치 경사하강법
    미니 배치 경사하강법
    확률적 경사하강법
  • \( W \leftarrow W-\gamma \frac{\partial L}{\partial W} \)

    \( \gamma \): 학습률
class SGD:
    def __init__(self, learning_rate = 0.01):
        self.learning_rate = learning_rate

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.learning_rate * grads[key]

optimizer = SGD()

# 계속 optimizer을 update 해주며 동작
for i in range(10000):
    #optimizer.update(params, grads)
  • SGD 단점: 단순하지만, 문제에 따라서 시간이 매우 오래걸림

 

  - 모멘텀(Momentum)

  • 운동량을 의미, 관성과 관련
  • 공이 그릇의 경사면을 따라서 내려가는 듯한 모습
  • 이전의 속도를 유지하려면 성향
    경사하강을 좀 더 유지하려는 성격을 지님
  • 단순히 SGD만 사용하는 것보다 적게 방향이 변함

https://link.springer.com/chapter/10.1007/978-1-4842-4470-8_33\upsilon\leftarrow \alpha \upsilon -\gamma \frac{\partial L}{\partial W}

  • \( \upsilon\leftarrow \alpha \upsilon -\gamma \frac{\partial L}{\partial W}  \)
    \( W \leftarrow W-\upsilon \)

    \( \alpha \): 관성계수
    \( \upsilon \): 속도
    \( \gamma \): 학습률
    \( \frac{\partial L}{\partial W} \): 손실함수에 대한 미분
import numpy as np

class Momentum:
    def __init__(self, learning_rate = 0.01, momentum = 0.9):
        self.learning_rate = learning_rate
        self.momentum = momentum
        self.v = None
    
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            # 처음 알고리즘 시행 시, v를 초기화시킨 후,param의 key를 각각 할당해줌
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)
        
        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.learning_rate * grads[key]
            params[key] += self.v[key]

 

  - AdaGrad(Adaptive Gradient)

  • 가장 가파른 경사를 따라 빠르게 하강하는 방법
  • 적응적 학습률이라고도 함, 학습률을 변화시키며 진행
  • 경사가 급할 때는 빠르게 변화,
    완만할 때는 느리게 변화
  • 간단한 문제에서는 좋을 수 있지만 딥러닝에서는 자주 쓰이지 않음,
    학습률이 너무 감소되어 전여최소값(global minimum)에 도달하기 전에 학습이 빨리 종료될 수 있기 때문
  • \( h\leftarrow h+\frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} \)
    \( W \leftarrow W+\gamma \frac{1}{\sqrt{h}} \frac{\partial L}{\partial W} \)

    \( h \): 기존 기울기를 제곱하여 더한 값
    \( \gamma \): 학습률
    \( \frac{\partial L}{\partial W} \): \( W \)에 대한 미분
  • 과거의 기울기를 제곱하여 계속 더하기 때문에 학습을 진행할수록 갱신 강도가 약해짐(\( \because \frac{1}{\sqrt{h}} \))
class AdaGrad:
    def __init__(self, learning_rate = 0.01):
        self.learning_rate = learning_rate
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
        
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.learning_rate * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

 

  - RMSProp(Root Mean Square Propagation)

  • AdaGrad를 보완하기 위한 방법으로 등장
  • 합 대신 지수의 평균값을 활용
  • 학습이 안되기 시작하면 학습률이 커져 잘 되게끔하고, 학습률이 너무 크면 학습률을 다시 줄임
  • \( h  \leftarrow   \rho  h + (1 - \rho) \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} \)
    \(  W  \leftarrow  W  + \gamma \frac{\partial L}{\partial W}  / \sqrt{h + \epsilon} \)

    \( h \): 기존 기울기를 제곱하여 업데이트 계수를 곱한 값과 업데이트 계수를 곱한 값을 더해줌
    \( \rho \): 지수 평균의 업데이트 계수
    \( \gamma \): 학습률
    \( \frac{\partial L}{partial W} \): \( W \)에 대한 미분
class RMSProp:
    def __init__(self, learning_rate = 0.01, decay_rate = 0.99):
        self.learning_rate = learning_rate
        self.decay_rate = decay_rate
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
        
        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.learning_rate * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

 

  - Adam(Adaptive moment estimation)

  • 모멘텀 최적화와 RMSProp의 아이디어를 합친 것
  • 지난 그레디언트의 지수 감소 평균을 따르고(Momentum), 지난 그레디언트 제곱의 지수 감소된 평균(RMSProp)을 따름
  • 가장 많이 사용되는 최적화 방법
  • \( t \leftarrow t + 1 \)
    \( m_t \leftarrow \beta_1 m_{t-1} - (1 - \beta_1)\frac{\partial L}{\partial W} \)
    \( v_t \leftarrow \beta_2 v_{t-1} + (1 - \beta_2) \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} \)
    \( \hat{m_t} \leftarrow \frac{m_t}{1 - \beta_1^t} \)
    \( \hat{v_t} \leftarrow \frac{v_t}{1 - \beta_2^t} \)
    \( W_t \leftarrow W_{t-1} + \gamma \hat{m_t}/\sqrt{\hat{v_t} + \epsilon} \)

    \( \beta \): 지수 평균의 업데이트 계수
    \( \gamma \): 학습률
    \( \beta_{1} \approx 0.9, \beta_{2} \approx 0.999 \)
    \( \frac{\partial L}{\partial W} \): \( W \)에 대한 미분
class Adam:
    def __init__(self, learning_rate = 0.01, beta1 = 0.9, beta2 = 0.999):
        self.learning_rate = learning_rate
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        learning_rate_t = self.learning_rate * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)

        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])

            params[key] -= learning_rate_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

 

  - 최적화 방법 비교

https://github.com/ilguyi/optimizers.numpy

 

 

3. 가중치 소실(Gradient Vanishing) - AI 두번째 위기

  • 활성화함수가 Sigmoid 함수일 때, 은닉층의 개수가 늘어날수록 가중치가 역전파되면서 가중치 소실 문제 발생
    • 0~1 사이의 값으로 추력되면서 0 또는 1에 가중치 값이 퍼짐,
      이는 미분값이 점점 0에 가까워짐을 의미
    • ReLU 함수 등장(비선형 함수)
  • 가중치 초기화 문제(은닉층의 활성화값 분포)
    • 가중치의 값이 일부 값으로 치우치게 되면
      활성화 함수를 통과한 값이 치우치게 되고, 표현할 수 있는 신경망의 수가 적어짐
    • 따라서, 활성화값이 골고루 분포되는 것이 중요

https://www.kaggle.com/getting-started/118228

  • 가중치 초기화

  - 아래의 사이트에서 가중치와 기울기가 0일때, 너무 작을 때, 적절할 때, 너무 클 때의 훈련 과정을 볼 수 있음
https://www.deeplearning.ai/ai-notes/initialization/index.html

 

AI Notes: Initializing neural networks - deeplearning.ai

In this post, we'll explain how to initialize neural network parameters effectively. Initialization can have a significant impact on convergence in training deep neural networks...

www.deeplearning.ai

 

  - 초기값: 0(zeros)

  • 학습이 올바르게 진행되지 않음
  • 0으로 설정하면, 오차역전파법에서 모든 가중치 값이 똑같이 갱신됨
import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.zeros((nodes, nodes))
    a = np.dot(x, w)
    z = sigmoid(a)
    activation_values[i] = z

import matplotlib.pyplot as plt

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 초기값: 균일분포(Uniform)

  • 활성화 값이 균일하지 않음(활성화함수: sigmoid)
  • 역전파로 전해지는 기울기값이 사라짐
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.uniform(1, 10, (nodes, nodes))
    a = np.dot(x, w)
    z = sigmoid(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 초기값: 정규분포(nomalization)

  • 활성화함수를 통과하면 양쪽으로 퍼짐
  • 0과 1에 퍼지면서 기울기 소실문제(gradient vanishing) 발생
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes)
    a = np.dot(x, w)
    z = sigmoid(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 아주 작은 정규분포값으로 가중치 초기화

  • 0과 1로 퍼지지 않고, 한 곳에 치우쳐 짐
  • 해당 신경망이 표현할 수 있는 문제가 제한됨
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

	# 0.01을 곱해 작은 값으로 변경
    w = np.random.randn(nodes, nodes) * 0.01
    a = np.dot(x, w)
    z = sigmoid(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 초기값: Xavier(Glorot)

  • 은닉층의 노드의 수가 n이라면 표준편차가 \( \frac{1}{\sqrt{n}} \)인 분포
  • 더 많은 가중치에 역전파가 전달 가능하고, 비교적 많은 문제를 표현할 수 있음
  • 활성화 함수가 선형인 함수일 때 매우 적합
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes) / np.sqrt(nodes)
    a = np.dot(x, w)
    z = sigmoid(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 초기값: Xavier(Glorot) - tanh

  • 활성화함: tanh
  • sigmoid 함수보다 더 깔끔한 종모양으로 분포
# sigmoid 대신 tanh 사용
def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes) / np.sqrt(nodes)
    a = np.dot(x, w)
    z = tanh(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  • 비선형 함수에서의 가중치 초기화

  - 초기값: 0(zeros)

  • 활성화함수: ReLU
def ReLU(x):
    return np.maximum(0, x)

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.zeros((nodes, nodes))
    a = np.dot(x, w)
    z = ReLU(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

  • 초기값을 0으로 쓰는 것은 어떤 상황이든 좋지 않음

 

  - 초기값: 정규분포(nomalization)

  • 활성화함수: ReLU
def ReLU(x):
    return np.maximum(0, x)

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes)
    a = np.dot(x, w)
    z = ReLU(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 표준편차: 0.01일 때

def ReLU(x):
    return np.maximum(0, x)

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes) * 0.01
    a = np.dot(x, w)
    z = ReLU(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 초기값: Xavier(Glorot)

def ReLU(x):
    return np.maximum(0, x)

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes) / np.sqrt(nodes)
    a = np.dot(x, w)
    z = ReLU(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

  - 초기값: He

  • 표준편차가 \( \sqrt{\frac{2}{n}} \)인 분포
  • 활성화값 분포가 균일하게 분포되어 있음
  • 활성화함수가 ReLU와 같은 비선형함수 일 때 더 적합하다고 알려진 분포
def ReLU(x):
    return np.maximum(0, x)

x = np.random.randn(1000, 50)
nodes = 50
hidden_layers = 6
activation_values = {}

for i in range(hidden_layers):
    if i != 0:
        x = activation_values[i-1]

    w = np.random.randn(nodes, nodes) * np.sqrt(2 / nodes)
    a = np.dot(x, w)
    z = ReLU(a)
    activation_values[i] = z

plt.figure(figsize = (12, 6))
for i, a in activation_values.items():
    plt.subplot(1, len(activation_values), i+1)
    plt.title(str(i+1) + 'th layer')
    plt.hist(a.flatten(), 50, range = (0, 1))
    plt.subplots_adjust(wspace = 0.5, hspace = 0.5)

plt.show()

 

 

4. 배치 정규화(Batch Normalization)

  • 가중치의 활성화값이 적당히 퍼지게끔 '강제'로 적용시키는 것
  • 미니배치 단위로 데이터의 평균이 0, 표준편차가 1로 정규화
  • 학습을 빨리 진행할 수 있음
  • 초기값에 크게 의존하지 않아도 됨
  • 과적합을 방지
  • 보통 Fully-Connected와 활성화함수(비선형) 사이에 놓임

https://www.jeremyjordan.me/batch-normalization/

class BatchNormalization:
    def __init__(self, gamma, beta, momentum = 0.9, running_mean = None, running_var = None):
        self.gamma = gamma
        self.beta = beta
        self.momentum = momentum
        self.input_shape = None
        
        self.running_mean = running_mean
        self.running_var = running_var
        
        self.batch_size = None
        self.xc = None
        self.std = None
        self.dgamma = None
        self.dbeta = None
    
    def forward(self, input_data, is_train = True):
        self.input_shape = input_data.shape
        if input_data.ndim != 2:
            N, C, H, W = input_data.shape
            input_data = input_data.reshape(N, -1)

        out = self.__forward(input_data, is_train)

        return out.reshape(*self.input_shape)
    
    def __forward(self, input_data, is_train):
        if self.running_mean is None:
            N, D = input_data.shape
            self.running_mean = np.zeros(D)
            self.running_var = np.zeros(D)
        
        if is_train:
            mu = input_data.mean(axis = 0)
            xc = input_data - mu
            var = np.mean(xc**2, axis = 0)
            std = np.sqrt(var + 10e-7)
            xn = xc / std

            self.batch_size = input_data.shape[0]
            self.xc = xc
            self.std = std
            self.running_mean = self.momentum * self.running_rate + (1 - self.momentum) * mu 
            self.running_var = self.momentum * self.running_var + (1 - self.momentum) * var
        
        else:
            xc = input_data - self.running_mean
            xn = xc / ((np.sqrt(self.running_var + 10e-7)))
        
        out = self.gamma * xn + self.beta
        return out
    
    def backward(self, dout):
        if dout.ndim != 2:
            N, C, H, W = dout.shape
            dout = dout.reshape(N, -1)
        
        dx = self.__backward(dout)

        dx = dx.reshape(*self.input_shape)
        return dx
    
    def __backward(self, dout):
        dbeta = dout.sum(axis = 0)
        dgamma = np.sum(self.xn * dout, axis = 0)
        dxn = self.gamma * dout
        dxc = dxn / self.std
        dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis = 0)
        dvar = 0.5 * dstd / self.std
        dxc += (2.0 / self.batch_size) * self.xc * dvar
        dmu = np.sum(dxc, axis = 0)
        dx = dxc - dmu / self.batch_size

        self.dgamma = dgamma
        self.dbeta = dbeta

        return dx

 

 

5. 과대적합(Overfitting) / 과소적합(Underfitting)

https://towardsdatascience.com/underfitting-and-overfitting-in-machine-learning-and-how-to-deal-with-it-6fe4a8a49dbf

  - 과대적합 (Overfitting, 오버피팅)

  • 모델이 학습 데이터에 한에서만 좋은 성능을 보이고 새로운 데이터에는 그렇지 못한 경우
  • 학습 데이터가 매우 적을 경우- 모델이 지나치게 복잡한 경우
  • 학습 횟수가 매우 많을 경우
  • 해결방안
    • 학습 데이터를 다양하게 수집
    • 모델을 단순화(파라미터가 적은 모델을 선택하거나, 학습 데이터의 특성 수를 줄이거나)
    • 정규화(Regularization)을 통한 규칙을 단순화
    • 적정한 하이퍼 파라미터 찾기

 

  - 과소적합 (Underfitting, 언더피팅)

  • 학습 데이터를 충분히 학습하지 않아 성능이 매우 안 좋을 경우
  • 모델이 지나치게 단순한 경우
  • 해결방안
    • 충분한 학습 데이터 수집
    • 보다 더 복잡한 모델
    • 에폭수(epochs)를 늘려 충분히 학습

 

 

6. 규제화(Regularization) - 가중치 감소

  • 과대적합(Overfitting, 오버피팅)을 방지하는 방법 중 하나
  • 과대적합은 가중치의 매개변수 값이 커서 발생하는 경우가 많음,
    이를 방지하기 위해 큰 가중치 값에 큰 규제를 가하는 것
  • 규제란 가중치의 절댓값을 가능한 작게 만드는 것으로,
    가중치의 모든 원소를 0에 가깝게 하여 모든 특성이 출력에 주는 영향을 최소한으로 만드는 것(기울기를 작게 만드는 것)을 의미한다.
    즉, 규제란 과대적합이 되지 않도록 모델을 강제로 제한한다는 의미
  • 적절한 규제값을 찾는 것이 중요.

 

  - L2 규제

  • 가중치의 제곱합
  • 손실함수 일정 값을 더함으로써 과적합을 방지
  • \( \lambda \)값이 크면 가중치 감소가 커지고, 작으면 가하는 규제가 적어짐
  • 더 Robust한 모델을 생성하므로 L1보다 많이 사용됨
  • \(Cost = \frac{1}{n} \sum{^n}_{i=1} {L(y_i, \hat{y_i}) + \frac{\lambda}{2}w^2} \)
    \( L(y_i, \hat{y_i}) \): 기존 Cost Function
# 작동 원리
def loss(X, true_y):
    # weight_decay += 0.5 * weight_decay_lambda * np.sum(W**2)
    # return weight_decay

 

  - L1 규제

  • 가중치의 절대값 합
  • L2 규제와 달리 어떤 가중치는 0이 되는데 이는 모델이 가벼워짐을 의미
  • \( Cost = \frac{1}{n} \sum{^n}_{i=1} {L(y_i, \hat{y_i}) + \frac{\lambda}{2} \left | w \right |} \)
    \( L(y_i, \hat{y_i}) \): 기존 Cost Function
# 작동 원리
def loss(X, true_y):
    # weight_decay += 0.5 * weight_decay_lambda * np.sum(np.abs(W))
    # return weight_decay

 

 

7. 드롭아웃(Dropout)

  • 과적합을 방지하기 위한 방법
  • 학습할 때 사용하는 노드의 수를 전체 노드 중에서 일부만을 사용
  • 보통 ratio_value는 0.5 또는 0.7

https://medium.com/konvergen/understanding-dropout-ddb60c9f98aa

class Dropout:
    def __init__(self, dropout_ratio = 0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, input_data, is_train = True):
        if is_train:
            self.mask = np.random.rand(*input_data.shape) > self.dropout_ratio
            return input_data * self.mask
        else:
            return input_data * (1.0 - self.dropout_ratio)
        
    def backward(self, dout):
        return dout * self.mask

 

 

8. 하이퍼 파라미터(hyper Parameter)

  - 학습률(Learning Rate)

  • 적절한 학습률에 따라 학습 정도가 달라짐
  • 적당한 학습률을 찾는 것이 핵심

 

  - 학습 횟수(Epochs)

  • 학습 횟수를 너무 작게, 또는 너무 크게 지정하면 과소적합 또는 과대적합
  • 몇 번씩 진행하며 최적의 epochs 값을 찾기

 

  - 미니 배치 크기(Mini Batch Size)

  • 미니 배치 학습: 한번 학습할 때 메모리의 부족현상을 막기 위해 전체 데이터의 일부를 여러 번 학습하는 방식
  • 한번 학습할 때마다 얼마만큼의 미니배치 크기를 사용할 지 결정
  • 배치 크기가 작을수록 학습 시간이 많이 소요되고,
    클수록 학습 시간이 적게 소요됨

 

 

9. 검증 데이터(Validation Data)

  • 주어진 데이터를 학습 + 검증 + 테스트 데이터로 구분하여 과적합을 방지
  • 일반적으로 전체 데이터의 2~30%를 테스트 데이터,
    나머지 20% 정도를 검증용 데이터,
    남은 부분을 학습용 데이터로 사용

https://towardsdatascience.com/train-test-split-and-cross-validation-in-python-80b61beca4b6

+ Recent posts