본문 바로가기
Data Science/머신러닝

수치미분 나만의 정리

by 코딩은 잼있어 2020. 6. 26.
728x90

수치미분

1. 미분 (derivative)

  • 미분이란 순간변화율 or 접점의 기울기
  • 입력변수 x가 미세하게 변할때, 함수 f(x)는 얼마나 변하는가?
  • f(x) = x^2 일 경우 f`(x) = 2x
    • f(3) = 9 해석 ==> x = 3 에서 출력은 9임을 의미
  • f `(3) = 6해석
    • 입력 x= 3 을 미세하게 변화시킬때 함수는 현재 입력 값의 2배인 6배 변화를일으킴을 의미\
머신러닝에서 자주 사용되는 함수의 미분공식

f(x) = 3 ==> f`(x) = 0

f(x) = x^n ==> f`(x) = nx^(n-1)

f(x) = e^x ==> f`(x) = e^x

f(x) = lnx ==> f`(x) = 1/x

f(x) = e^(-x) ==> f`(x) = -e^(-x)

2. 편미분 (partial derivative)

편미분은 입력변수가 하나 이상인 다변수 함수에서, 미분하고자 하는 변수 하나를 제외한 나머지 변수들을 상수로 취급하고, 해당 변수를 미분하는것

편미분 예시1

f(x, y) = 2x + 3xy + y^3 , 변수 x에 대하여 편미분

f'(x, y) = x + 3y

편미분 예시2

f(x, y) = 2x + 3xy + y^3 , 변수 y에 대하여 편미분

f'(x, y) = 3x + 3y^2

3. 연쇄법칙 (chain rule)

합성함수란 여러 함수로 구성된 함수로서, 이러한 합성함수를 미분하려면 합성함수를 구성하는 각 함수의 미분의 곱으로 나타내는 chain rule이용

합성함수 예시1

f(x) = e^(3x^2) 일때, t = 3x^2로 치환하여 ==> f(t) = e^t 가 됨

f(t) = e^t 를 x에 대하여 미분을 진행하기 위해 chain rule을 사용

img

4. 코드

import numpy as np

def numerical_derivative(f, x):    # 수치미분 debug version
    delta_x = 1e-4 
    # [2] 각각의 편미분 결과값을 넣어줄 grad 준비
    grad = np.zeros_like(x)
    # 함수의 모든 입력값
    print("debug 1. initial input variable =", x)  
    # 결과를 담아줄 배열
    print("debug 2. initial grad =", grad)              
    print("=======================================")

    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])

    while not it.finished:

        # [3] 2 * 2 행렬이 들어오면 1,1=> 1,2 => 2,1 => 2,2 순서대로 
        # 편미분 하고 grad 에 결과값을 넣어둠

        idx = it.multi_index
        print("debug 3. idx = ", idx, ", x[idx] = ", x[idx])   

        tmp_val = x[idx]
        x[idx] = float(tmp_val) + delta_x
        fx1 = f(x)   # f(x+delta_x)

        x[idx] = tmp_val - delta_x 
        fx2 = f(x)   # f(x-delta_x)
        grad[idx] = (fx1 - fx2) / (2*delta_x)       # 미분 공식

        print("debug 4. grad[idx] = ", grad[idx])       # 편미분 출력값
        print("debug 5. grad = ", grad) 
        print("=======================================")

        x[idx] = tmp_val
        it.iternext()   

    return grad

# 입력변수 4 개인 함수 
# f(w,x,y,z) = wx + xyz + 3w + zy^2
# input_obj 는 행렬
def func1(input_obj):    

    w = input_obj[0, 0]
    x = input_obj[0, 1] 
    y = input_obj[1, 0]
    z = input_obj[1, 1]

    return  ( w*x + x*y*z + 3*w + z*np.power(y,2) )   

# 입력을 2X2 행렬로 구성함
input = np.array([ [1.0, 2.0], [3.0, 4.0] ]) 
# [1] 함수 func1와 input를 준비 
numerical_derivative( func1, input )
# 입력값과 편미분 결과를 넣어줄 배열
debug 1. initial input variable = [[1. 2.]
                                   [3. 4.]]
debug 2. initial grad = [[0. 0.]
                         [0. 0.]]
=======================================    
debug 3. idx =  (0, 0) , x[idx] =  1.0
debug 4. grad[idx] =  5.000000000023874
debug 5. grad =  [[5. 0.]
                  [0. 0.]]
=======================================
debug 3. idx =  (0, 1) , x[idx] =  2.0
debug 4. grad[idx] =  13.00000000000523
debug 5. grad =  [[ 5. 13.]
                 [ 0.  0.]]
=======================================
debug 3. idx =  (1, 0) , x[idx] =  3.0
debug 4. grad[idx] =  32.00000000006753
debug 5. grad =  [[ 5. 13.]
                  [32.  0.]]
=======================================
debug 3. idx =  (1, 1) , x[idx] =  4.0
debug 4. grad[idx] =  15.000000000000568
debug 5. grad =  [[ 5. 13.]
                 [32. 15.]]
=======================================

func1함수 내에서 w, x, y, z를 (0,0), (0,1), (1,0), (1,1)로 정의했다

그래서 numerical_derivative의 while문을 돌때마다 idx값이 0,0부터 시작해서 1,1까지 진행하는데 w부터 편미분을 진행해서 z까지 끝낸 결과를 grad 배열에 넣어준다.

728x90