[머신러닝 기초] How to train Neural Network?
ML

[머신러닝 기초] How to train Neural Network?

어떻게 Neural Network를 학습시킬 수 있을까?

Neural net은 두 가지 방향의 계산을 통해서 학습이 이루어진다.

  1. forward propagation: 현재 weight에 대해 loss를 계산한다.
  2. back propagation: loss를 뒤에서부터 앞으로 보내면서 가중치를 업데이트한다.

 

 

Backpropagation

 

backpropagation(역전파)이란 역방향으로 오차를 전파시키면서 각 층의 가중치를 업데이트하고 최적의 학습 결과를 찾아가는 방법이다. 역전파를 이용한 가중치 업데이트 절차는 아래와 같이 요약할 수 있다.

1. 주어진 가중치 값을 이용해 출력층의 출력값을 계산함.

2. 오차를 각 가중치로 미분한 값을 기존 가중치에서 빼준다. (여기서 경사 하강법을 적용하며, 역전파를 통해 이루어진다.)

3. 2번 단계를 모든 가중치에 대해 이루어진다.

4. 1~3단계를 주어진 학습횟수만큼 또는 주어진 허용 오차값에 도달할 때까지 반복한다.

Vanishing Gradient

학습이란, 기울기가 작아지는 방향으로 업데이트를 반복하는 과정을 의미한다. 하지만 시그모이드와 같은 활성화함수는 미분값이 작기 때문에 기울기가 실제 데이터 입력값에 비해 은닉층이나 출력층의 출력값이 작게 나온다. 이 과정에서 작아진 기울기는 학습의 효율을 저하시킨다.

 

 

위 그림과 같이 시그모이드 함수는 미분의 최대값이 0.25로 1보다 작다. 은닉층이 깊은 경우 미분값을 가중치에 곱하는 횟수와 그 깊이가 비례하여 증가하게 되는데, 이 과정에서 가중치는 0에 수렴하여 사라지게 된다. 이러한 현상을 Vanishing Gradient라고 하며, 이를 해결하기 위해 ReLU가 가장 많이 사용된다.

Gradient Descent

 

경사하강법(Gradient Descent)는 오차 역전파의 과정에서 중요한 개념이다. 훈련 시 사용되는 전체 데이터를 미분하여 기울기가 낮은 쪽으로 계속 이동시켜 극값을 구하면, 이것을 근사값(예측값)으로 확정하는 원리이다.

 

경사 하강법은 한 번 업데이트 할 때마다 전체 데이터를 미분해야 하기 때문에 진행속도가 느리다는 단점이 있다. 이것을 보완하기 위해서 개선된 방법이 확률적 경사 하강법이다. 확률적 경사 하강법은 업데이트 시 전체 데이터를 미분하는 것이 아니라 랜덤 추출된 일부 데이터를 사용하기 때문에 더 빠른 수행속도를 기대할 수 있다.

GD 종류

  • Stochastic Gradient Descent (SGD): 데이터 마다(batch=1) gradient를 이용해 weight를 업데이트
  • Mini-Batch Gradient Descent (MBGD) : batch의 gradient 평균을 이용해 weight를 업데이트
  • Gradient Descent (GD) : 전체 데이터셋의 gradient 평균을 이용하여 weight를 업데이트

 

하지만.. 위의 내용들만으로는 역전파에서 어디에 경사하강법을 적용한다는 것이 잘 이해가 되지 않았다. 😥

 

Pytorch 예제에서 가져온 신경망의 순전파와 역전파 코드

# -*- coding: utf-8 -*-

import torch


dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # GPU에서 실행하려면 이 주석을 제거하세요.

# N은 배치 크기이며, D_in은 입력의 차원입니다;
# H는 은닉층의 차원이며, D_out은 출력 차원입니다.
N, D_in, H, D_out = 64, 1000, 100, 10

# 무작위의 입력과 출력 데이터를 생성합니다.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# 무작위로 가중치를 초기화합니다.
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6
for t in range(500):
    # 순전파 단계: 예측값 y를 계산합니다.
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

    # 손실(loss)을 계산하고 출력합니다.
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

    # 손실에 따른 w1, w2의 변화도를 계산하고 역전파합니다.
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    # 경사하강법(gradient descent)를 사용하여 가중치를 갱신합니다.
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

위 코드에서 보니, 먼저 순전파를 통해 에측값 y_pred를 계산하고, 그 예측값인 y_predy간의 손실을 계산한다. 이후 손실에 따른 가중치(w1, w2)의 변화도를 계산하고 역전파하는 과정을 거친다. 이 과정에서 역전파를 통해 계산된 새로운 가중치(grad_w1, grad_w2)가 구해진다. 이후, 경사하강법을 적용(lrgrad_w1, grad_w2)하여 가중치(w1,w2)를 갱신한다.

 

 

흠.. 하지만 아직도 경사하강법과 역전파의 관계를 얼핏 이해만 되지 정확히 이해되지는 않는다.

 

Review Gradient descent algorithm

Loss function은 실제 값과 추정 값의 차이를 나타내는 함수이다. 우리의 목적은 이 loss function최소화하는 것이다. gradient descent는 이 loss를 최소화하는 weight를 찾는 방법 중 하나이다. 현 위치에서 gradient가 가장 급격한 방향으로 조금씩 이동(learning rate)하며 최소값으로 도달하며 loss를 최소화한다.
즉, 가중치에 대한 편미분을 통해 각각의 가중치가 loss에 얼마나 영향을 미치는 지를 나타내는 값이 gradient이고, 이에 반대되는 방향으로 얼마나 움직일지를 결정하는 것이 learning rate이다.

 

결국 정리하자면! backpropagation을 통해서 weight를 업데이트하는데, 이 과정에서 편미분이 사용되며 (역으로 계산하기 위해서) 각 가중치에 해당하는 편미분 값을 learning rate만큼 곱한 뒤, 이전 step의 값에서 빼줌으로서 해당 가중치를 업데이트한다! 여기서 learning rate와 편미분 값을 곱하여 빼주는 과정을 어떠한 그래프에서 기울기가 급격한 쪽으로 이동하는 것, 즉 GD라고 볼 수 있다. 따라서 backpropagation 과정 내에 편미분, GD가 모두 이루어지면서 weight를 업데이트 해 나가는 것이다!

 

참고자료

모두를 위한 딥러닝-딥넷트웍 학습 시키기

예제로 배우는 파이토치