본문 바로가기

ML

[ML] 딥러닝 1 - 8강 수치미분과 gradient

 

※ 본 글은 한경훈 교수님의 머신러닝 강의를 정리, 보충한 글입니다. ※

[딥러닝I] 8강. 수치미분과 gradient - YouTube

 


수치미분

우리가 고등학교때 배운 미분은 극한을 이용한 미분이었지만, 이를 컴퓨터에게 이해시키기는 매우 힘들다.

( 0 이 아니지만 한없이 0에 가까워진다는 극한의 개념을 수식으로 나타내기 힘들기 때문! )

따라서 h를 극한으로 보내는 대신, 아주 작은 값(이 책에서는 0.0001)으로 잡아 값을 근사시킨다.

이때, h를 0.0001 보다 더 작은 수로 근사시키면, 반올림 오차때문에 0으로 인식되는 경우도 있으므로 주의해야 한다.

또한 f(x+h)-(x)/h 보다 f(x+h)-(x-h)/2h가 실제 미분값 f'(x)에 더 가까우므로 수치미분으로 f(x+h)-(x-h)/2h의 값을 자주 사용한다.

def numerical_diff(f,x):
    h = 1e-4           #0.0001
    return (f(x+h)-f(x-h))/2*h

 


수치미분을 이용하여 접선 구하기

def tangent_line(f, x):             # cf. 접선을 영어로 tangent_line이라 한다.
    d = numerical_diff(f, x)          # f'(x)
    print(d)
    y = f(x) - d * x                  # y = f(x) - f'(x)*x
    return lambda t: d * t + y       # g(t) = = f'(x)(t-x) + f(x)

x = np.arange(0.0, 20.0, 0.1)         # [0, 0.1, 0.2, ..., 19.9]
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")

tf = tangent_line(function_1, 5)
y2 = tf(x)

plt.plot(x, y)                        # 그래프
plt.plot(x, y2)                       # 접선 그래프
plt.show()

수치미분을 이용해 x = 5 에서의 접선을 구해보았다.

 


Gradient

모든 변수에 대한 편미분을 벡터로 정리한 것을 gradient(기울기)라고 하고 ∇를 앞에 붙여 표기한다.

ex, f(x,y)가 3x^2 * y 일 때, ∇ f(x,y) 는 (6yx, 3x^2) 이다.

 

 

gradient의 사용

기존에는 방향 미분계수를 구하기 위해 식 f(x+h)-f(x)/h 에 값을 직접 대입하곤 했다.

하지만 gradient와 방향 벡터를 내적하면 방향 미분계수를 바로 구할 수 있으므로 훨씬 간단해진다.

ex. f(x,y) = x^2 + y^2 일때, (1,1)로의 방향 미분계수는 ∇ f(x,y) = (2x, 2y)와 (1,1)을 내적한 2x + 2y 즉 4이다.

 

cf. 이를 바탕으로 접평면의 방정식을 구하면 아래와 같다.

 

$$z = ∇ f(x_0,y_0) ~ · ((x,y)-(x_0,y_0))+f(x_0,y_0)$$

 


방향 미분계수의 최대, 최소

함수 𝑓와 점 𝐱는 고정되어 있고 방향의 크기 |v| 는  1이라 하자.

방향 미분 계수는 gradient와 벡터의 내적이므로 아래와 같이 나태낼 수 있다.

 

$$∇ f(x) · v = ∇ f(x) |v| cosθ = ∇ f(x) cosθ$$

 

따라서 방향 미분이 가장 커지는 방향은 cosθ가 1일 때 (θ = 0)

즉, gradient 방향과 방향벡터의 방향이 같을 때이고, 방향미분계수의 최대값은 gradient의 크기이다.

같은 원리로, 방향 미분이 가장 작아지는 방향은 cosθ가 -1일 때 (θ = 180도)

즉, gradient 방향과 방향벡터의 방향이 반대일 때이고, 방향미분계수의 최소값은 - gradient의 크기이다.

 

정리

방향 미분이 가장 커지는 방향은 gradient 방향이고 값은 gradient의 크기이다.

⭐방향 미분이 가장 작아지는 방향은 gradient 반대 방향이고 값은 마이너스 gradient의 크기이다

방향 미분이 0이 되는 방향은 gradient와 수직인 방향이다.

 

🤔아니 우리는 머신러닝을 배우고 있는데 방향미분계수의 최대최소는 왜 배우는거지??

