6. 학습 관련 기술들

이번장에서 배운 내용

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

매개변수 갱신

  • 신경망 학습의 목적은 손실 함수의 값을 가능한 한 낮추는 매개변수를 찾는 것 → 매개변수의 최적화
  • SGD 방법 이외에 Momentum, AdaGrad, Adam 등의 방법이 있다.
  • 사용 기법에 따라 갱신 경로가 다르다.
  • 일반적으로 SGD보다 다른 세 기법이 빠르게 학습하고, 때로는 최종 정확도도 높게 나타난다.

확률적 경사 하강법(SGD)

  • 매개변수의 기울기를 구해, 기울어진 방향으로 매개변수 값을 갱신하는 일을 반복해서 최적의 값을 구하는 것

SGD

  • SGD의 단점
    • 비등방성 함수(anisotropy 함수: 방향에 따라 성질, 즉 기울기가 달라지는 함수)에서는 탐색 경로가 비효율적
    • 이러한 단점을 개선해주는 Momentum, AdaGrad, Adam 방법

모멘텀(Momentum)

  • Momentum은 운동향을 뜻하는 단어로 물리와 관계가 있음

Momentum

  • 모멘텀의 갱신 경로는 공이 그릇 바닥을 구르듯 움직인다.
  • SGD와 비교하면, ‘지그재그 정도’가 덜한 것을 알 수 있다.
  • 이는 x축의 힘은 아주 작지만 방향을 변하지 않아서 한 방향으로 일정하게 가속하기 때문
  • 거꾸로 y축의 힘은 크지만 위아래로 번갈아 받아서 상충하여 y축 방향의 속도는 안정적이지 않다.
  • SGD보다 x축 방향으로 빠르게 다가가 지그재그 움직임이 들어든다.

AdaGrad

  • 신경망 학습에서는 학습률(learning rate) 값이 중요하다.
    • 값이 너무 작으면 학습 시간이 너무 길어지고, 반대로 너무 크면 발산하여 학습이 제대로 이뤄지지 않는다.
  • 학습률을 정하는 효과적 기술로 학습률 감소(learning rate decay)가 있다.
    • 학습을 진행하면서 학습률을 점차 줄여가는 방법으로, 처음에는 크게 학습하다가 조금씩 작게 학습하는 것
  • 학습률을 서서히 낮추는 가장 간단한 방법은 매개변수 전체 의 학습률 값을 일괄적으로 낮추는 것이다.

  • AdaGrad 는 이를 더욱 발전시켜서, 각각의 매개변수에 맞춤형 값을 만들어준다.
  • AdaGrad는 개별 매개변수에 적응적으로 학습률을 조정하면서 학습을 진행한다.
  • AdaGrad는 과거의 기울기를 제곱하여 계속 더해간다.
    • 학습을 진행할수록 갱신 강도가 약해진다.
    • 실제로 무한히 계속 학습한다면 어느 순간 갱신량이 0이 되어 전혀 갱신되지 않게 된다.
    • 이를 갱신한 방법으로 RMSProp이라는 방법이 있다.
    • RMSProp은 과거의 모든 기울기를 균일하게 더해가는 것이 아니라, 먼 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영한다.
    • 이를 지수이동평균(Exponential Moving Average, EMA)라고 하며, 과거 기울기의 반영 규모를 기하급수적으로 감소시킨다.

AdaGrad

Adam

  • 모멘텀은 공이 그릇 바닥을 구르는 듯한 움직임을 보였고, AdaGrad는 매개변수의 원소마다 적응적으로 갱신 정도를 조정했다.
    • 이 두 기법을 융합한 것이 Adam이다.
  • 매개변수 공간을 효율적으로 탐색해주며, 하이퍼파라미터의 편향 보정 이 진행된다.
  • Adam은 하이퍼파라미터를 3개 설정한다.
    • 학습률 a
    • 일차 모멘텀용 계수 b1
    • 이차 모멘텀용 계수 b2
    • 논문에 따르면 기본 설정값은 b1=0.9, b2=0.999

가중치의 초깃값

  • 오버피팅을 억제해 범용 성능을 높이는 테크닉인 가중치 감소(weight decay)
    • 가중치 매개변수의 값이 작아지도록 학습하는 방법으로, 가중치를 작게 하여 오버피팅이 일어나지 않게 하는 것
    • 그러나 초깃값을 모두 0으로 해서는 안된다. 정확히는 가중치를 균일한 값으로 설정해서는 안된다.
    • 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다.
    • 이는 가중치를 여러 개 갖는 의미를 사라지게 한다.
    • 그러므로 초깃값을 무작위로 설정 해야 한다.

은닉층의 활성화값 분포

  • 가중치의 초깃값 에 따라 은닉층 활성확밧들이 어떻게 변화하는지 체크
### 가중치를 표준편차가 1인 정규분포로 초기화할 때의 각 층의 활성화값 분포 ###

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

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

x = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 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
    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()

