오답노트

[밑딥] 학습 관련 기술들 본문

Python/DL

[밑딥] 학습 관련 기술들

권멋져 2023. 2. 27. 22:07

매개변수 갱신

신경망 학습의 목적은 손실 함수의 값을 가능한 한 낮추는 매개변수를 찾는 것이고 이는 곧 매개변수의 최적값을 찾는 문제이다. 이러한 문제를 푸는 것을 최적화라고 한다.

지금까지 최적의 매개변수 값을 찾는 단서로 매개변수의 기울기(미분)를 이용했다. 매개변수의 기울기를 구해, 기울어진 방향으로 매개변수 값을 갱신하는 일을 몇번이고 반복해서 점점 최적의 값에 다가갔다. 이것이 확률적 경사 하강법(SGD)이란 단순한 방법인데, 지금부터 SGD의 단점과 다른 최적화 기법을 알아보자.

 

모험가 이야기

가장 크게 기울어진 방향으로 가자는 것이 SGD의 전략이다. 이 일을 반복하면 언젠가 깊은 곳에 찾아갈 수 있을지 모른다.

 

학률적 경사 하강법(SGD)

class SGD:
  def __init__(self, lr=0.01):
    self.lr = lr
  
  def update(self, params, grads):
    for key in params.keys():
      params[key] -= self.lr * grads[key]

 

SGD 단점

SGD는 단순하고 구현도 쉽지만, 문제에 따라서는 비효율적일 때가 있다. SGD의 단점은 비등방성 함수로 방향에 따라 성질, 즉 여기에서는 기울기가 달라지는 함수에서는 탐색 경로가 비효율적이라는 것이다. 기울어진 방향이 본래의 최솟값과 다른 방향을 가리켜서 라는 점도 생각해봐야 한다.

 

모멘텀

모멘텀(Momentum)은 운동량을 뜻하는 단어로, 모멘텀의 갱신 경로는 공이 그릇 바닥을 구르듯 움직인다. SGD와 비교하면 '지그재그 정도'가 덜하다.

 

AdaGrad

신경망 학습에서는 학습률이 중요한데 이 값이 너무 작으면 학습 시간이 너무 길어지고, 반대로 너무 크면 발산하여 학습이 제대로 이뤄지지 않는다.

이 학습률을 정하는 효과적 기술로 학습률 감소(learning rate decay)가 있다. 이는 학습을 진행하면서 학습률을 점차 줄여가는 방법이다. 학습률을 서서히 낮추는 가장 간단한 방법은 매개변수 '전체'의 학습률 값을 일괄적으로 낮추는 것이다. AdaGrad는 각각의 매개변수에 맞춤형 값을 만들어준다.

import numpy as np

class AdaGrad:
  def __init__(self. lr=0.01):
    self.lr = lr
    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.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

여기서 주의할 것은 마지막 줄에서 1e-7이라는 작은 값을 더하는 것이다. self.h[key]에 0이 여도 0으로 나누는 사태를 막아준다.

 

Adam

모멘텀은 공이 그릇 바닥을 구르는 듯한 움직임을, AdaGrad는 매개변수의 원소마다 적응적으로 갱신 정도를 조정했다. 이 두 기법을 융합하면 Adam이 된다.

매개변수 공간을 효율적으로 탐색해줄 것과 편향 보정이 진행된다는 점이 Adam의 특징이다.

 

어느 갱신 방법을 이용할 것인가?

유감스럽게도 모든 문제에서 항상 뛰어난 기법이라는 것은 아직까진 없다.

 

가중치의 초깃값

신경망 학습에서 특히 중요한 것이 가중치의 초깃값이다.

 

초깃값을 0으로 하면?

오버피팅을 억제해 범용 성능을 높이는 테크닉인 가중치 감소 weghit decay 기법을 소개한다. 가중치 감소는 가중치 매개변수의 값이 작아지도록 학습하는 방법이다. 가중치 값을 작게하여 오버피팅이 일어나지 않게 하는 것이다.

 

은닉층의 활성화값 분포

은닉층의 활성화 값(활성화 함수의 출력 데이터)의 분포를 관찰하면 중요한 정보를 얻을 수 있다.

 

import numpy as np
import matplotlib.pyplot as plt

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

x = np.random.randn(1000,100)
node_num = 100
hidden_layer_size = 5
activations = {}

