[딥러닝 기초] 메개변수 갱신
매개변수 갱신
- 신경망 학습의 목적은 손실 함수의 값을 가능한 낮추는 매개변수를 찾는 것 -> 매개변수의 최적값을 찾는 문제 -> 이러한 문제를 푸는 것을 최적화 라고 한다.
- 지금까지 최적의 매개변수 값을 찾는 단서로 매개변수의 기울기(미분)을 이용했다.
- 매개변수의 기울기를 구해, 기울어진 방향으로 배개변수 값을 갱신하는 일을 몇 번이고 반복해서 점점 최적의 값에 다가갔다.
- 이것이 확률적 경사 하강법(SGD) 이란 단순한 방법이다.
확률적 경사 하강법(SGD)
- W는 갱신할 가중치 매개변수고 은 W에 대한 손실 함수의 기울기다.
- 은 학습률을 의미하는데 실제로 0.01 이나 0.001 과 같은 값을 미리 정해서 사용한다.
- 또, <- 는 우변의 값으로 좌변의 값을 갱신한다는 뜻이다.
- 기울어진 방향으로 일정 거리만 가겠다는 단순한 방법
코드 구현
class SGD:
def __init__(self, lr=0.01):
self,lr = Ir
def update(self, params, grads):
for key in params.keys():
params[key] -= self.lr * grads[key]
- 초기화 때 받는 인수인 lr은 learning rate (학습률)를 뜻한다.
- 이 학습률을 인스턴스 변수 로 유지한다.
- update (params, grads) 메서드는 SGD 과정에서 반복해서 호출된다. 인수인 params와 grads는 (지금까지의 신경망 구현과 마찬가지로) 딕셔너리 변수다.
- params
['Wl']
, grads['Wl']
등과 같이 각각 가중치 매개변수와 기울기를 저장하고 있다. - SGD 클래스를 사용하면 신경망 매개변수의 진행을 다음과 같이 수행할 수 있다.
책에 나온 예시코드
network = TwoLayerNet(...)
optimizer = SGD()
for i in range(10000):
x_batdiz t_batch = get_mini_batch(...) # 미니배치
grads = network.gradient(x_batch, t_batch)
params = network.params optimizer.update(params, grads)
- 최적화를 담당하는 캘래스를 분리해 구현하면 기능을 모듈화 하기 좋다.
- optimizer = SGD() -> optimizer = Momentum() 으로 변경하는 식
- 대부분의 딥러닝 프레임 워크는 다영한 최적화 기법을 구현해 제공하며 원하는 기법으로 쉽게 바꿀 수 있는 구조로 되어 있다.
SGD의 단점
- 해당 함수의 최솟값을 구하는 문제에서 이 함수를 그래프와 등고선으로 나타내면 아래와 같다.
- 함수의 기울기를 그리면 아래와 같다.
- 기울기는 Y축 방향은 크고 X축 방향은 작다는 것이 특징이다.
- Y 축 방향은 가파른데 X 축 방향은 완만하다.
- 최솟값이 되는 장소는 (x,y) = (0,0) 하지만 밑의 그림에서 보여지는 기울기 대부분은 (0, 0) 방향을 가리키지 않는다.
밑에서 까만 선은 함수의 등고선을 표현한 것이고 빨간 점은 최솟값을 찾아 나가는 경로를 나타낸 것
- 이 그래프는 50번 갱신했는데 그 이하로는 최솟값 (0,0)에 제대로 다가가지 못한다.
- SGD의 단점은 비등상성 함수 (방향에 따라 성질, 즉 여기에서는 기울기가 달라지는 함수) 에서 탐색 경로가 비료율적이다.
- 이럴 떄는 SGD 같이 무작정 기울어진 방향으로 진행하는 단순한 방식보다는 더 좋은 방법이 있다.
- 모멘텀, AdaGrad, Adam
모멘텀
- W 는 갱신할 가중치 매개변수, 는 W에 대한 손실 함수의 기울기 는 학습률이다.
- V라는 변수가 새로 나오는데 속도에 해당된다.
- 기울기 방향으로 힘을 받아 물체가 가속된다는 개념
- 항은 물체가 아무런 힘을 받지 않을 때 서서히 하강시키는 역할을 한다. (0.9 등의 값으로 설정한다고 함)
- 물리로 치면 공기나 지면 마찰에 해당.
코드 구현
class Momentum:
def __init__(self, lr=0.01z momentum=0.9):
self.lr = Ir
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는 물체의 속도다.
- V는 초기화 때는 아무 값도 담지 않고, 대신 update()가 처음 호출될 때 매개변수와 같은 구조의 데이터를 딕셔너리 변수로 저장한다.
- 나머지는 위와 동일하다.
- SGD와 비교하면 지그재그 정도가 덜하다.
- 이는 x 축의 힘은 아주 작지만 방향은 변하지 않아서 한 방향으로 일정하게 가속하기 때문이다.
- 거꾸로 y축의 힘은 크지만 위아래로 번갈아 받아서 상충하여 y축의 방향의 속도는 안정적이지 않다.
- 전체적으로 SGD 보다 x 축 방향으로 빠르게 다가가 지그재그 움직임이 줄어든다.
AdaGrad
- 신경망 학습에서는 학습률 값이 중요하다. 이 값이 너무 작으면 학습 시간이 너무 길어지고, 반대로 너무 크면 발산하여 학습이 제대로 이뤄지지 않는다.
- 이 학습률을 정하는 효과적 기술로 학습률 감소 가 있다.
- 이는 학습을 진행하면서 학습률을 점차 줄여가는 방법이다.
- 처음에는 크게 학습하다가 조금씩 작게 학습한다는 얘기로, 실제 신경망 학습에서 자주 쓰인다.
- 학습률을 서서히 낮추는 가장 간단한 방법은 매개변수 ‘전체’의 학습률 값을 일괄적으로 낮추는 것이다.
- 이를 더욱 발전시킨 것이 AdaGrad
- AdaGrad 는 각각의 매개변수에 맞춤형 값을 만들어준다.
- 적응적으로 학습률을 조정하면서 학습을 진행한다.
- 마찬가지로 W는 갱신할 가중치 매개변수
- 는 W에 대한 손실함수의 기울기
- 는 학습률을 뜻 한다.
- 여기서 h 라는 변수는 기존 기울기 값을 제곱하여 계속 더해준다.
- 는 행렬의 원소별 곱셈을 의미
- 그리고 매개변수를 갱신할 때 을 곱해 학습률을 조정한다.
- 매개변수의 원소 중에서 많이 움직인 (크게 갱신된) 원소는 학습률이 낮아진다는 뜻인데,
- 다시 말해 학습률 감소가 매개변수의 원소마다 다르게 적용됨을 뜻한다.
- AdaGrad는 과거의 기울기를 제곱하여 계속 더해간다. 그래서 학습을 진행할수록 갱신 강도가 약해진다
- 실제로 무한히 계속 학습한다면 어느 순간 갱신량이 0이 되어 전혀 갱신되지 않는다.
- 이 문제 를 개선한 기법으로서 RMSPropn이라는 방법이 있다.
- RMSProp은 과거의 모든 기울기를 균일하게 더해가는 것이 아니라, 먼 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영한다. 이를 지수이동평균 (Exponential Moving Average. EMA )이라 하여, 과거 기울기의 반영 규모를 기하급수적으로 감소시킨다.
코드구현
class AdaGrad:
def __init__(self, lr=0.01):
self.lr = Ir
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.000001) 은
self.h[key]
에 0이 담겨 있더라도 0으로 나누는 사태를 사전에 방지해준다. - 대부분 딥러닝 프레임 워크에서도 이 값도 인수로 설정할 수 있다고 한다.
- 그림에서 보면 최솟값을 향해 효율적으로 움직이는 것을 알 수 있다.
- y 축 방향은 기울기가 커서 처음에는 크게 움직이지만, 그 큰 움직임에 비례해 갱신 정도도 큰 폭으로 작아지도록 조정된다.
- 그래서 y축 방향으로 갱신 강도가 빠르게 약해지고, 지그재그 움직임이 줄어든다.
Adam
- 위에서 소개한 모멘텀과 AdaGrad를 융합한 기법이다.
- 매개변수를 효율적으로 탐색하면서 하이퍼파라미터의 ‘편향 보정’이 진행된다는 점도 Adam의 특징이다.
class Adam:
"""Adam (http://arxiv.org/abs/1412.6980v8)"""
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.iter = 0
self.m = None
self.v = None
def update(self, params, grads):
if self.m is None:
self.m, self.v = {}, {}
for key, val in params.items():
self.m[key] = np.zeros_like(val)
self.v[key] = np.zeros_like(val)
self.iter += 1
lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)
for key in params.keys():
#self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
#self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
#unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
#unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
#params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)
논문 링크 : 논문링크
수학기호 : 수학기호 E 는 무엇?
- 모멘텀과 비슷하지만 모멘텀 때보다 공의 좌우 흔들림이 적다.
- 이는 학습의 갱신 강도를 적응적으로 조정해서 얻는 혜택이다.
- Adam 은 하이퍼 파라미터로 3개를 설정한다.
- 하나는 지금까지의 학습률 (논문에서는 로 나옴)
- 나머지 두개는 일차 모멘텀용 계수 , 이차 모멘텀용 계수 .
- 논문에 따르면 기본 설정 값은 -> 0.9
- -> 0.999
- 이 값이면 대부분의 경우에서 좋은 결과를 얻을 수 있다고 한다.
어느 갱신 방법을 이용해야 될까?
- 풀어야 할 문제가 무엇이냐에 따라 달라진다.
- (학습률 등의) 하이퍼 파라미터를 어떻게 설정하느냐에 따라서도 결과가 바뀐다.
- 각자의 장단이 있어 잘 푸는 문제와 그렇지 않은 문제가 있다.
데이터셋으로 본 갱신 방법 비교
- loss 는 손실함수의 값 iterations는 학습의 반복 횟수
- 각 층이 100개인 뉴런으로 구성된 5층 신경망에서 ReLU를 활성화 함수로 사용해 측정한 결과다.
- 학습 결과에서는 SGD의 학습 진도가 가장 느리다.
- 여기서 주의할 점은 하이퍼파라미터인 학습률과 신경망의 구조 (층 깊이 등)에 따라 결과가 달라진다는 점이다.
- 다만 일반적으로 SGD 보다 다른 세 기법이 빠르게 학습하고, 때로는 최종 정확도도 높게 나타난다.
손실함수 개념 복습
- 딥러닝에서 모델을 학습할 때 손실 함수를 사용해서 모델의 성능을 평가한다.
- 손실 함수는 모델의 출력과 실제 값 사이의 차이를 계산하는 함수며 이 차이가 작을수록 모델의 예측이 실제 값과 일치한다는 것을 의미한다.
- 따라서 손실 함수의 값이 낮을수록 모델의 성능이 높아진다고 할 수 있다.
- 이는 모델이 더 정확하게 예측하고 있기 때문
- 예를 들어, 분류 문제에서는 정확도가 높을수록 모델이 데이터를 더 정확하게 분류한다는 것을 의미
- 회귀 문제에서는 평균 제곱 오차(MSE)와 같은 손실 함수의 값이 작을수록 모델이 실제 값과 더 가까워진다는 것을 의미
- 따라서 딥러닝에서는 손실 함수의 값을 최소화하는 방향으로 모델을 학습시키는 것이 중요하다.
- 최적화 알고리즘을 사용하여 모델의 파라미터를 업데이트하고 손실 함수의 값을 줄이는 방향으로 모델을 학습해야한다.
- 하이퍼 파라미터와 문제에 따라 결국 결과 값이 달라지므로 사람의 손을 타긴 탄다.
- 머신러닝보다 좀 덜 탈뿐이다.
출처 : 밑바닥부터 시작하는 딥러닝
댓글남기기