출력

  • 각 층의 활성화 값들이 0과 1에 치우쳐 분포되어 있다.
  • 시그모이드 함수는 그 출력이 0에 가까워지자 그 미분은 0에 다가간다.
    • 그래서 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다.
    • 이것을 기울기 소실(gradient vanishing) 문제라고 한다.
    • 층을 깊게하는 딥러닝에서는 기울기 소실은 더 심각한 문제가 될 수 있다.
### 가중치를 표준편차가 0.01인 정규분포로 초기화할 때의 각 층의 활성화값 분포 ###
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

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

x = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이 곳에 활성화 결과(활성화 값)를 저장

for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]
    w = np.random.randn(node_num, node_num) * 0.01
    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()

출력

  • 이번에는 0.5에 집중되어 있다.
  • 활성화값들이 치우쳤다는 것은 표현력 관점에서는 큰 문제이다.
  • 다수의 뉴런이 거의 값은 값을 출력하고 있으니 뉴런을 여러 개 둔 의미가 없어진다는 것이다.
  • 그래서 활성화값들이 치우치면 표현력을 제한 한다는 관점에서 문제가 된다.
    • 각 층의 활성화 값은 적당히 고루 분포되어야 한다.

Xavier 초깃값

  • Xavier Glorot, Yoshua Bengio의 논문에서 권장하는 가중치 초깃값
  • 이 논문에서는 각 층의 활성화값들을 광범위하게 분포시킬 목적으로 가중치의 적절한 분포를 찾고자 함
\[W\sim N({ 0 }, Var(W))\] \[Var(W)=\sqrt{\frac { 1 }{ { n } } }\]

($n$ : 이전 layer(input)의 노드 수)

  • 앞 층에 노드가 많을수록 대상 노드의 초깃값으로 설정하는 가중치가 좁게 퍼진다.
  • Xavier의 논문은 앞 층의 입력 노드 수 외에 다음 층의 출력 노드 수도 고려한 설정값을 제안한다.
\[W\sim N({ 0 }, Var(W))\] \[Var(W)=\sqrt{\frac { 2 }{ { n }_{ in }+{ n }_{ out } } }\]

($n_{in}$ : 이전 layer(input)의 노드 수, $n_{out}$ : 다음 layer의 노드 수)

### 가중치를 초깃값으로 Xavier Initialization을 이용할 때의 각 층의 활성화값 분포 ###
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

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

x = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이 곳에 활성화 결과(활성화 값)를 저장

for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]
    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()

출력

  • 층이 깊어지면서 형태가 다소 일그러지지만, 앞에서 본 방식보다는 확실히 넓게 분포됨을 알 수 있다.
  • 오른쪽으로 갈수록 약간씩 일그러지고 있는데, 이 일그러짐은 sigmoid 함수 대신 tanh 함수(쌍곡선 함수)를 이용하면 개선된다.
    • 실제로 tanh 함수를 이용하면 말끔한 종 모양으로 분포된다.
    • tanh 함수가 원점(0,0)에서 대칭인 S 곡선인 반면, sigmoid 함수는 (x,y)=(0,0.5)에서 대칭인 S 곡선이다.
    • 활성화 함수용으로는 원점에서 대칭인 함수가 바람직하다고 알려져있다.

He 초깃값

  • Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과다.
    • sigmoid 함수와 tanh 함수는 좌우 대칭이라 중앙 부근이 선형인 함수로 볼 수 있다.
    • 그래서 Xavier 초깃값이 적당하다
  • 반면, ReLU를 이용할 때는 이에 특화된 초깃값을 이용해야 한다.
    • 그것이 바로 He 초깃값이다.
    • ReLU는 음의 영역이 0이라서 더 넓게 분포시키기 위해 2배의 계수가 필요하다고 직감적으로 해석할 수 있다.
\[W\sim N({ 0 }, Var(W))\] \[Var(W)=\sqrt{\frac { 2 }{ { n }_{ in } } }\]

($n_{in}$ : 이전 layer(input)의 노드 수)

활성화 함수로 ReLU를 사용한 경우의 가중치 초깃값에 따른 활성화값 분포 변화

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

x = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이 곳에 활성화 결과(활성화 값)를 저장