for i in range(hidden_layer_size):
  if i != 0:
    x = activations[i-1]
  
  #w = np.random.randn(node_num, node_num) * 1
  #w = np.random.randn(node_num, node_num) * 0.01
  w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
  a = np.dot(x,w)
  z = sigmoid(a)
  activations[i] = z
for i, a in activations.items():
  plt.subplot(1,len(activations), i+1)
  plt.title(str(i+1) + '-layer')
  plt.hist(a.flatten(),30,range=(0,1))

plt.show()

for i, a in activations.items():
  plt.subplot(1,len(activations), i+1)
  plt.title(str(i+1) + '-layer')
  plt.hist(a.flatten(),30,range=(0,1))

plt.show()

표준편차가 1인 정규분포를 이용했는데, 각 층의 활성화값들이 0과 1에 치우쳐 분포되어 있다. 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다. 이것을 기울기 소실 (gradient vanishing)이라는 문제다.

표준편차가 0.01로한 정규분포의 경우 0.5부근에 집중되었다. 앞의 예처럼 0과 1로 치우치진 않았으니 기울기 소실 문제는 일어나지 않지만, 활성화값들이 치우쳤다는 것은 표현력 관점에서는 큰 문제가 있는 것이다. 다수의 뉴런이 거의 같은 값을 출력하고 있으니 뉴런을 여러개 둔 의미가 없다는 뜻이다. 활성화값들이 추이치면 표현력을 제한한다는 관점에서 문제가 된다.

Xavier 초깃값은 일반적으로 딥러닝 프레임워크들이 표준적으로 사용하고 있는 초깃값이다. Xavier 초깃값은 초깃밧의 표준편차가 1/√n 이 되도록 설정하는 것이다. 여기서 n은 앞 층의 노드 수다. Xavier 초깃값을 사용하면 앞 층에 노드가 많을수록 대상 노드의 초깃값으로 설정하는 가중치가 좁게 퍼진다.

 

for i, a in activations.items():
  plt.subplot(1,len(activations), i+1)
  plt.title(str(i+1) + '-layer')
  plt.hist(a.flatten(),30,range=(0,1))

plt.show()

ReLU를 사용할 때의 가중치 초깃값

Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과다. 반면 ReLU를 이용할 때는 ReLU에 특화된 초깃값을 이용하라고 권장한다. He 초깃값은 앞 계층의 노드가 n개일 때 표준편차가 √2/n 인 정규분포를 사용한다. ReLU는 음의 영역이 0이라서 더 넓게 분포시키기 위해 2배의 계수가 필요하다고 해석할 수 잇다.

 

배치 정규화

가중치의 초깃값을 적절히 설정하면 각 층의 활성화값 분포가 적당히 퍼지면서 학습이 원활하게 수행됨을 알았다. 그렇다면 각 층이 활성화를 적당히 퍼뜨리도록 강제하는 배치 정규화를 해보자

배치 정규화 알고리즘

  • 학습을 빨리 진행할 수 있다.(학습 속도 개선)
  • 초깃값에 크게 의존하지 않는다 (골치 아픈 초깃값 선택 장애 없음)
  • 오버피팅을 억제한다(드롭 아웃등의 필요성 감소)

배치 정규화는 Batch Norm이라는 계층으로 데이터 분포가 평균이 0, 분산이 1이 되도록 정규화한다. 또, 배치 정규화 계층마다 이 정규화된 데이터에 고유한 확대와 이동 변환을 수행한다.

배치 정규화의 효과

배치 정규화를 사용할 때의 학습 진도가 빠른 것으로 나타난다. 실제로 배치 정규화를 이용하지 않는 경우엔 초깃값이 잘 분포되어 있지 않으면 학습이 전혀 진행되지 않는 모습도 확인할 수 있다.

 

바른 학습을 위해

기계학습에서는 오버피팅 문제가 되는 일이 많다. 아직 보지 못한 데이터가 주어져도 바르게 식별해내는 모델이 바람직하다. 복잡하고 표현력이 높은 모델을 만들 수는 있지만, 그만큼 오버피팅을 억제하는 기술이 중요해진다.

 

오버피팅

오버피팅은 주로 매개변수가 많고 표현력이 높은 모델, 훈련 데이터가 적을 때 발생한다.

훈련 데이터를 사용하여 측정한 정확도는 거의 100%에 가깝다. 그러나 시험 데이터에 대해서는 큰 차이를 보이는데 이것이 훈련 데이터에만 적응해버린 결과다.

 

가중치 감소

