이전 단계에서 로젠브록 함수의 최솟값을 경사하강법으로 구했는데
기울기를 구하는 작업을 5만 번 가까이 반복해야 목적지에 도달했습니다. 이는 경사하강법이 수려이 느리다는 건데요.
경사하강법을 대체하고 수렴이 더 빠른 방법 중 유명한 것이 '뉴턴 방법'입니다.
경사하강법은 '계곡'에서 서서히 목표값에 접근해가는 반면에 뉴턴 방법은 계곡을 뛰어넘어 목적지에 도착합니다.
* 로젠브록 함수에서는 경사하강법과 뉴턴 방법의 갱신 횟수 차이가 크게 나왔는데, 초깃값이나 학습률 등을 어떻게 설정하느냐에 따라 달라질 수 있습니다. 일반적으로 초깃값이 정답에 충분히 가까우면 뉴턴 방법이 더 빨리 수렴합니다.
29.1 뉴턴 방법을 활용한 최적화 이론
변수를 하나만 받는 함수를 예로 들어 뉴턴 방법을 활용한 최적화 구현을 설명합니다.
y = f(x)라는 함수의 최솟값을 구한다고 했을때 뉴턴 방법으로 최적화하려면 테일러 급수에 따라 y = f(x)를 아래와 같이 변환합니다.
f(x) = f(a) + f'(a)(x-a) + 1/2!f''(a)(x-a)**2 + 1/3!f'''(a)(x-a)**3 + ...
테일러 금수에 따라 어떤 점 a를 기점으로 f를 x의 다항식으로 나타낼 수 있습니다.
이때 1차 미분, 2차 미분, 3차 미분 형태로 항이 증가하는데 증가하는 걸 어느 시점에 중단하면 f(x)를 근사적으로 나타낼 수 있습니다.
2차 미분에서 중단하면 아래 같이 y = f(x) 함수를 2차 미분에서 중단했습니다.
f(x) ≃ f(a) + f'(a)(x-a) + 1/2f''(a)(x-a)**2
변수 x를 기준으로 x의 2차 함수이며 y = f(x)는 어떤 함수를 x의 2차 함수로 근사한 것입니다.
(파이썬으로 그래프 그리다 실패해서..허접한 그림으로 대체...)

근사한 2차 함수는 a에서 y = f(x)에 접하는 곡선입니다.
2차 함수의 최솟값은 해것적으로 구할 수 있습니다. 미분 결과가 0인 지점을 확인하면 되니까요
f(x) = f(a) + f'(a)(x-a) + 1/2f''(a)(x-a)**2 일때 f'(x)을 계산하면
f'(x) = 0 + f'(a) + f''(a)(x-a) 가 되고 f'(x)가 0이 되는 지점 x를 찾는다고 했을때
0 = f'(a) + f''(a)(x-a) # f'(x)를 0으로 바꿔주고
이 식을 x에 대해서 풀어주면
f''(a)(x-a) = -f'(a)
x - a = - f'(a) / f''(a)
x = a - f'(a)/f''(a)
이 x값이 f(x)의 극값을 가지는 위치입니다.
a의 위치를 - f'(a)/f''(a) 씩 갱신해주고 갱신 된 a의 위치에서 같은 작업을 해주면'뉴턴 방법에 의한 최적화'입니다.
x <- x - af'(x) ---경사하강법x <- x - f'(x)/f''(x) ---뉴턴 방법
두 방법이 모두 x를 갱신 하지만 경사하강법은 a라는 계수를 사람이 수동으로 설정하고 a의 값 만큼 기울기 방향으로 진행하여 x의 값을 갱신합니다.뉴턴 방법은 2차 미분을 이용하여 경사하강법에서 말하는 a를 자동으로 조정합니다.
정리하자면,
경사하강법은 1차 미분만의 정보를 사용하고 뉴턴 방법을 사용한 최적화는 2차 미분의 정보도 이용합니다.
뉴턴 방법은 추가된 2차 미분 정보 덕에 효율적인 탐색을 기대할 수 있고 목적지에 더 빨리 도달할 확률이 커집니다.
29.2 뉴턴 방법을 활용한 최적화 구현
DeZero에서는 수동으로 2차 미분을 구현합니다.
y = x**4 - 2x**2
∂y / ∂x = 4x**3 - 4x
∂**2y / ∂x**2 = 12x**2 - 4
import numpy as np
from dezero import Variable
# import dezero's simple_core explicitly
import dezero
if not dezero.is_simple_core:
from dezero.core_simple import Variable
from dezero.core_simple import setup_variable
setup_variable()
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)
1차 미분은 지금까지처럼 역전파로 구하고 2차 미분은 gx2 함수를 구현한 것처럼 수동으로 구합니다.
뉴턴 방법의 갱신 수식에 따라 x를 갱신하고 코드를 실행하면 아래와 같이 나옵니다.

찾으려고 했던 답이 1이기 때문에 7번째 갱신에서 답에 도달한걸 알 수 있습니다.
'Study 공부할레나' 카테고리의 다른 글
| [스터디/밑시딥3] 제 3고지 고차미분계산_31. 고차 미분(이론 편) (0) | 2023.09.09 |
|---|---|
| [스터디/밑시딥3] 제 3고지 고차미분계산_30. 고차 미분(준비편) (0) | 2023.09.09 |
| [스터디/밑시딥3] 제 3고지 고차미분계산_28. 함수 최적화 (1) | 2023.09.06 |
| [스터디/밑시딥3] 제 3고지 고차미분계산_27. 테일러 급수 미분 (0) | 2023.09.06 |
| [스터디/밑시딥3] 제 3고지 고차미분계산_26. 계산 그래프 시각화(2) (0) | 2023.09.03 |