## 표준편차가 0.01인 정규분포를 가중치 초깃값으로 사용한 경우 ##
for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]
    w = np.random.randn(node_num, node_num) * 0.01
    a = np.dot(x, w)
    z = relu(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.ylim((0,7001))
plt.show()

출력

x = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이 곳에 활성화 결과(활성화 값)를 저장

## Xavier 초깃값을 사용한 경우 ##
for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]
    w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
    a = np.dot(x, w)
    z = relu(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.ylim((0,7001))
plt.show()

출력

x = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이 곳에 활성화 결과(활성화 값)를 저장

## He 초깃값을 사용한 경우 ##
for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]
    w = np.random.randn(node_num, node_num) / np.sqrt(node_num) * 2
    a = np.dot(x, w)
    z = relu(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.ylim((0,7001))
plt.show()

출력

  • std = 0.01일 때의 각 층의 활성확밧들은 아주 작은 값
    • 역전파 때 가중치의 기울기 역시 작아진다는 뜻
    • 실제로도 핛브이 거의 이뤄지지 않을 것
  • Xavier 초깃값 결과를 보면 층이 깊어지면서 치우침이 조금씩 커진다.
    • 학습할 때, 기울기 소실 문제을 일으킴
  • He 초깃값은 모든 층에서 균일하게 분포됨
    • 층이 깊어져도 분포가 균일하게 유지되기에 역전파 때도 적절한 값이 나올 것으로 기대할 수 있음
  • 활성화 함수로 ReLU__를 사용할 때는 __He 초깃값 을, sigmoidtanh 등의 S자 모양 곡선일 때는 Xavier 초깃값 사용

배치 정규화

  • 가중치 초깃값을 적절히 설정하면 각 층의 활성화값 분포가 적당히 퍼지면서 학습이 원활하게 수행됨
    • 각 층이 활성화를 적당히 퍼뜨리도록 ‘강제’하는 것 → 배치 정규화
  • 배치 정규화의 장점
    • 학습 속도 개선
    • 초깃값에 크게 의존하지 않는다.
    • 오버피팅 억제 로 드롭아웃 등의 필요성 감소

배치정규화


오버 피팅 방지

  • 오버피팅이란, 신경망이 훈련 데이터에만 지나치게 적응되어 그 외의 데이터에는 제대로 대응하지 못하는 상태를 말한다.
  • 주로 다음의 두 경우에 일어난다.
    • 매개변수가 많고 표현력이 높은 모델
    • 훈련 데이터가 적음
  • 오버피팅 억제를 위해 사용하는 방법은 가중치 감소, 드롭아웃

가중치 감소(weight decay)

  • 큰 가중치에 대해서는 그에 상응하는 큰 페널티를 부과하여 오버피팅을 억제하는 방법
  • 가중치 감소는 모든 가중치 각각의 손실함수에 $\frac 12{\lambda}W^2$ 을 더한다.
    • 가중치의 기울기를 구하는 계산에서는 그동안의 오차역전파법에 따르 결과에 정규화 항을 미분한 ${\lambda}W$을 더한다.

드롭아웃(dropout)

  • 드롭아웃이란 뉴런을 임의로 삭제하면서 학습하는 방법이다.
  • 훈련 때 은닉층의 뉴런을 무작위로 골라 삭제한다. 삭제된 뉴런은 신호를 전달하지 않게 된다.
  • 훈련 때는 데이터를 흘릴 때마다 삭제할 뉴런을 무작위로 선택하고, 시험 때는 모든 뉴런에 신호를 전달한다.
  • 단, 시험 때는 각 뉴런의 출력에 훈련 때 삭제 안한 비율을 곱하여 출력한다.

드롭아웃

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
  • 앙상블 학습은 드롭아웃과 밀접하다.
  • 드롭아웃이 학습 떄 뉴런을 무작위로 삭제하는 행위를 매번 다른 모델을 학습시키는 것으로 해석할 수 있기 때문이다.
  • 드롭아웃은 앙상블 학습과 같은 효과를 대략 하나의 네트워크로 구현했다고 생각할 수 있다.

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

검증 데이터

  • 하이퍼파라미터의 성능을 평가할 때는 시험 데이터(test data)를 사용해서 안된다.
    • 하이퍼파라미터 값이 시험 데이터에 오버피팅 되기 때문이다.
  • 하이퍼파라미터 조정용 데이터를 일반적으로 검증 데이터(validation data) 라고 한다.
    • 훈련 데이터(train data): 매개변수 학습
    • 검증 데이터(validation data): 하이퍼파라미터 성능 평가
    • 시험 데이터(test data): 신경망의 범용 성능 평가

하이퍼파라미터 최적화

  • 하이퍼파라미터의 최적 값 이 존재하는 범위를 조금씩 줄여가는 것이 핵심이다.
    • grid search같은 규칙적인 탐색보다는 무작위로 샘플링해 탐색하는 편이 좋은 결과를 낸다고 알려져 있다.
    • 이는 최종 정확도에 미치는 영향력이 하이퍼파라미터마다 다르기 때문이다
  • 하이퍼파라미터의 범위는 대략적으로 지정하는 것이 효과적인데, 실제로도 0.001 ~ 1000 사이와 같이 10의 거듭제곱 단위로 범위를 지정한다.
    • 이를 로그 스케일 로 지정한다고 한다.
  • 하이퍼파라미터를 최적화할 때는 딥러닝 학습에는 오랜 시간이 걸리므로, 학습을 위한 에폭을 작게 하여 1회 평가에 걸리는 시간을 단축하는 것이 효과적이다.
  • 베이즈 최적화(bayesian optimization)이라는 최적화 방법도 존재한다.

프로세스

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