사진, 내용, 코드는 책을 참고한 것입니다. 코드는 오픈소스로 저저의 깃허브에 모두에게 공개되어있습니다.
오픈된 코드중 5 chapter를 재활용했습니다
기존에 다뤘더 MLP를 이미지 처리에 적용시킨 것 외에는 다른 내용이 없기때문에 블로그에 chapter5를 생략했습니다.
본 페이지는 chapter6 입니다.
딥러닝에서의 복합 출력의 학습법
복합 출력이란 두 가지 차원에서의 분류를 한꺼번에 수행하는것
- 예: 이미지의 도메인과 품목이 무엇인지 동시 판별
차원 축소로 순서쌍으로 만드는 방법도 있으나 출력이 과도하게 커지고, 각각의 특성 또한 포착하기 어려워짐
딥러닝에서는 같은 퍼셉트론이라도 학습과정에 따라 역할이 달라짐
동일한 구조의 신경망을 다른용도로 이용하기 위해서는 후처리 과정에서의 처리 방법만 변경하면 됨
후처리 과정의 순전파: 손실함수 계산 | 역전파: 손실 기울기 계산
신경망은 기본적으로 알맞은 크기의 출력 벡터를 생성하면 됨(예: 도메인 3 + 품목판별 31 = 34)
하나의 신경망 출력을 복합 출력으로 처리하는 대신 필요한 출력별로 별도의 신경망을 구성하는 것은 출력 계층만 따졌을때는 별 차이가 없지만, 은닉 계층을 따졌을 때는 커다란 차이가 생김
하나의 은닉계층은 모든 출력 계층에 영향을 주고 역전파 피드백을 받음
출력상의 내용은 서로 연관되어 있는 경우가 많고 공통특성이 있을 수 있음
각각의 출력별로 은닉계층을 하나 하나 학습하는 것보다 한번에 공통특성을 포착할 수 있게 학습 시키는게 계산량 절감 측면에서 효율적임
정답:y1,y2−>output1,output2−>Loss=Loss1+Loss2
L을 줄이는 것은 L_1, L_2를 함께 줄이는 것과 같다고 볼 수 있음
여기서 각자 다른 영역은 편미분과 같은 연산에서 서로 상수로 취급됨 : output_1의 손실기울기는 y_1의 후처리를 통해 진행하는데 이 때 y_2는 상수로 취급
Adam 알고리즘 (Adaptive momenteum)
파라미터에 적용되는 실질적인 학습률을 개별 파라미터 별로 동적으로 조절해 경사하강법의 동작을 보완하고 학습 품질을 높여주는 방법
경사하강법은 손실기울기와 학습률을 곱한 값을 파라미터에서 뺴주는 방식으로 학습
아담 알고리즘은 모멘텀 개념이 추가되어 처리과정을 보완함
모멘텀 정보와 2차 모멘텀 정보는 신경망 차원에서 관리되는 것이 아닌 개별 파라미터 수준에서 따로 계산되고 관리됨, 즉 파라미터 하나 당 모멘텀 정보와 2차 모멘텀 정보가 따라붙어 메모리 소비가 세배가 늘어남
딥러닝 옵티마이저 정리
Gradient Descent
Batch Gradient Descent
θt+1=θt−η∇θJ(θ)J=∑i=1Nerrori
θ를 인수로 갖는 목적함수 J(θ) 를 최소화 시키는 방법
이렇다면 그냥 미분 계수가 0인 지점을 찾으면 된다고 볼 수 있지만
실제 함수는 닫힌 형태가 아니고 복잡한 형태라서 그 근을 계산하기 어렵고
미분계수를 컴퓨터로 구현하는 것보다 gradient descent를 컴퓨터로 구현하는게 편함
∇ 는 나블라라고 읽으며 벡터의 미분을 의미
η 에타는 learning rate로 gradient 반대 방향으로 얼마나 업데이트 할 것인지 결정함, 너무 크면 minimum을 지나쳐 발산할수도 있고, 너무 작으면 수렴 속도를 늦출 수 있음
기본적인 Gradient Descent 는 Batch Gradient Descent인데 이는 한번의 업데이트를 위해 모든 데이터를 계산에 포함해야하기 때문에 속도가 느리다는 단점이 있음
Stochastic Gradient Descent (SGD)
한 번의 파라미터 업데이트를 위해 하나의 훈련 데이터를 사용하는 것이 특징 θt+1=θt−η∇θJ(θ;xi,yi)
그만큼 빠르지만, 매 업데이트마다 들쭉날쭉한 gradient로 파라미터를 업데이트 함
Mini-batch Gradient Descent
가장 흔하게 딥러닝에서 사용하는 방법
batch_size에 설정된 mini batch 데이터마다 손실함수를 만들어 gradient를 계산함
Batch Gradient보다 업데이트 속도가 빠르고 gradient 크기도 들쭉날쭉하지 않음
문제점
ml/dl 성능은 learning rate에 영향을 많이 받는데 SGD는 한번 정해진 learning rate를 학습상황에 따라 조정해주는 기능을 가지고 있지 않음
코딩에서는 decay를 통해 줄여줄 수 있지만 특정 에포크마다 learning rate를 조정해줄만한 방법이 필요하기도 함
모든 파라미터에 동일한 learning rate를 적용함
Saddle Point(안장점) 혹은 Local Minimum에서 빠져나오기 힘듬
Gradient Descent Optimization Algorithms
Momentum
말그대로 관성을 의미함, 직관을 그대로 반영한 방법
시기가 가까울수록 gradient의 영향력이 높으며 멀수록 γ배씩 감소시킴 vt=γvt−1+η∇θtJ(θt)θt+1=θt−vt
점화식의 원리로 시간이 작을수록 계속해서 γ가 곱해지는 것을 알 수 있음
보통 γ는 0.9를 사용함
이 개념을 통해 local minimum을 탈출할 수 있는 수단을 마련함
모멘텀은 지수 가중평균을 베이스로 하고 있음
vt=βvt−1+(1−β)θt
여기서 1−β를 1에 근사하고 θ에 그라디언트를 대입시키면 모멘텀임
vt(biascorrection)=(1−βt)vt: Adam 파트에서 bias-correct(바이어스 보정)을 사용하는데 이 또한 지수 가중평균에서 나온 개념임
Nestrov Accelerated Gradient (NAG)
앞을 미리 보고 현재의 관성을 조절하여 업데이트 크기를 바꾸는 방식
기존의 모멘텀
η∇θtJ(θt): 현재 parameter에서 gradient를 계산하고
vt=γvt−1+η∇θtJ(θt): 모멘텀을 구하고
θt+1=θt−vt: 모멘텀만큼 업데이트를 해줌
NAG
η∇θtJ(θt−vt): 현재 parameter로부터 vt 만큼 떨어진 위치의 gradient를 계산
η∇θtJ(θt−γvt−1): 현재의 gradient를 구하기 전까지는 현재의 모멘텀을 알 수 없기 때문에 vt대신 γvt−1 사용
vt=γvt−1+η∇θtJ(θt−γvt−1):다음으로 현재 모멘텀을 계산해줌
θt+1=θt−vt: 모멘텀만큼 업데이트를 해줌
이는 SGD가 관성에 의해 수렴지점을 벗어나는걸 방지해줌
AdaGrad
SGD, Momentum, NAG는 모든 파라미터에 대해서 같은 learning rate를 적용함
인풋 변수 중 희소한 변수는 업데이트 횟수가 적기 때문에 한번 업데이트 할 때 수렴 지점까지 조금은 더 빠르게 접근할 필요가 있음
θt,i: t번째 step의 i번 파라미터
gt,i=∇θtJ(θt,i): θt,i에 대한 gradient 벡터
θt+1,i=θt,i−Gt,ii+ϵηgt,i
Gt,ii=g1,i2+g2,i2+...+gt,i2: t 시점까지의 θi에 대한 gradient들의 제곱 총합을 갖는 대각행렬
ϵ은 분모를 0으로 만들지 않기 위해 충분히 작은 값
Vector Form: θt+1=θt−Gt+ϵη⊙gt
⊙ 은 matrix vector 곱셈을 의미하는 연산자
G는 대각행렬이기 때문에 역수취해서 곱한다고 보면됨
업데이트 빈도가 높을수록 learning rate는 점점 작아짐
하지만 learning rate의 과도한 손실은 문제가 될 수 있음
AdaDelta
기존 문제
Adagrad의 iteration 마다 learning rate가 작아지는 문제
Hyperparameter인 learning rate의 필요
첫번째 이슈를 해결하기위해서 분모를 대체함
모든 gradient 정보를 저장하는 것이 아닌 지난 w개의 gradient 정보를 저장
E[g2]t: gradient 제곱의 합이 아닌 제곱에 대한 기댓값을 저장
E[g2]t=γE[g2]t−1+(1−γ)gt2: Decaying Average of Squared Gradient
Δθt=−E[g2]t+ϵηgt=−RMS[g]tηgt: Root Mean Square로 식 간략화
논문 저자는 first order method를 사용해 θ를 업데이트 할 때 Δθ와의 unit(단위)가 맞지 않음을 지적하며 second order method를 사용해서 unit을 통일 해야함을 강조함
θt는 gradient ∇θtJ(θt)를 포함하고 있음
따라서 first order method를 적용했을 때 유닛이 θ의 역수가 나옴 Δθ∝g∝∂θ∂J∝unit(θ)1
unit(x)는 x의 단위를 의미 함
가운데 손실함수J를 세타로 미분을 하는 식을 확장, 일반화 시켰을 때 고차 미분이 되고 그런 관점에서 비례식은 성립함
여기에 second order method 행렬인 헤시안 행렬을 사용했을 때 unit을 맞춰줄 수 있음 Δθ∝H−1g∝∂θ2∂2J∂θ∂J∝unit(θ)
- Newton's Method를 사용한 것임 ∂θ2∂2J1=−∂θ∂JΔθ
- Δθt=−RMS[g]tηg(t)로 앞에서 정의를 했는데 분모에는 이미 mean squared gradient가 들어갔고, unit(단위)로 계산되어야 하기 때문에 분자에 mean squared weight를 새롭게 구함, 여기서도 분모를 구한 과정과 비슷하게 전개하면 E[Δθ2]t=γE[Δθ2]t−1+(1−γ)Δθt2RMS[Δθ]t=E[Δθ2]t+ϵ
- 과 같이 나오고, 현재는 t를 업데이트 하기 전에 근사한 값으로 t-1시점의 값을 집어넣음 Δθt=−RMS[g]tRMS[Δθ]t−1g(t)
최종적으로 업데이트 식은 다음과 같고 결국 learning rate를 필요로 하지 않게됨 θt+1=θt+Δθt
AdaGrad, AdaDelta, RMSProp 처럼 각 파라미터마다 다른 크기의 업데이트를 적용하는 방법
여기서 언급하는 모멘텀은 통계적 의미에서의 모멘텀임(E(X) = 1차 모멘텀, E(X^2) = 2차 모멘텀)
Exponentially Weighted Average(지수 가중 평균 or 지수 이동 평균) 사용
-mt는 1차 모멘텀에 대한 추정치 vt는 2차 모멘텀에 대한 추정치에 가중평균 식 적용
mt=β1mt−1+(1−β1)gtvt=β2vt−1+(1−β2)gt2
mt와 vt의 초깃값을 0벡터로 줬을 때 학습 초기 구간에서 가중치가 0으로 수렴하는 경향이 있다. 이러한 편향을 잡아주기 위해 bias-correct 계산이 필요함
mt^=1−β1tmtvt^=1−β2tvt
최종적으로 업데이트 식은 다음과같음
θt+1=θt−vt^+ϵηmt^
AdaMax
Adam의 v_t텀에 다른 norm을 사용한 방법
Lass, Ridge 회귀에서 사용하는 노름(norm)개념을 사용하는 것
Adam에서는 g_t제곱을 사용하여, 최종식에는 루트가 있음, 즉 l2 norm
Adam의 l2 norm을 lp norm으로 일반화 시키면 vt=β2pvt−1+(1−β2p)∣gt∣p
- p제곱이 아니고 단순 계수 노테이션
-lp norm 값은 커질수록 불안하지만, l∞norm은 안정적인 양상을 띈다고 함, 따라서 AdaMax에서는 l∞norm을 채택
ut=β2∞vt−1+(1−β2∞)∣gt∣∞=max(β2vt−1,∣gt∣)
따라서 최종 업데이트 식은 다음과 같음 θt+1=θt−utηmt^
NAdam(Nesterov-accelerated Adaptive Memoment)
기존의 NAV를 변형함
모멘텀 mt−1을 gradient 업데이트와 θ업데이트에 모두 사용
업데이트할때 mt−1 대신 mt를 사용함
gt=∇θtJ(θ)mt=γmt−1+ηgt
위 까지는 NAV와 일치하지만 γmt−1 대신 γmt를 사용하여 mt+1, 즉 미래 momentum을 사용한 효과를 보임
def adam_eval_adam_delta(self, pm, key, delta):
# 모멘텀과 2차 모멘텀 값을 이동평균 방식으로 유지함
# 미니배치 하나를 처리하는 단계마다 기존 값과 새 값 사이의 가중 평균을 구함
# 이 때 적용할 비율을 ro_1, ro_2
ro_1 = 0.9
ro_2 = 0.999
epsilon = 1.0e-8
# 모멘텀, 2차 모멘텀, 처리 횟수 : 아담 알고리즘에서 지속적으로 관리해야 하는 값
skey, tkey, step = 's' + key, 't' + key, 'n' + key
# 처음 호출되어 버퍼 공간이 존재하지 않을 때
if skey not in pm:
pm[skey] = np.zeros(pm[key].shape)
pm[tkey] = np.zeros(pm[key].shape)
pm[step] = 0
# 이동평균, 기존 값과 새 값의 가중 평균 계산 -> 모멘텀 값
s = pm[skey] = ro_1 * pm[skey] + (1 - ro_1) * delta
t = pm[tkey] = ro_2 * pm[tkey] + (1 - ro_2) * (delta * delta)
# step이 작을 때 모멘텀, 2차 모멘텀 증폭시키는 식
pm[step] += 1
s = s / (1 - np.power(ro_1, pm[step]))
t = t / (1 - np.power(ro_2, pm[step]))
return s / (np.sqrt(t)+epsilon)
AdamModel.eval_adam_delta = adam_eval_adam_delta