ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [cs231n] 3강 손실 함수와 최적화 (3/4, 최적화 / optimization)
    AI 2021. 1. 11. 18:39

     

    최적화를 얘기할 때 저는 종종 걷는 것을 생각합니다.

    거대한 계곡 주변을요.

    어떤 얘기냐면, 여러분이 이 큰 계곡을, 다른 산과 계곡과 시내를 등등 걸어다니는 거죠. 이 풍경의 모든 지점은 W 파라미터의 어떤 세팅들에 해당합니다. 여러분이 여기의 작은 사람이고, 이 계곡을 돌아다니는 겁니다. 이 점들의 높이는 손실과 같고, W에 의해 발생하죠. 이 작은 사람으로서 해야 할 일은 이 풍경을 돌아다니면서, 어떻게든 이 계곡의 바닥을 찾는 것입니다. 이건 일반적으로 어려운 문제이죠. 만약 내가 정말 똑똑하다면, 내 손실 함수와 정규화 등등분석적 특징에 대해 열심히 생각해 보고, 어쩌면 최소화하는 것을 만들 수 있을 지도 모르겠지만요. 그건 일종의 마법처럼 이 계곡의 밑바닥으로 계속 순간이동하는 거죠. 그러나 실제에선, 여러분의 예측 함수 f와 손실 함수와 정규화가 일단 커지고 복잡해지면, 그리고 신경망을 쓰면, 여러분을 곧장 최소값으로 데려다 주는 명확한 분석적 해답을 쓰는 것을 별로 기대하기 어렵죠. 그래서 실제에선, 다양한 종류의 반복적 메소드를 사용합니다. 우리는 어떤 해답으로 시작을 해서, 시간이 지남에 따라 그걸 점점 개선시키죠. 여러분이 제일 처음에 생각해 볼 수 있는 최고로 멍청한 건 랜덤 탐색 (search)입니다.

    W를 많이 받아서, 임의로 샘플링하고, 그걸 손실함수에 넣죠. 그담엔 얼마나 잘되는지 봅니다. 스포일러지만, 이건 정말 나쁜 알고리즘이죠. 이걸 쓰면 안됩니다. 적어도 시도할 수 있겠다고 생각은 해볼 수 있죠. CIFAR 10에 대해 랜덤 탐색으로 선형 분류기를 훈련시키는 것 같은 것을요.

    이것에는 10개의 클래스가 있고 랜덤 확률은 10%죠. 그리고 몇 번의 랜덤 시도를 하면, 우리는 순전히 바보같은 운으로  W의 어떤 세팅이 15%정도의 정확도가 나온다는 걸 우리는 결국 알수 있었습니다. 완전 랜덤 보단 낫죠. 최첨단 수준은 95%입니다. 좁여야 할 갭이 좀 있으니 실제에선 사용하지 마세요. :)

    더 나은 전략은 이 풍경에서 주변의 기하학을 이용하는 겁니다. 여러분이 이 풍경을 돌아다니는 이 작은 사람이라면, 아마도 여러분은 계곡 바닥으로 내려 가는 길을 바로 알 수는 없을 겁니다. 그러나 여러분이 할 수 있는 것은 발로 느끼면 그 근처의 기하학적 구조 어떻게 생겼는지 알아내는 것입니다. 제가 여기 서 있다면, 어떤 방향이 나를 좀 더 아래쪽으로 데려다 줄 지를 발로 느껴보는 거죠. 땅의 경사가 어디 있는지, 나를 이 방향으로 내려가게 해 줄 지를요. 그 방향으로 한발 움직여서 조금 내려가고, 어느쪽이 아래인지를 발로 다시 느끼는 걸 계속 반복해서, 결국 계곡의 바닥에 도착하길 바라는 거죠. 이게 비교적 쉬운 알고리즘으로 보이는데, 만약 여러분이 세부사항을 올바르게 알고 있다면 사실 이게 실제에서 매우 잘 동작합니다. 이것이 이런 큰 신경망을 훈련시킬 때, 우리가 따랴야 할 일반적으로 전략이죠. 선형 분류기와 다른 것들을 훈련시킬 때도 마찬가지구요. 세부 정보가 약간 빠진 것 같은데요. 뭐가 경사죠?

    미분 수업을 생각해 보면, 적어도 1차원에서는, 경사는 이 함수의 미분이죠. 만약 1차원 함수 f가 있다면, 그건 스칼라 x를 받아서, 어떤 커브의 높이를 돌려주고, 그 다음 우리는 어떤 지점에서든지 경사를 혹은 미분값을 계산할 수 있죠. 어느 방향으로든지 작은 걸음 h를 받아서 그 걸음에 대한 함수값의 차이를 비교하는 겁니다. 그리고 걸음 크기를 0으로 하면, 그 지점에서 그 함수의 경사가 나오죠. 이건 꽤 자연스럽게 멀티 변수 함수에도 일반화됩니다. 실제에서, 우리의  x는 아마 스칼라가 아닐거고, 벡터일거고, x는 전체 벡터인데, 이 우리가 미분값을 사용한다는 개념이 다변수까지 일반화되죠. 다변수 세팅에서 경사는 부분 미분의 벡터입니다. 그리고 경사는 x와 같은 모양을 가질 겁니다. 경사의 각 항목은 만약 우리가 좌표에서 그 방향으로 움직일 때, 함수 f의 경사값이 뭔지를 얘기해 주죠. 경사는 매우 좋은 특성을 갖는 걸로 알려졌는데, 경사는 부분 미분한 벡터이기 때문에 함수의 가장 크게 증가하는 방향을 가리킵니다. 따라서, 반대 경사 방향을 보면, 함수의 가장 크게 감소하는 방향을 알려주죠. 더 일반적으로, 그 풍경에서 모든 방향으로의 경사를 알고 싶다면, 그건 경사와  그 방향을 가리키는 단위 벡터 (unit vector)와의 내적과 같죠. 경사가 중요한 건, 현재지점에서 여러분의 함수로의 이 선형 1차 근사를 제공합니다. 실제론, 딥 러닝의 많은 부분은 여러분의 함수의 경사를 계산하는 것에 관한 겁니다. 그 경사를 이용해서 여러분의 파라미터나 벡터를 반복적으로 업데이트하죠. 컴퓨터에서 이 경사를 평가하는 방법으로 생각해 볼 수 있는 나이브 (naive)한 방법은, 유한 차분 (finite difference)의 방법을 이용하는 거죠. 

    여기서 우리는 현재 W가 파라미터 벡터고, 현재 손실이 약 1.25라고 알려 줍니다. 우리의 목표는 경사 dW를 W와 같은 형태로 계산하면 경사의 각 항은 우리가 작은 무한소 (infinitesimal)의 양만큼 움직인다면, 그 좌표방향으로 손실이 얼마나 변할 지를 알려줍니다. 

    생각해 볼 수 있는 방법은 이 유한한 차이를 계산하는거죠. 우리는 W가 있고, 우리는 아마 W의 첫번째 원소를 작은 값 h만큼 증가시키려고 할 겁니다. 그리고 우리 손실 함수와 분류기와 등등을 사용해서 손실을 다시 계산하죠. 이 세팅에서는, 첫번째 차원에서 조금 움직인다면, 우리의 손실은 조금 줄어들어, 1.2534에서 1.25322가 되죠.

    우린 이 리밋 정의 (limit definition)를 사용해서 이 첫번째 차원의 경사에 대한 유한 차분 근사를 얻습니다.

    2번째 차원에서 이 절차를 반복하는 걸 생각해 보면, 첫번째 차원을 원래 값으로 되돌려놓고, 두번째 방향으로 조금 움직입니다.

    다시 우리는 손실을 계산하고, 두번째 슬롯에서 경사에 대한 근사를 계산하기 위해서 이 유한 차분 근사를 사용합니다.

    3차에 대해서 또 반복하죠.

    같은 일은 반복합니다. 이건 사실 끔찍한 아이디어죠. 왜냐면 엄청 느려요. 생각해 보면, 이 함수 f를 계산하는 것은 실제로 엄청 느린데, 이게 큰 합성곱 신경망이라면, 이 파라미터 벡터 더블유는 아마도 여기처럼 10개의 항목을 갖고 있지는 않을 겁니다. 복잡한 딥러닝 모델에 대해선수천만개일 수도 있고, 수억개일수도 있죠. 실제론, 여러분의 유한 차분에 대한 경사를 계산하고 싶지 않을 겁니다. 왜냐면, 하나의 경사를 얻기 위해서 여러분은 아마도 수억개의 함수 평가를 기다려야 합니다. 그건 엄청 느리고, 진짜 나쁜 거죠.

    그러나 고맙게도 우린 그걸 할 필요 없죠. 아마도 여러분은 여태까지 살아오면서, 미분 수업을 들어 본 적이 있을 텐데요.

    이 분들 덕분에, 우리의 손실에 대한 표현을 그냥 쓰면 됩니다. 

    이 경사가 무엇이 되어야 하는지에 대한 표현을 적기 위해서 미분이라는 마법 망치를 쓰죠. 유한 차분을 거쳐 그걸 분석적으로 계산하는 것 보다는 이게 훨씬 효율적이죠. 장점은 첫째로 정확하고, 둘째로 이게 훨씬 빠릅니다. 단지 이 하나의 표현식만 계산만 하면 되니까요. 

    현재 W의 그림으로 돌아가 보면, 모든 W의 차원에 대해 반복하기 보단, 경사에 대한 분석적 표현이 뭔지를 우리는 미리 알수 있죠. 그냥 그걸 적고 W부터 바로 dW, 즉 한 단계에서 경사를 계산하죠.

    요약하면, 이 뉴메리컬 경사 (numerical gradient)는 간단하고 상식적이죠. 근데 이걸 실제에선 쓰지 않을 겁니다. 실제에선, 경사 계산을 수행할 때는 항상 분석적 경사를 취해서, 그걸 사용하죠. 그러나 한가지 재밌는 노트는 이 뉴메리컬 경사는 매우 유용한 디버깅 도구라는 겁니다. 예를 들어 손실과 손실의 경사를 계산하는 코드를 썼는데, 어떻게 이걸 디버깅하죠? 어떻게 확실히 분석적 표현이 여러분이 유도하고 코드로 작성한 것과 맞다는 것을 확인할 수 있죠?

    이걸 위해 아주 자주 쓰는 디버깅 전략은 수치적 경사를 일종의 유닛 테스트로 사용해서 확실히 여러분의 분석적 경사가 맞는지 확인하는 거죠. 다시 말하지만, 이건 엄청 느리고, 정확하지 않아서, 이 수치적 경사 체킹을 할 때는, 실제로 합리적인 시간내에 동작하도록 문제의 파라미터를 소위, 스케일 다운 (scale down)해야 할 겁니다. 여러분 자신의 경사 계산을 작성할 때 이건 매우 유용한 디버깅 전략입니다. 이건 정말 실제에서 흔히 써요.

    댓글

Designed by Tistory.