오버비팅 억제용으로 많이 이용해온 방법 중 가중치 감소라는 것이 있다. 이는 학습 과정에서 큰 가중치에 대해서는 그에 상응하는 큰 패널티를 부과하여 오버피팅을 억제하는 방법이다. 원래 오버피팅은 가중치 매개변수의 값이 커서 발생하는 경우가 많기 때문이다.

 

드롭아웃

가중치 감소는 간단하게 구현할 수 있고 어느 정도 지나친 학습을 억제할 수 있다. 그러나 신경망 모델이 복잡해지면 가중치 감소만으로 대응하기 어렵다. 이럴 때 흔히 드롭아웃이라는 기법을 사용한다. 드롭아웃은 뉴런을 임의로 삭제하면서 학습하는 방법이다. 훈련 때 은닉층의 뉴런을 무작위로 골라 삭제하고, 시험 때는 모든 뉴런에 신호를 전달한다. 단, 시험 때는 각 뉴럭의 출력에 훈련 때 삭제한 비율을 곱하여 출력한다.

 

class Dropout:
  def __init__(self, dropout_ratio=0.5):
    self.dropout_ratio = dropout_ratio
    self.mask = None
  
  def forward(self, x, train_flg=True):
    if train_flg:
      self.mask = np.random.rand(*x.shape) > self.dropout_ratio
      return x * self.mask
    else:
      return x * (1.0 - self.dropout_ratio)
  
  def backward(self, dout):
    return dout * self.mask

여기서 핵심은 훈련 시에는 순전파 때마다 self.mask에 삭제할 뉴런을 False로 표시한다는 것이다. self.mask는 x와 형상이 같은 배열을 무작위로 생성하고, 그값이 dropout_ratio보다 큰 원소만 True로 설정한다. 역전파 때의 동작은 ReLU와 같다. 즉, 순전파 때 신호를 통과시키는 뉴런은 역전파 때도 신호를 그대로 통과시키고, 순전파 때 통과시키지 않은 뉴런은 역전파 때도 신호를 차단한다.

 

적절한 하이퍼파라미터 값 찾기

신경망에는 하이퍼파라미터가 다수 등장하는데 이를테면 각 층의 뉴런 수, 배치 크기, 매개변수 갱신 시의 학습률과 가중치 감소 등이 있다. 이러한 하이퍼파라미터의 값을 적절히 설정하지 않으면 모델의 성능이 크게 떨어지기도 한다.

 

검증 데이터

하이퍼 파라미터를 조정할 때는 하이퍼파라미터 전용 확인 데이터가 필요하다. 이런 데이터를 검증 데이터 (validation data)라고 부른다.

 

하이퍼파라미터 최적화

하이퍼파라미터를 최적화할 때의 핵심은 하이퍼파라미터의 '최적 값'이 존재하는 범위를 조금씩 줄여간다는 것이다.

  • 0단계 : 하이퍼파라미터 값의 범위를 설정한다.
  • 1단계 : 설정된 범위에서 하이퍼파라미터의 값을 무작위로 추출
  • 2단계 : 1단계에서 샘플링한 하이퍼파라미터 값을 사용하여 학습하고, 검증 데이터로 정확도를 평가한다. (단 에폭은 작게 설정)
  • 3단계 : 1단계와 2단계를 특정 회수 (100회 등) 반복하여, 그 정확도의 결과를 보고 하이퍼파라미터의 범위를 좁힌다.

 

정리

  • 매개변수 갱신 방법에는 확률적 경사 하강법(SGD) 외에도 모멘텀, AdaGrad, Adam등이 있다.
  • 가중치 초깃값을 정하는 방법은 올바른 학습을 하는데 매우 중요하다.
  • 가중치의 초깃값으로는 'Xavier 초깃값'과 'He 초깃값'이 효과적이다.
  • 배치 정규화를 이용하면 학습을 빠르게 진행할 수 있으며, 초깃값에 영향을 덜 받게 된다.
  • 오버피팅을 억제하는 정규화 기술로는 가중치 감소와 드롭아웃이 있다.
  • 하이퍼파라미터 값 탐색은 최적값이 존재할 법한 범위를 점차 좁히면서 하는 것이 효과적이다.
 

'Python > DL' 카테고리의 다른 글

[NLP/RS] Negative Sampling  (0) 2023.03.03
[밑딥] 합성곱 신경망 (CNN)  (0) 2023.02.27
[밑딥] 오차역전파법  (0) 2023.02.27
[밑딥] 신경망 학습  (0) 2023.02.27
[밑딥] 신경망  (0) 2023.01.21