5장, 학습 관련 기술들
서론
이번 장에서는 딥러닝의 핵심 개념을 배운다.
- 가중치 매개변수의 최적값을 찾는 [최적화 방법]
- 가중치 매개변수의 [초깃값]
- 하이퍼파라미터의 설정 방법 등등
위의 내용들을 통해서 딥러닝의 효율과 정확도를 향상시킬 수 있다.
매개변수 갱신
신경망 학습(딥러닝)의 목적은 손실함수의 값을 최소화하는 매개변수를 찾는 것이다.
이러한 과정을 최적화(optimization) 라고 한다.
앞 장까지 우리는 최적의 매개변수를 찾는 기준으로
[가중치 매개변수에 대한 손실함수의 기울기]를 이용했다.
다시 말해서, 최적화 기법으로 SGD를 이용했다고 할 수 있다.
그러나 상황에 따라서 SGD보다 효율적인 방법도 있다.
또 다른 최적화 기법을 알아보자.
먼저, SGD(Stochastic Gradient Descent) 복습이다.
SGD는 미니 배치를 사용하여 기울기를 구하고,
기울기가 제시한 방향으로 일정거리(학습률)만큼 이동하며,
손실함수의 값을 최소화 하는 곳을 찾는다.
수식으로 표현하면 W <- W - η(∂L/∂W) 이다.
수식으로 매개변수를 갱신함을 표현했다.
W는 갱신할 가중치 매개변수이고,
(∂L/∂W)는 W에 대한 손실함수의 미분(기울기)이다.
η(eta)기호는 갱신하는 양을 의미하는데, 딥러닝에서는 학습률이다.#SGD, Stochastic Gradient Descent
#(확률적인 기울기 하강법)
#단순히 기울기가 제시한 방향으로 갱신한다.
class SGD:
def __init__(self, learning_rate=0.01):
self.learning_rate = learning_rate
def update(self, parameters, gradients):
for key in parameters.key():
parameters[key] -= self.learning_rate * gradients[key]
#parameters 와 gradients는 딕셔너리 변수
SGD의 단점
비등방성 함수(방향에 따라 성질이 바뀌는 함수, 여기서는 방향에 따라 기울기가 달라지는 함수를 의미한다.)에서는 최적화 갱신 경로가 비효율적이라는 점이다.
SGD가 지그재그로 탐색하는 모습을 볼 수 있는데,
그 근본적인 원인은 기울기가 제시하는 방향이 작아지는 방향은 맞지만,
실제 최솟값을 가리키지 않는 곳이 대부분이기 때문이다.
이러한 SGD의 단점을 개선한 세 방법을 소개한다.
- Momentum
- AdaGrad
- Adam
모멘텀 (Momentum)
모멘텀은 [운동량], [관성]을 뜻하는 물리용어이다.
수식으로는,
v <- αv - η(∂L/∂W) — ㄱ식
W <- W - v — ㄴ식
W는 갱신할 가중치 매개변수이고,
(∂L/∂W)는 W에 대한 손실함수의 미분(기울기)이다.
η(eta)기호는 갱신하는 양을 의미하는데, 딥러닝에서는 학습률이다.
v는 물리에서의 속도(velocity)이다.
αv는 물체가 아무런 힘을 받지 않을때, 서서히 하강시키는 역할을 한다.
(물리에서의 지면 마찰 또는 공기 저항에 해당)
ㄱ식은 기울기 방향으로 힘을 받아 물체가 가속되는 물리 법칙을 표현한 것이다.
ㄴ식에서 매개변수를 갱신한다.#공이 바닥을 구르는 듯한 움직임을 보여준다.
class Momentum:
def __init__(self, learning_rate=0.01, momentum=0.9):
self.learning_rate = learning_rate
self.momentum = momentum
self.v = None # v는 속도(velocity)
def update(self, parameters, gradients): # parameters 와 gradients는 딕셔너리 변수
if self.v is None:
self.v = {}
for key, val in parameters.items():
self.v[key] = np.zeros_like(val)
for key in parameters.key():
self.v[key] = (self.momentum * self.v[key]) - (self.learning_rate * gradients[key])
parameters[key] += self.v[key]
AdaGrad (Adaptive Gradient Algorithm)
딥러닝에서는 학습률 값이 중요하다. 이 값이 너무 작으면 시간이 너무 오래 걸리고, 너무 커도 발산하여 학습이 잘 이루어지지 않는다.
학습률을 정하는 효과적인 기술로 [학습률 감소(learning rate decay)]가 있다.
처음에는 큰 값으로 학습하다가 점차 줄여나가는 방식이다.
학습률을 점차 낮추는 가장 쉬운 방법은 매개변수 “전체”의 학습률 값을 일괄적으로 낮추는 것이다.
이런 아이디어를 발전시킨것이 AdaGrad다.
AdaGrad는 “각각의” 매개변수에 “맞춤형(적응형)” 값을 준다.
수식으로는,
h <- h + (∂L/∂W) ⊗ (∂L/∂W) — ㄱ식
W <- W + η1/√h(∂L/∂W) — ㄴ식
W는 갱신할 가중치 매개변수이고,
(∂L/∂W)는 W에 대한 손실함수의 미분(기울기)이다.
η(eta)기호는 갱신하는 양을 의미하는데, 딥러닝에서는 학습률이다.
⊗기호는 행렬의 원소별 곱셈을 의미한다.
h는 기존의 기울기를 제곱해서 계속 더해나가는 것이다.
그리고 ㄴ식에서 매개변수를 갱신할 때, 1/√h를 곱한다.
매개변수 원소 중 많이 갱신된 원소는 학습률이 그만큼 작아진다는 뜻이다.
(원소마다 학습률 감소가 다르게 적용된다는 것.)class AdaGrad:
def __init__(self, learning_rate=0.01):
self.learning_rate = learning_rate
self.h = None
def update(self, parameters, gradients): # parameters 와 gradients는 딕셔너리 변수
if self.h is None:
self.h = {}
for key, val in parameters.items():
self.h[key] = np.zeros_like(val)
for key in parameters.key():
self.h[key] += gradients[key] * gradients[key]
parameters[key] -= (self.learning_rate * gradients[key]) / (np.sqrt(self.h[key])+ 1e-7)
#1e-7을 더해서 0으로 나누는 일이 없도록 해준다.
AdaGrad 심화 // RMSProp
AdaGrad는 과거의 기울기를 제곱해서 계속 더해간다.
그래서 학습을 계속 할 수록 갱신 정도가 약해진다.
실제로 무한히 학습하면 갱신량이 0이 된다.
이 문제를 개선한 RMSProp(Root Mean Square propagation)이라는 방법이 있다.
RMSProp는 먼 과거의 기울기는 서서히 잊고 새로운 기울기를 크게 반영한다.
이것을 지수 이동 평균(Exponential Moving Average, EMA)라 하고,
과거 기울기의 반영 정도를 기하급수적으로 감소시킨다.
Adam (Adaptive Moment Estimation)
모멘텀과 AdaGrad를 융합해보면 어떨까? 라는 아이디어에서 시작되었다.
하이퍼파라미터의 편향보정이 진행된다는 점이 특징이다.
모멘텀과 비슷한 움직임을 보이지만, 공의 좌우 흔들림이 적다.
이는 각각의 매개변수에 갱신 정도를 적응형으로 조절했기 때문이다.
어떤 갱신 방법을 이용할 것인가?
“풀어야 할 문제가 무엇인가”에 따라 달라지고,
학습률 등 하이퍼파라미터를 어떻게 설정하느냐에 따라 달라진다.
여전히 SGD도 많이 쓰이며, 최근에는 Adam도 많이 쓰인다고 한다.
가중치의 초깃값
초깃값을 0으로 하면 어떨까?
결론부터 말하면 나쁜 생각이다. 정확히는 가중치를 균일한 값으로 설정해서는 안된다.
오차역전파에서 모든 가중치 값이 동일하게 갱신되기 때문이다.
가중치를 여러개 둔 의미가 사라지는 것이다.
이런 상황을 피하기 위해서는 초깃값을 [무작위]로 설정해야 한다.
은닉층의 활성화 함수의 출력 데이터(활성화 값) 분포
은닉층의 활성화 함수의 출력 데이터 분포를 보면 중요한 정보를 얻을 수 있다.
활성화 함수로 sigmoid 함수를 사용하는 5층 신경망에 무작위로 생성한 입력데이터를 넣으며 각 층에서의 활성화 값의 분포를 그리는 실험이다.
이 실험에서는,,
가중치의 분포를 바꿈에 따라서 활성화 값의 분포가 어떻게 변화하는지를 관찰하는 것이 핵심이다.
https://github.com/junsoofeb/Deep-Learning/blob/master/05_1_initial_value_of_weight.ipynb
히스토그램과 자세한 내용은 위의 링크를 참고!
가중치의 초깃값 [정리]
sigmoid, tanh 함수 등 S자 모양 곡선을 활성화 함수로 사용하는 경우는
Xavier 초깃값이 바람직하고,
ReLU 함수를 활성화 함수로 사용하는 경우에는 He 초깃값이 바람직 하다.
cf)
tanh 함수는 hyperbolic tangent(쌍곡 탄젠트) 함수이다.
sigmoid와 tanh 둘 다 S자 모양인데,
sigmoid는 (x, y) = (0, 0.5)에서 대칭이고,
tanh는 원점(0, 0)대칭이다.
활성화 함수용으로는 원점대칭인 함수가 적절하다고 한다.
***
배치 정규화 (Batch Normalization)
방금까지 가중치의 초깃값을 적절히 설정하면,
각 층의 활성화 값의 분포가 잘 퍼지는 것을 배웠다.
그렇다면, 각 층에서 활성화 값을 적당히 퍼뜨리도록 하는것은 어떨까?
이것이 [배치 정규화]의 아이디어 출발점이다.
배치 정규화의 장점
학습 속도가 향상된다.
초깃값에 크게 의존하지 않는다.
오버피팅을 억제한다.
이러한 장점들 덕분에 많이 쓰인다.
배치 정규화는 미니 배치를 단위로 정규화한다.
구체적으로는 데이터 분포가 평균이 0, 분산이 1이 되도록 한다.

