deep-learning (5)

MinGeo·2022년 8월 22일
0

deep-learning

목록 보기
5/6

오늘의 주제

오늘은 '밑바닥부터 시작하는 딥러닝'의 chapter 6 '학습 관련 기술들'에 대해서 다뤄볼 예정이다.

매개변수 갱신

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

우리가 직전 장까지 최적의 매개변수 값을 찾는 단서로 매개변수의 기울기, 미분을 이용했다. 이 기울기를 구해 기울어진 방향으로 매개변수 값을 갱신하는 일을 반복하여 최적의 값에 다가가는 것을 확률적 경사 하강법(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처럼 기울기 방향으로 진행하는 단순한 방식 보다 다른 방식이 적합하다.

이번에는 이런 단점을 개선한 방법인 모멘텀, AdaGrad, Adam이렇게 3가지 방법에 대해서 알아보자

모멘텀

모멘텀은 운동량을 뜻하는 단어이다. 식은 다음과 같다.

모멘텀은 물리와 관계가 있기 때문에, 위 식에 존재하는 알파는 물리에서의 지면 마찰이나 공기 저항에 해당한다. 그러므로 av는 물체가 아무런 힘을 받지 않을 때 서서히 하강시키는 역할을 한다.

그림을 보면 모멘텀에 대한 이해가 쉬울것이다.

파이썬 코드는 다음과 같다.

class Momentum:
	def __init__(self, lr=0.01, momentum=0.9):
    	self.lr = lr
        self.momentum = momentum
        self.v = None
    def update(self, params, grads):
    	if self.v is None:
        	self.v = {}
            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.lr*grads[key]
            params[key] += self.v[key]

v가 물체의 속도이다. 초기화 때에는 아무 값도 담지 않고, update 함수가 처음 호출될 때 매개변수와 같은 구조의 데이터를 딕셔너리 변수로 저장한다.

모멘텀을 사용한 최적화 그래프이다.

SGD에 비하면 지그재그 정도가 덜하다. 그 이휴는 x축의 힘은 작지만 방향은 변하기 않은 채로 일정하게 가속하기 때문이다. 또한 y축의 힘은 강하지만 위아래로 번갈아 받아 상충하여 y축 방향의 속도는 안정적이지 않다. SGD보다 x축 방향으로 빠르게 다가가 지그재그 움직임이 줄어든다.

AdaGrad

신경망 학습에서는 학습률 값에 따라 학습의 성공 여부가 갈린다. 학습률 값이 너무 크면 발산하여 학습이 제대로 이뤄지지 않고, 너무 작으면 학습 시간이 너무 길어진다.
이 학습률을 정하는 기술로 우리는 학습률 감소를 사용한다. 이 방법은 학습일 진행하면서 학습률을 점차 줄여가는 방법이다.

매개변수 전체의 학습률 값을 일괄적으로 낮추는 것이 더 간단한 방법이지만, 이를 발전시킨 방법이 AdaGrad이다. AdaGrad는 각각의 매개변수에 맞춤형 값을 만들어준다.

AdaGrad는 개별 매개변수에 적응적으로 학습률을 조정하면서 학습을 진행한다. 수식은 다음과 같다.

위 식을 보면 매개변수의 원소에서 크게 갱신된 원소는 학습률이 낮아진다는 것을 알 수 있다. 그러므로 학습률 감소는 매개변수의 원소마다 다르게 적용된다는 것을 뜻한다.

AdaGrad는 과거의 기울기를 제곱하여 계속 더해가기 때문에 학습을 진행할수록 갱신 강도가 약해진다는 특징이 있다.

파이썬 코드로 구현해보자

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이라는 작은 값은 0을 0으로 나누는 상황을 막아주기 위한 것이다.

AdaGrad의 최적화 경로를 한번 보도록 하자

위 그림을 보면 최솟값을 향해 효율적으로 움직이는 것을 알 수 있다. y축 방향은 기울기가 커서 처음에는 크게 움직이지만, 그 큰 움직임에 비례해 갱신 정도도 큰 폭으로 작아지도록 조정된다. 그래서 y축 방향으로 갱신 강도가 빠르게 약해지고, 지그재그 움직임이 줄어든다.

Adam

모멘텀과 AdaGrad 두 기법을 융합한 형태의 새로운 기법이다. 두 방법의 이점을 조합하고, 하이퍼파라미터의 편향 보정이 진행되는 점이 특징이다.

Adam의 최적화 경로는 다음과 같다.

모멘텀과 패턴이 비슷하지만, 좌우 흔들림이 적다. 이는 학습의 갱신 강도를 적응적으로 조정했기에 나오는 모습이다.

가중치의 초깃값

어비피팅을 억제해 범용 성능을 높이는 가중치 감소 기법에 대해서 알아보자. 가중치 감소는 가중치 매개변수의 값이 작아지도록 학습하는 방법이다. 가중치 값을 작게 하여 오버피팅이 일어나지 않게 하는 것이다.

하지만 가중체 초깃값을 0으로 해서는 안된다. 왜냐면 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다.

2층 신경망에서 첫 번째, 두 번째 층의 가중치가 0이라고 가정해보자. 그럼 순전파 때는 입력층의 가중치가 모두 0이기 때문에 두 번째 층의 뉴런에 모두 같은 값이 전달된다. 두 번째 층의 모든 뉴런에 같은 값이 입력된다는 것은 역전파 때 두 번째 층의 가중치가 모두 똑같이 갱신된다는 걸 의미한다. 가중치들은 같은 초깃값에서 시작하고 갱신을 거쳐도 여전히 같은 값을 유지한다. 이는 가중치를 여러 개 갖는 의미를 사라지게 한다.

은닉층의 활성화값 분포

은닉층의 활성화 값의 분포를 관찰하면 중요한 정보를 얻을 수 있다.
층이 5개가 있고, 각 층의 뉴런은 100개라고 가정하고 입력 데이터로서 1000개의 데이터를 정규분포로 무작위로 생성하여 5층 신경망에 흘린다. 활성화함수로는 시그모이드 함수를 활용했고, 표준편차가 1인 정규분포를 이용해 활성화 값들의 분포를 히스토그램으로 나타냈다.

import numpy as np
import matplotlib.pyplot as plt

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

히스토그램은 다음과 같다.

각 층의 활성화 값들이 0과 1에 치우쳐 분포되어 있다. 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다. 이런 현상을 기울기 소실 이라고 한다.

이번에는 가중치의 표준편차를 0.01로 바꿔 다시 진행하였다.
히스토그램을 다시 보자

이번에는 0.5 부근에 집중된 것을 알 수가 있다. 기울기 소실 문제는 일어나지 않지만, 활성화값들이 치우쳤다는 것은 표현력 관점에서는 큰 문제가 있다. 다수의 뉴런이 거의 같은 값을 출력하고 있으니 뉴런을 여러 개 둔 의미가 없어진다. 그래서 활성화 값들이 치우치면 표현력을 제한한다는 문제가 발생한다.

이런 문제를 해결하기 위해 Xavier 초깃값을 사용해보자. 이 초깃값은 앞 계층의 노드가 n개라면 표준편차가 1/root(n)인 분포를 사용하는 것이다.

Xavier 초깃값을 사용하면 앞 층에 노드가 많을수록 대상 노드의 초깃값으로 설정하는 가중치가 좁게 퍼진다.

Xavier 초깃값을 사용한 히스토그램을 보자

앞의 방식보다 넓게 분포되는 것을 알 수 있다.

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

ReLU를 이용할 때는 ReLU에 특화된 초깃값을 이용하라고 권장한다. 이 초깃값을 He 초깃값이라고 한다. 앞 계층의 노드가 n개일 때, 표준편차가 root(2/n)인 정규분포를 사용한다.

배치 정규화

가중치의 초깃값을 적절히 설정하면 각 층의 활성화값 분포가 적당히 퍼지면서 학습이 원활하게 수행된다. 배치 정규화를 이용하면 각 층이 활성화를 적당히 퍼뜨리도록 강제할 수 있다.

배치 정규화의 장점은 다음과 같다.

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

배치 정규화 계층을 신경망에 삽입하여 사용한다.

배치 정규화는 학습 시 미니배치를 단위로 정규화한다. 구체적으로는 데이터 분포가 평균이 0, 분산이 1이 되도록 정규화 한다. 수식은 다음과 같다

위부터 평균, 분산, 마지막은 평균 0, 분산 1인 데이터로 변환한 것이다. 이 처리를 활성화 함수의 앞 혹은 뒤에 삽입함으로써 데이터 분포가 덜 치우치게 할 수 있다.

배치 정규화 계층마다 이 정규화된 데이터에 고유한 확대와 이동 변환을 수행한다. 수식은 다음과 같다.

y처럼 생긴게 확대, b처럼 생긴게 이동을 담당한다. 시작은 확대 1, 이동 0부터 시작하여 학습하면서 적합한 값으로 조정한다.

위 배치 정규화의 알고리즘이 신경망에서 순전파 때 적용된다.

배치 정규화의 효과

이 책에 존재하는 MNIST 데이터셋을 사용하여 배치 정규화 계층 유무에 따른 학습 진도를 비교해보자.

배치 정규화가 학습 진도를 빠르게 한 것을 알 수 있다.

오버피팅

기계학습에서는 오버피팅이 문제가 되는 경우가 많다. 오버피팅이란 신경망이 훈련 데이터에만 지나치게 적응되어 그 외의 데이터에는 제대로 대응하지 못하는 상태를 말한다. 기계학습은 범용 성능을 지향하여 훈련데이터에는 포함되지 않는, 아직 보지 못한 데이터가 주어져도 바르게 식별해내는 모델이 바람직하다.

오버피팅은 주로 다음의 두 경우에 발생한다.

매개변수가 많고 표현력이 높은 모델
훈련 데이터가 적음

일부러 두가지 조건을 충족하여 오버피팅을 발생시켜 보았다. 그리고 훈련 데이터와 시험 데이터를 사용하여 각 데이터에 대한 그래프를 확인해 보았다.

보면 정확도에 큰 차이를 보이는 것을 보여준다.

가중치 감소

위 상황처럼 오버피팅을 막기 위해서 가중치 감소라는 방법을 사용한다. 가중치 감소는 학습 과정에서 큰 가중치에 대해서는 그에 상응하는 큰 페널티를 부과하여 오버피팅을 억제하는 방법이다. 오버피팅의 주 원인이 가중치 매개변수의 값이 커서 발생하는 것이기 때문이다.

신경망 학습의 목적은 손실 함수의 값을 줄이는 것이다. 가중치의 제곱 노름(L2 노름)을 손실 함수에 더하면 가중치가 커지는 것을 억제할 수 있다. 가중치 감소는 모든 가중치 각각의 손실 함수에 1/2(람다W^2)를 더한다. 따라서 가중치의 기울기를 구하는 계산에서는 그동안의 오차역전파법에 따른 결과에 정규화 항을 미분한 람다W를 더한다.

예를 들어보자. 위 식에서 람다 값에 0.1을 넣어서 가중치 감소를 적용한다. 결과는 아래 그래프와 같다.

훈련 데이터에 대한 정확도와 시험 데이터에 대한 정확도에는 여전히 차이가 있지만, 가중치 감소를 이용하지 않은 것 과 비교하면 차이가 줄었다.

드롭아웃

앞에서는 가중치 감소를 이용해 오버피팅을 억제하는 방법으로 사용하였다. 하지만 신경망 모델이 복잡해지면 가중치 감소만으로는 대응하기 어려워진다. 이럴때 사용하는 기법이 드롭아웃이다.

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

드롭아웃을 코드로 구현해보자

class Dropout:
    def __init__(self, dropout_ratio = 0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None
 
    def forward(self, x, train_fig=True):
        if train_fig:
            self.mask - np.ranmdom.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

위 구현한 코드를 적용하여 앞의 데이터셋에 적용해 보았다.

드롭아웃을 이용하면 위 처럼 표현력을 높이면서도 오버피팅을 억제할 수 있다.

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

여기서 말하는 하이퍼파라미터는 뉴런 수, 배치 크기, 매개변수 갱신 시의 학습률과 가중치 감소를 의미한다. 이 값이 적절히 설정되어야 모델의 성능이 준수하게 나올 수 있다.

검증 데이터

지금까지는 데이터셋을 훈련 데이터와 시험 데이터라는 두 가지로 분리하여 사용하였다. 하지만 하이퍼파라미터의 성능을 평가할 때는 시험 데이터를 사용해서는 안된다. 왜냐면 시험 데이터를 사용하여 하이퍼파라미터를 조정하면 하이퍼파라미터 값이 시험 데이터에 오버피팅 되기 때문이다. 그래서 하이퍼파라미터를 조정할 때는 전용 확인 데이터가 필요하다. 이를 우리는 검증 데이터 라고 부른다.

훈련 데이터 : 매개변수 학습
검증 데이터 : 하이퍼파라미터 성능 평가
시험 데이터 : 신경망의 범용 성능 평가

하이퍼파라미터 최적화

하이퍼파라미터 최적화 과정의 핵심은 하이퍼파라미터의 최적 값이 존재하는 범위를 조금 씩 줄여간다는 것이다. 대략적인 범위를 설정하고, 그 범위에서 무작위로 하이퍼파라미터 값을 골라낸 후, 그 값으로 정확도를 평가한다. 이 작업을 여러 번 반복하며 하이퍼파라미터의 최적 값의 범위를 좁혀가는 것이다.

하이퍼파라미터를 최적화할때는 딥러닝 학습에는 오랜시간이 걸린다는 점을 인지해야 한다. 그러므로 학습을 위한 에폭을 작게 하여, 1회 평가에 걸리는 시간을 단축해야 한다.

과정은 다음과 같다.

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

마무리

오늘은 매개변수 갱신 방법, 가중치 초깃값, 배치 정규화, 오버피팅 억제 정규화 기술, 하이퍼파라미터 값 탐색에 대해서 알아보았다. 다음에는 7장 합성곱 신경망(CNN)에 대해서 알아볼 것이다.

0개의 댓글