ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [cs231n] 7강 신경망 훈련하기 (2/4, 더 멋진 최적화 (fancier optimization))
    AI 2021. 3. 23. 14:35

    모멘텀을 약간 변형한 것이 있는데, 가끔 볼 수 있는 것이고, 네스테로프 (Nesterov) 가속 경사라고 불립니다. 때로는 네스테로프 모멘텀이라고 불리죠. 이건 순서를 조금 바꿉니다. 일반적인 SGD 모멘텀에서는, 현재 위치에서 경사를 추정하고 우리의 속도와 경사를 혼합할 것을 취합니다. 네스테로프 가속 경사에서는 약간 다른 것을 합니다. 여기서 여러분은 빨간 점에서 출발하고 속도가 여러분을 데려가는 방향으로 움직입니다. 그지점에서 경사를 평가하고 원점으로 돌아가서 그 둘을 섞습니다. 이건 좀 재밌는 해석이긴 하지만 여러분은 정보를 약간 더 섞는다고 생각할 수 있습니다. 만약 어러분의 속도의 방향이 약간 잘못되었다면, 그건 목표 지형의 약간 더 큰 부분으로부터 경사 정보를 통합시킬 수 있도록 해주는 거죠. 이건 또한 콘벡스 (convex) 최적화에 있어서, 매우 좋은 이론적 특성들이 있지만, 신경망과 같은 컨벡스가 아닌 문제에 대해서는 그것들은 좀 쓸모없는 것이 되죠.

    방정식으로 적어보면, 네스테로프 모멘텀은 이렇게 적을 수 있습니다. 우리의 속도를 업데이트하고 , 이전 속도에 따라서 한 걸음 움직이죠. 그리고 거기에서 경사를 평가합니다. 이제 다음 걸음을 걸을 때, 우리는 사실 우리의 속도의 방향으로 걷는 거고, 그 속도는 이 여러 지점으로부터의 정보를 포함하고 있습니다.

    이 네스테로프 공식은 약간 화나게 하는데요. 일반적으로 손실 함수가 있을 때, 여러분은 손실과 경사를 같은 지점에서 평가하고 싶습니다. 그런데 네스테로프는 이걸 약간 깨뜨리기 때문입니다. 이건 작업하기 약간 짜증나죠.

    고맙게도, 여러분이 변수 변경을 할 수 있는데요. 변수를 바꾸고 약간 다시 섞으면, 네스테로프 모멘텀을 약간 다르게 쓸 수 있습니다. 그럼 손실과 경사를 항상 동시에 평가할 수 있게 해줍니다. 일단 변수를 이렇게 바꾸면, 네스트로프의 좋은 해석을 얻을 수 있는데, 첫번째 단계에서, 이건 바닐라 SGD의 경우에서 속도를 업데이트하는 것과 정확히 똑같이 보이죠. 여기서 우리는 현재 속도가 있고 현재 지점에서 경사를 평가하고 이 둘을 감소하는 방식으로 섞을 수 있습니다. 파라미터 벡터를 업데이트할 때는, 두번째 방정식을 보면, 우리의 현재 지점 더하기 현재 속도 더하기 우리의 현재 속도와 이전 속도의 차이에 가중치를 준 값이 있죠. 여기서 네스테로프 모멘텀은 현재 속도와 이전 속도 사이의 오류를 수정하는 항을 포함하고 있는 것이죠.

    이런 단순한 문제에 대한 SGD와 SGD 모멘텀과 네스테로프 모멘텀을 보면, 검정색 SGD가 최소로 향해 천천히 전진하는 것을 볼 수 있죠. 파란색과 녹색은 모멘텀과 네스테로브를 보여주죠. 이것들은 최소를 오버슈팅하는 동작이 있는데, 그것들은 속도를 높여서 최소값을 지나가고 이후에 자신을 수정해서 최소값으로 돌아옵니다.

    또 다른 흔히 사용되는 최적화 전략은 아다그래드 (AdaGrad)라고 불리는 알고리즘입니다. 존 두치 (John Duchi)는 이곳의 교수님인데, 박사과정일때 연구한 것이죠. 아다그래드의 아이디어는 최적화 과정 동안, 훈련동안 보는 모든 경사 제곱에 대해 이동 추정 (running estimate)을 하거나 혹은 이동 합 (running sum)을 구합니다. 속도항을 가지고 있는 것보다는, 그 대신 이 그래드스퀘어드 (grad_squared) 항을 가지는 거죠. 훈련중에, 이 이 그래드스퀘어드 항에 대한 제곱 경사를 계속 더하는 겁니다. 이제 파라미터 벡터를 업데이트할 때, 이 그래드 스퀘어드 항으로 나눌 겁니다. 우리가 업데이트 단계를 진행할 때요.

    궁금한 점은 매우 높은 조건 숫자 (high condition number)를 가지고 있는 상황에서, 이런 종류의 스케일링 (scaling)이 무엇을 하느냐는 겁니다. 우리가 2개의 좌표, 항상 매우 높은 경사를 가지고 있는 하나와, 항상 매우 작은 경사를 가지고 있는 하나를 갖고 있다면, 작은 경사의 제곱을 더함으로써, 우리는 작은 숫자로 나누는거죠. 그럼 우리는 하나의 차원을 따른 움직임을 가속화할 것입니다. 그다음 경사가 매우 큰 경향이 있는 다른 차원을 따르면, 큰 숫자로 나눌거고 그러면 우리는 구불구불한 차원을 따라서는 우리의 진행이 느려지게 될 겁니다. 그런데 여기 문제가 하나 있죠.

    학습과정을 지나면서, t가 점점 커지면, 아다그래드에게 무슨일이 벌어지느냐는 겁니다. 아다그래드는 걸음이 점점 더 작아집니다, 왜냐면, 시간이 지날 수록 제곱 경사의 추정을 계속해서 업데이트하기 때문이죠. 이 추정값은 훈련과정을 거치면서 단조롭게 계속 커지고 커지고 또 커집니다. 이것이 시간이 지남에 따른 걸음의 크기를 더 작게 더 작게 더 작게 만드는 거죠. 콘벡스 (convex) 경우에는 이것이 사실 매우 좋다라는 것을 보여주는 좋은 이론이 있죠. 왜냐면, 컨벡스 경우에는 최소에 근접하면 느려져서 수렴하기를 원하기 때문이죠. 컨벡스 경우에는 그것이 기능이죠. 그러나 컨벡스 경우가 아니면, 그건 약간 문제입니다. 왜냐면, 안장점에 다가가면, 아다그래드로는 멈출지도 모릅니다. 그리고 더이상 전진을 못하는거죠.

    아다그래드를 약간 변형한 것이 있는데요. 알엠에스프랍 (RMSProp)이라고 불립니다. 이것이 이 걱정을 약간 해결해 줍니다. 알엠에스프랍에서는 여전히 경사 제곱의 추정을 유지하는데요. 그 제곱 추정이 훈련동안 계속해서 증가하도록 놔두는 대신, 그 제곱 추정이 사실 감소하도록 합니다. 실제 경사에 대한 모멘텀이 아니라 경사 제곱에 대한 모멘텀을 가진다는 점을 제외하면, 이건 결국 모멘텀 업데이트 같아 보이네요. 이제 알엠에스프랍으로, 우리의 경사를 계산한 다음, 우리는 그래드스퀘어드의 현재 추정을 취해서 감소율로 곱합니다. 감소율은 보통 0.9 혹은 0.99죠. 그다음 우리는 현재 경사 제곱의 1 - 감소율을 더합니다. 그다음 우리가 걸음을 걸을 때, 그 걸음은 아다그래드와 똑같아 보입니다. 걸음에서 경사 제곱으로 나눠서 하나의 차원에 따른 움직임을 가속화하고 다른 차원을 따라서는 움직임을 느리게 하는 좋은 속성을 가지는 거죠. 그러나 알엠에스프랍에선, 이 추정값들이 리키하기 (leaky) 때문에, 여러분이 원하지 않는 어쩌면 항상 느려지게 되는 문제를 해결합니다.

    여기서 다시, 우리가 좋아하는 장난감 문제를 다시 보여주고 있는데요. SGD는 검은색이고, SGD 모멘텀은 파란색, 알엠에스프랍은 빨간색입니다. 알엠에스프랍과 SGD 모멘텀은 둘 다 SGD보단 훨씬 잘하고 있는 것을 볼 수 있습니다. 그러나 그들의 질적인 면에서 동작은 약간 다릅니다. SGD 모멘텀은 최소값을 지나쳤다가 되돌아오죠. 반면에 알엠에스프랍은 궤적을 조정해서 모든 차원에서 거의 동등한 진전을 이루고 있습니다. 그런데, 여러분이 보이지 않겠지만, 이 플랏 (plot)은 같은 학습률의 아다그래드 (AdaGrad)를 녹색으로 보여주고 있는데요. 지속적으로 감소하는 학습률 문제 때문에, 그건 멈춰버렸습니다. 실제에선 아다그래드는 이런 것들에는 많이 사용되지 않을 것 같습니다. 아다그래드에 약간 불공평한 비교일 수도 있네요. 어쩌면 아다그래드의 학습률을 올려야 할 겁니다. 그럼 이경우 알엠에스프랍같은 모양으로 끝날 겁니다. 그러나 일반적으로, 신경망을 훈련시킬 때, 아다그래드를 사용하지 않는 경향이 있습니다.

    우리는 아다그래드와 알엠에스프랍에서 경사 제곱의 추정을 만들면서, 경사 제곱으로 나누는 다른 아이디어를 가지게 되었습니다. 이 둘은 모두 각각 좋은 아이디어처럼 보입니다. 둘을 붙여서 모두 사용하는 것은 어떨까요? 그게 더 나을지도 모르겠습니다. 그것이 우리를 아담 (Adam)이라고 불리는 알고리즘으로 데려다 줍니다. 혹은 아담과 매우 가까운 곳에 데려다 줍니다. 몇 슬라이드 지나서 여기서 약간 고쳐야할 것들을 볼 겁니다.

    여기 아담에서 우리는 첫번째 모멘텀과 두번째 모멘텀의 추정을 유지합니다. 빨간색 안을 보면, 첫번째 모멘텀의 추정을 경사의 가중치 합으로 만들죠. 우리는 아다그래드나 알엠에스프랍 같은 두번째 모멘텀의 이 이동 추정을 갖죠. 이건 경사의 제곱의 이동 추정입니다. 우리가 업데이트된 걸음을 걸을 때, 우리는 첫번째 모멘텀, 즉 속도를 사용하고 또한 두번째 모멘텀 혹은 두번째 모멘텀의 루트로 나눕니다. 즉 이 경사 제곱항이죠. 아담의 아이디어는 결국 알엠에스 + 모멘텀처럼 보이거나 혹은 모멘텀 + 두번째 경사 제곱처럼 보입니다. 그건 둘의 좋은 특성을 합친거죠. 그러낭 여기 약간의 문제가 있죠. 그건 제일 처음 단계에서 무슨 일이 벌어지느냐 입니다. 제일 처음 단계에서 처음에, 우리는 두번째 모멘텀을 0으로 초기화 했습니다. 두번째 모멘텀을 한번 업데이트한 후에, 이 beta2인, 두번째 모멘텀 감소율은 0.9나 0.99입니다. 1과 매우 가까운 숫자죠. 한번 업데이트한 후에는 두번째 모멘텀이 여전히 0과 매우 매우 가깝습니다. 여기서 업데이트를 하고, 두번째 모멘텀으로 나눌 때, 우리는 매우 작은 숫자로 나누고 있는 겁니다. 우리는 처음에 매우 매우 큰 걸음을 걷는거죠. 처음에 이 매우 매우 큰 걸음은 문제의 지형 때문이 아니죠. 우리가 두번째 모멘텀 추정을 0으로 했다는 사실의 산출물입니다.

    아담에서 우리는 처음 몇 걸음에서, 그건 매우 커지고, 우리가 큰 걸음을 걸을 수 있고, 망칠 수 있다는 것을 기억하세요. 아담은 또한 이 바이어스 (bias) 교정 항을 더해서 시작할 때 매우 큰 걸음을 걷는 이 문제를 피하도록 합니다. 보다시피 첫번째 모멘텀과 두번째 모멘텀을 업데이트한 후에, first_moment와 seconde_moment의 바이어스되지 않은 추정을 만들어서 현재 시간 스텝 t를 합칩니다. 이제 우리는 원래의 첫번째와 두번째 모멘트 추정값을 사용하는 것이 아니라 이 바이어스되지 않은 추정값을 사용해서 걸음을 걷습니다. 이것이 아담의 전체 모습입니다.

    그런데 아담은 정말 정말 좋은 최적화 알고리즘이죠. 많은 다양한 문제에서 정말 잘 동작합니다. 그래서 이것이 제가 어떤 새로운 문제와 씨름할 때, 기본값으로 사용하는 알고리즘입니다.특히 beta1을 0.9로, beta2를 0.999로 학습률을 1e-3나 5e-4로 하는 것은, 여태까지 제가 작업했던 모든 아키텍처에대해서 좋은 시작점이었습니다. 그렇게 해보세요. 일반적으로 그건 정말 좋은 시작점입니다.

    이것들을 플롯으로 그려보면, 같은 문제에 대한 SGD, SGD + 모멘텀, 알엠에스프랍, 아담을 보세요. 아담은 여기 보라색인데, SGD 모멘텀과 알엠에스프랍 둘다의 원소를 모두를 합친 것 같습니다. 아담은 에스지디 모멘텀과 같이 약간 최소값을 지나치지만, 모멘텀 만큼 지나치지는 않죠. 아담은 또한 알엠에스프랍처럼 구부려지려고 하며 모든 차원을 따라서 같은 진전을 만들어내려는 동작을 합니다. 어쩌면 이 작은 2차원 예에서, 아담은 다른 것들에 비슷하게 수렴하지만 모멘텀과 알엠에스 프랍의 동작을 합쳐 놓았다는 것을 정성적으로 볼 수 있죠. 

    또한 이 모든 최적화 알고리즘에서 우리가 본 것은 학습률이 하이퍼파라미터라 (hyperparameter)는 겁니다. 우리는 이 그림을 앞서 몇 번 보았는데, 보다시피, 여러 학습률을 사용해 보면, 가끔 너무 높아서 노란색처럼 폭발할 수도 있고, 너무 낮은 학습률을 사용하면, 파란색처럼 수렴하는데 매우 오래 걸립니다. 적절한 학습률을 고르는 것은 꽤 힘들죠. 이건 약간 어려운 문제입니다. 왜냐면, 학습과정 전체에 걸쳐서 하나의 학습률만 고집할 필요는 없거든요.

    가끔씩 사람들이 시간에 걸쳐서 학습률을 감소시키는 것을 볼 수 있을 겁니다. 우리는 왼쪽의 이 여러 곡선의 효과들을 합쳐서 각각의 좋은 특징들을 얻는거죠. 가끔씩 여러분은 훈련 시작할 때쯤엔 더 높은 학습률로 시작하다가 학습률을 줄여서 훈련 과정을 거치면서 점점 더 작게 만들 것입니다. 이것을 위한 몇가지 전략이 스텝 디케이 (step decay)입니다. 10만번째 이터레이션 (iteration)에서 어떤 인수로 줄이고 계속 진행하는 거죠. 지수적 감소 (exponential decay)도 볼 수 있을 겁니다. 그건 훈련 동안 계속해서 줄이는 거죠. 여러분들은 훈련하는 동안 지속적으로 학습률을 줄이는 여러 변형들을 볼 수 있을 겁니다.

    논문들 특히, 레즈넷 (ResNet) 논문들을 보면, 이런 것과 같은 플롯들을 자주 볼텐데, 손실이 줄어드고, 그다음 갑자기 떨어지고, 다시 평평해 지고, 다시 급락하는 하는거죠. 이 플롯에서 벌어지는 일이 뭐냐면, 스텝 디케이 학습률을 사용하고 있는 겁니다. 평평하다가 갑자기 다시 떨어지는 이 부분들은 어떤 인수로 학습률을 떨어뜨린 이터레이션들인 겁니다. 학습률을 떨어뜨리는 아이디어는, 여러분이 생각할 수 있겠지만, 어떤 좋은 영역에 가까이 가면, 경사가 더 작아지는 겁니다. 그건 너무 많이 튀어 오르게되고, 그다음 우리가 학습률을 떨어뜨리면, 점점 느려져서 지형 아래쪽으로 계속 전진하게 됩니다. 실제에선 이것이 도움이 되곤 하죠.

    한가지 얘기하고 싶은 건 학습률 감소가 SGD 모멘텀에서 흔히 사용되지만, 아담 같은 것에서는 덜 쓰이죠. 지적하고 싶은 다른 한가지는 학습률 감소는 2차 (second order) 하이퍼파라미터입니다. 일반적으로 시작부터 이것에 대해 최적화하지 말아야 합니다. 보통 처음에 망을 동작하게 할 때, 시작할 때부터 학습률 감소가 없는 좋은 학습률을 고르고 싶을 겁니다. 학습률 감소와 초기 학습률, 그리고 다른 것들에 대해 함께 교차 검증을 하려고 하는 것은 여러분을 혼란스럽게 할 겁니다. 학습률 감소 설정에 대해 할 일은 감소가 없는 것으로 시도해 보고, 무슨 일이 일어나는지 보는 겁니다. 그다음 손실 곡선을 노려보고 어디서 감소가 필요할 지를 알아내는거죠.

    한가지 간단히 얘기하고 싶은 것은 우리가 지금까지 얘기한 이 모든 알고리즘들은 1차 (first order) 최적화 알고리즘입니다. 이 1차원 그림에서, 우리는 구불구불한 목표함수가 있고, 빨간 점이 현재 위치입니다.

    우리가 기본적으로 하는 것은 그 지점에서 경사를 계산하는 것이죠. 우리는 경사 정보를 사용해서 우리의 함수에 대한 어떤 선형 추정을 계산하는 겁니다. 우리의 함수에 대한 일종의 1차 테일러 (Taylor) 추정이죠. 이제 우리는 이 1차 추정이 우리의 실제 함수인 것처럼 생각하고, 그 추정을 최소화하기 위해 움직입니다. (make a step) 그러나 이 추정이 매우 큰 영역에서 유지되지 않으므로, 우리는 그 방향으로 아주 멀리 갈수는 없습니다. 하지만, 여기서의 아이디어는 함수의 첫번째 미분에 대한 정보를 포함하고 있다는 겁니다.

    여러분은 약간 더 멋지게 할 수도 있습니다. 여기 2차 추정 아이디어가 있는데요. 일계 도함수 (first derivative)와 이계 도함수 (second derivative)정보를 모두 고려하는 것입니다. 우리의 함수에 대해 이계 테일러 추정을 하고 이차 방정식으로 우리의 함수를 지역적으로 (locally) 추정을 하는 겁니다. 이차 방정식으로는 바로 최소로 갈 수 있으니 행복한거죠. 이것이 이차 (second order) 최적화의 아이디어입니다.

    이걸 다차원으로 일반화하면 뉴턴 스텝 (Newton step)이라는 것을 얻게 됩니다. 이 이계 도함수 행렬인 헤세 행렬을 계산하면 여러분의 함수에 대한 이 이차 추정을 최소값으로 바로 가기 위해 헤세 행렬의 역행렬을 얻을 겁니다. 우리가 봤던 다른 것들과 비교해 볼 때, 이 업데이트 규칙에서 좀 다른 점을 봤나요?

    네 이건 학습률이 없습니다. 멋지죠. 우리는 2차 추정을 하고 이차 방정식의 최소값으로 바로 움직입니다. 적어도 이 바닐라 버전의 뉴턴 방법에서는 사실상 학습률이 필요 없습니다. 매 걸음마다 최소로 바로 움직입니다. 그러나 실제에선 학습률을 어쨌든 가지게 될 겁니다. 왜냐면, 이차 추정은 완벽하지 않을 거고, 여러분은 단지 최소를 향한 방향으로 움직이길 원할 겁니다. 최소로 바로 가기 보다는요. 하지만 적어도 이 바닐라 버전에선, 학습률이 없습니다.

    하지만 불행하게도, 딥러닝에게는 이건 어쩌면 약간 비실용적입니다. 왜냐면, 이 헤세 행렬은 N x N이고, N은 망의 파라미터 숫자죠. 만약 N이 1억이면, 1억의 제곱은 너무 크죠. 분명히 그걸 메모리에 저장할 수 없을 거고, 그걸 역행렬로 만들 수도 없을 겁니다.

    실제에서 사람들은 가끔 이 유사 (quasi) 뉴턴 방법을 사용합니다. 완전 헤세 (full Hessian)로 작업하고 완전 헤세를 역행렬로 만들기 보다는요. 추정값으로 작업하는 거죠. low rank approximation이 흔히 사용됩니다.

    L-BFGS스는 특별한 이차 최적화 함수로, 추정한 이차를 갖습니다. 헤세의 이 추정값을 유지하는데, 가끔 보게 될 것입니다. 그러나 실제에서는, 많은 딥러닝 문제에 대해서 아주 잘 동작하지는 않습니다. 왜냐면, 이 이차 추정은 확률적 경우를 별로 잘 처리하지 못합니다. 또한 컨벡스 (convex)가 아닌 문제에 대해서는 잘 동작하지 않는 경향이 있습니다. 지금 당장 많이 들여다 보고 싶진 않네요.

    실제에선, 아담이 다양한 신경망 문제들에 대해 정말 좋은 선택입니다. 그러나 만약 전체 배치 업데이트가 가능한 상황이라면, 문제가 어떤 stochasticity (확률성)을 가지고 있지 않은 거니까, 엘비에프지에스가 좋은 선택이죠. 엘비에프지에스는 신경망을 훈련시키는데는 사실 사용되지 않습니다. 하지만, 몇 강의 뒤에 보겠지만, 스타일 트랜스퍼 (style transfer)같은 것을 위해 가끔 사용됩니다. 더 적은 확률성 (stochasity)을 가지고 파라미터가 더 적지만, 최적화 문제를 풀고 싶은 경우죠.

    지금까지 얘기한 모든 전략들은 훈련 오류 (error)를 줄이는 것에 관한 겁니다. 이 모든 최적화 알고리즘은 훈련 오류를 줄여서 목표 함수를 최소화하는 것에 관한 거죠. 하지만 우리는 훈련 오류에 대해 별로 신경쓰지 않습니다. 그 대신, 우리는 보지 않은 데이타에 대한 성능을 신경씁니다. 훈련과 테스트 오류 사이의 이 간격을 좁히는 것에 신경을 쓰죠. 여기서 궁금한 점은 일단 우리가 이미 목표함수를 잘 최적화했다면, 이 간격을 줄이고 우리의 모델이 보지 않은 데이타에서 더 잘 동작하게 하기 위해서 할 수 있는 일은 무엇이냐는 거죠.

    댓글

Designed by Tistory.