미니 배치 B = {x1, x2, … , xm}라는 m개의 입력 데이터 집합에 대해
평균 M, 분산 σ²을 구한다.
x_hat은 정규화된 입력데이터이다.
배치 정규화 계층마다 정규화된 데이터에
고유한 확대와 이동 변환을 수행한다. (scale & shift)
γ(감마)가 확대, β가 이동을 의미한다.
γ = 1, β = 0부터 시작하며 학습하면서 적절한 값으로 조절된다.
(γ = 1는 1배 확대, β = 0는 0만큼 이동 –> 원본 그대로 라는 의미이다.)
배치 정규화를 활성화 함수 앞 또는 뒤에 삽입하여 데이터 분포가 덜 치우치도록(퍼지도록) 할 수 있다.
오버피팅
오버피팅 : 신경망이 훈련 데이터에만 지나치게 적응되어 훈련 데이터 이외의 데이터에는 제대로 대응하지 못하는 상태.
오버피팅은 주로 다음의 경우에 발생한다.
- 매개변수가 많고, 표현력이 높은 모델
- 훈련 데이터가 적을 때
가중치 감소 (Weight decay)
오버피팅을 억제하기 위해서 예전부터 많이 사용해온 방법 중에 [가중치 감소]가 있다.
학습 과정에서 큰 가중치에 대해서는 큰 페널티를 부과하여 오버피팅을 억제하는 것이다.
원래 오버피팅은 가중치 매개변수의 값이 커서 발생하는 경우가 많기 때문이다.
드롭 아웃(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
[이 코드에서 핵심]
훈련 때에는 순전파마다 self.mask에 삭제할 뉴런을 False로 표시한다는 점이다.
self.mask는 x와 같은 shape의 배열을 무작위 값으로 생성하고, 그 값이 dropout_ratio보다 큰 원소만 True로 설정한다.
역전파의 동작은 렐루와 같다.
순전파 때의 신호를 통과시킨 뉴런은 역전파 때도 통과시키고,
순전파 때 신호가 통과시키지 않은 뉴런은 역전파 때도 통과시키지 않는다.
앙상블 학습과 드롭아웃
앙상블 학습(ensemble learning)은 기계학습에서 애용된다.
개별적으로 학습시킨 여러 모델의 출력을 평균내어 추론하는 방식이다.
앙상블 학습은 드롭아웃과 밀접하다.
드롭아웃이 학습 때 뉴런을 삭제하는 행위를 매번 다른 모델을 학습시키는 것으로 볼 수 있고,
추론 때 뉴런의 출력에 삭제한 비율을 곱함으로써 앙상블 학습에서 평균을 내는 것의 효과를 낸다.
드롭아웃은 앙상블 학습과 같은 효과를 1개의 네트워크로 구현했다고 볼 수 있다.
적절한 하이퍼파라미터 값 찾기
신경망에는 하이퍼파라미터가 다수 등장한다.
각 층의 뉴런 수, 배치 사이즈, 매개변수 갱신 시의 학습률과 가중치 감소 등이 있다.
이번 절에서는 하이퍼파라미터의 값을 최대한 효율적으로 찾는 방법을 배운다.
검증 데이터 (vaildation data)
지금까지 데이터 셋을 훈련 데이터와 시험 데이터 2가지로 분리하여 이용했다.
훈련 데이터는 매개변수(가중치와 편향)의 학습용도이며,
시험 데이터는 마지막에 성능 평가 용도였다.
앞으로 하이퍼파라미터를 다양한 값으로 설정하고 검증할텐데,
시험 데이터를 사용하지 않도록 주의해야한다.
시험 데이터를 사용하면 하이퍼파라미터 값이 시험 데이터에 오버피팅될 수 있기 때문이다.
그렇기 때문에 하이퍼파라미터 조정 전용 데이터가 필요한데,
일반적으로 검증 데이터라고 한다.
검증데이터는 하이퍼파라미터 성능 평가 용도이다.
하이퍼파라미터 최적화
하이퍼파라미터를 최적화할 때 핵심은,
하이퍼파라미터의 [최적값]이 존재하는 범위를 조금씩 줄여간다는 것이다.
우선 하이퍼파라미터 값의 대략적인 범위(10의 계승 단위로)를 정하고,
그 범위에서 무작위로 샘플링하여 정확도를 평가한다.
이 과정을 반복하며 하이퍼파라미터의 최적값의 범위를 좁혀간다.
무작위로 샘플링하는 이유
신경망 하이퍼파라미터 최적화에서는 규칙적인 탐색보다 무작위 샘플링이 더 좋은 결과를 낸다고 알려져있다.
정확도에 미치는 영향력이 하이퍼파라미터마다 다르기 때문이다.
정리하면,
하이퍼파라미터 값의 범위를 설정한다.
설정된 범위에서 하이퍼파라미터 값을 무작위로 추출한다.
2단계 에서 샘플링한 값을 사용하여 학습하고, 검증 데이터로 정확도를 평가한다.
2단계와 3단계를 반복하며, 그 정확도를 보고 하이퍼파라미터 값의 범위를 좁힌다.
어느정도 좁혀지면 그 범위에서 값을 하나 골라낸다.
cf) 조금 더 세련된 방법으로 [베이즈 최적화]가 있다.