Deep learning/모델 구현

31. 함수 최적화(중요)

jwjwvison 2021. 10. 12. 20:55

 최적화란 어떤 함수가 주어졌을 때 그 최솟값(또는 최대값)을 반환하는 '입력(함수의 인수)'을 찾는 일이다. 신경망 학습의 목표도 손실 함수의 출력을 최소화하는 매개변수를 찾는 것이니 최적화 문제에 속한다.

 

  • 로젠블록 함수

 이번에는 로젠블록 함수의 출력이 최소가 되는 x0와 x1을 찾을 것이다. 답부터 말하면 (x0,x1)=(1,1)이다.

 

  • 미분 계산

 가장 먼저 로젠블록 함수의 (x0,x1)=(0.0, 2.0)에서의 미분을 계산해 보자.

def rosenbrock(x0,x1):
    y=100*(x1-x0 **2) **2 + (1-x0) ** 2
    return y

 이 코드를 실행하면 x0와 x1의 미분을 각각 -2.0과 400.0이라고 나온다. 이때 두 미분값을 모은 값, 즉 (-2.0, 400.0) 벡터를 기울기(gradient) 혹은 기울기 벡터라고 한다. 기울기는 각 지점에서 함수의 출력을 가장 크게 하는 방향을 가리킨다. 지금 위의 예에서는 (x0,x1)=(0.0,2.0)지점에서 y값을 가장 크게 늘려주는 방향이 (-2.0, 400.0)이라는 의미이다. 반대로 기울기에 마이너스를 곱한 (2.0, -400.0) 방향은 y값을 가장 작게 줄여주는 방향을 뜻한다.

 

  • 경사하강법 구현

 복잡한 형상의 함수라면 기울기가 가리키는 방향에 반드시 최대값이 존재한다고는 볼 수 없다(마찬가지로 반대 방향에 최솟값이 존재한다고 볼 수도 없다). 그러나 국소적으로 보면 기울기는 함수의 출력을 가장 크게 하는 방향을 나타낸다. 그래서 기울기 방향으로 일정거리만큼 이동하여 다시 기울기를 구한느 작업을 반복하면 점차 원하는 지점에 접근하리라 기대할 수 있다. 이것이 경사하강법이다. 

 

 그렇다면 로젠블록 함수의 최솟값을 우리의 Dezero를 통해 구해보자.

X0=Variable(np.array(0.0))
x1=Variable(np.array(2.0))
lr=0.001
iters=50000

for i in range(iters):
    print(x0,x1)
    y=rosenbrock(x0,x1)
    

    x0.cleargrad()
    x1.cleargrad()
    y.backward()

    
    x0.data -= lr * x0.grad
    x1.data -= lr * x1.grad

 출발점 (0.0, 2.0)에서 시작하여 위치가 계속 갱신되는 모습을 볼 수 있다. 다음 그림은 이 결과를 플롯한 모습이다.

 iters=50000으로 설정해 실행하면 실제로 (1.0,1.0) 위치에 간신히 도착한다.

 

 위의 결과들을 통해 경사하강법은 일반적으로 수렴이 느리다는 단점이 있다.

 

 경사하강법을 대체할 수 있는, 수렴이 더 빠른 방법은 여러 가지가 있다. 그중에서 유명한 것이 뉴턴 방법이다. 뉴턴 방법으로 최적화하면 더 적은 단계로 최적의 결과를 얻을 가능성이 높아진다.

뉴턴 방법은 계곡을 뛰어넘어 단번에 목적지에 도착한다. 갱신 횟수는 불과 6회이다.

 

  • 뉴턴 방법을 활용한 최적화 이론

 이번에는 뉴턴 방법을 활용해 최적화를 구현할 것이다. 경사하강법 대신 뉴턴 방법을 사용하여 실제로 더 빨리 수렴하는지 확인할 것이다.

 

 아래는 뉴턴 방법의 원리에 대한 설명이다.

 결론적으로 뉴턴 방법을 활용한 최적화는 2차 미분의 정보도 이용한다. 뉴턴 방법은 추가된 2차 미분 정보 덕에 효율적인 탐색을 기대할 수 있으며, 결과적으로 목적지에 더 빨리 도달할 확률이 커진다.

 

 이제 y=x^4 - 2x^2 수식을 최적화 해보겠다. 초깃값을 x=2로 설정한 후 최솟값 중 하나인 x=1에 도달할 수 있는지 검증해 보자.

 

  • 뉴턴 방법을 활용한 최적화 구현

 Dezero는 아직 2차 미분은 자동으로 구하지 못하므로 다음과 같이 수동으로 2차 미분을 구한다.

def f(x):
    y=x**4 -2*x**2
    return y

def gx2(x):
    return 12 * x ** 2 -4

x=Variable(np.array(2.0))
iters=10

for i in range(iters):
    print(i,x)
    
    y=f(x)
    x.cleargrad()
    y.backward()
    
    x.data -=x.grad / gx2(x.data)

 위의 결과를 보면 목적지까지 빠르게 도달했음을 알 수 있다.

 

'Deep learning > 모델 구현' 카테고리의 다른 글

33. 형상 변환 함수, 합계 함수  (0) 2021.10.16
32. 고차 미분  (0) 2021.10.14
30. 테일러 급수 미분  (0) 2021.10.12
29. 연산자 오버로드  (0) 2021.10.10
28. 변수 사용성 개선  (0) 2021.10.10