라고 생각할 수 있지만, 한경훈 교수님 피셜 위 정리는 머신러닝을 통틀어서 가장 중요한 수학적 정리라고 한다.

위 정리는 딥러닝과 머신러닝의 초석이 되기도 하고 쓰이기도 굉장히 자주 쓰인다고 한다! 이유는 뒤에 나옴!

 


등위선, 등위면

고등수학에서 일변수 함수 f(x)는 그래프를 좌표평면 위에 그릴 수 있었다.

하지만, 이변수함수나 삼변변수 함수는 3차원 이상이므로 평면위에 그래프를 그리기 힘들다.

이러한 문제를 해결하기 위해 도입한 개념이 바로 등위(곡)선, 등위(곡)면이다.

 

ex.

이변수함수 f(x,y)와 상수 c에 대해 [(x,y):f(x,y)=c]를 만족하는 f(x,y)를 f의 c - 등위선이라 한다.

삼변수함수 f(x,y,z)와 상수 c 에 대해 [(x,y,z):f(x,y,z)=c]를 만족하는 f(x,y,z)를 f의 c - 등위면이라 한다.

 

gradient 를 이용하면 등위곡선 or 등위곡면에서 함수값의 변화율이 가장 큰 방향을 찾을 수 있다.

'함수값이 가장 빨리 증가할 때는 gradient랑 같은 방향일 때이다.'라는 정리를 이용하면 된다.

같은 방법으로 함수값이 가장 빨리 감소하는 방향은 gradient랑 반대 방향이다.

 

등위선(면)에서 gradient의 의미는 기하학적으로도 해석될 수 있다.

원래는 수학적 증명을 거쳐야 하지만 내가 이해가 잘 안되므로 (...😓) 간단하게 증명하고 넘어가자!

등위선을 가장 빨리 올라가는 궤적은 등위선에 수직인 궤적이다.

앞서 우리는 함수값의 변화율이 최대일때의 방향은 gradient 방향인 것을 증명하였다.

따라서 등위선의 수직방향은 gradient 방향과 동일함을 알 수 있다.

⇒ 즉, 등위선(면)과 gradient는 항상 수직이다.

cf. 사실 1학년때 배운 미적분학에 대해 하나도 기억나지 않으므로 위의 정리가 맞는지는 잘 모르겠다..
여튼 중요한 것은 등위선(면)과 gradient가 항상 수직이라는 사실 아닐까..?

 


수치미분을 이용해 gradient 구현

def _numerical_gradient_no_batch(f, x):      # batch가 아닌 경우 즉, x가 벡터인 경우
    h = 1e-4                                 # 0.0001
    grad = np.zeros_like(x)                  # x와 모양이 같고 원소가 0인 벡터 생성
    
    for idx in range(x.size):                # x = (a,b,c)이고 idx = 0 일때
        tmp_val = x[idx]                     # tmp_val == a
        
        # f(x+h) 계산
        x[idx] = float(tmp_val) + h          # x = (a+0.0001,b,c)
        fxh1 = f(x)                          # fxh1 = f(a+0.0001,b,c)
         
        # f(x-h) 계산
        x[idx] = tmp_val - h                 # x = (a-0.0001,b,c)
        fxh2 = f(x)                          # fxh2 = f(a-0.0001,b,c)
        
        grad[idx] = (fxh1 - fxh2) / (2*h)    # grad[0] = a에 대해 수치미분(편미분)
        x[idx] = tmp_val                     # 값 복원 : x = (a,b,c)
        
    return grad                              # grad = (a 편미분, b 편미분, c 편미분)
def numerical_gradient(f, X):
    # x가 백터면 no_batch 로 처리
    if X.ndim == 1:      
        return _numerical_gradient_no_batch(f, X)
    # x가 행렬이면 batch로 판단하여 처리
    else:   
        grad = np.zeros_like(X)
        # enumerate - 인덱스 번호와 컬렉션의 원소를 tuple형태로 반환
				# idx : 몇번째 행인지, x : 해당 행의 원소
        for idx, x in enumerate(X): 
            grad[idx] = _numerical_gradient_no_batch(f, x)
        
        return grad

이미지 출처 :

https://www.quora.com/Why-does-the-method-of-Lagrange-multipliers-work-for-optimization-in-multivariable-calculus-Why-exactly-given-a-function-f-x-y-and-a-constraint-g-x-y-c-can-we-set-the-gradients-of-the-functions-to-be-multiples-of-each-other