[Machine Learning] λ―ΈλΆ„ - Numerical, Partial Derivative

ByungJik_OhΒ·2025λ…„ 3μ›” 31일
0

[Hyundai Rotem KDT]

λͺ©λ‘ 보기
9/22
post-thumbnail

πŸ“š 수치 λ―ΈλΆ„ (Numerical Derivative)

λ―ΈλΆ„?
μ–΄λ–€ ν•¨μˆ˜μ˜ μ •μ˜μ—­ 속 각 μ μ—μ„œ λ…λ¦½λ³€μˆ˜μ˜ λ³€ν™”λŸ‰κ³Ό ν•¨μˆ˜κ°’μ˜ λ³€ν™”λŸ‰μ˜ λΉ„μœ¨. κ·Έ λΉ„μœ¨μ˜ κ·Ήν•œ ν˜Ήμ€ κ·Ήν•œμ˜ μ§‘ν•©μœΌλ‘œ μΉ˜μ—­μ΄ κ΅¬μ„±λ˜λŠ” μƒˆλ‘œμš΄ ν•¨μˆ˜.

미뢄은 크게 λ‘κ°€μ§€λ‘œ λ‚˜λˆŒ 수 μžˆλ‹€.

  1. 해석 λ―ΈλΆ„ : λ―ΈλΆ„ κΈ°λ³Έ 곡식을 μ΄μš©ν•΄μ„œ λ…Όλ¦¬μ μœΌλ‘œ 미뢄을 μˆ˜ν–‰
  2. 수치 λ―ΈλΆ„ : μ‹€μ œ μˆ˜μΉ˜κ°’μ„ μ΄μš©ν•΄ λ―ΈλΆ„μ˜ 근사값을 μ–»μ–΄λ‚΄λŠ” 방법

μ΄λ•Œ νŒŒμ΄μ¬μ—μ„œλŠ” μ‹€μ œ 계산을 톡해 λ―ΈλΆ„μ˜ κ·Όμ‚¬μΉ˜λ₯Ό μ–»μ–΄λ‚΄λŠ” μˆ˜μΉ˜λ―ΈλΆ„μ„ μ‚¬μš©ν•œλ‹€. 방법은 3κ°€μ§€λ‘œ μ „ν–₯μ°¨λΆ„, ν›„ν–₯μ°¨λΆ„, 쀑앙차뢄이 μžˆλŠ”λ° μš°λ¦¬λŠ” 쀑앙차뢄을 톡해 미뢄을 κ΅¬ν˜„ν•΄ λ³Ό 것이닀.


πŸ“š 쀑앙차뢄

μ‹€μ œ λ―ΈλΆ„κ³Ό κ°€μž₯ κ°€κΉŒμš΄ λ―ΈλΆ„λ²•μœΌλ‘œ, xx에 Β±Ξ”x\pm \Delta xλ₯Ό ν•΄μ€€ 값을 μ‚¬μš©ν•˜μ—¬ 쀑앙 차뢄을 κ΅¬ν•œλ‹€. μ΄λ•Œ Ξ”x\Delta x의 값은 10βˆ’8^{-8} λ³΄λ‹€λŠ” 큰 값을 μ΄μš©ν•˜λ©°, 일반적으둜 10βˆ’6^{-6}μ •λ„μ˜ 값을 μ‚¬μš©ν•œλ‹€.

쀑앙차뢄을 κ΅¬ν•˜λŠ” 식은 λ‹€μŒκ³Ό κ°™λ‹€.

fβ€²(x)=lim⁑Δxβ†’0f(x+Ξ”x)βˆ’f(xβˆ’Ξ”x)2Ξ”xf'(x) = \lim_{\Delta x \to 0} \frac{f(x + \Delta x) - f(x - \Delta x)}{2 \Delta x}

이제 이λ₯Ό μ½”λ“œλ‘œ κ΅¬ν˜„ν•΄λ³΄μž.


πŸ“š Numerical Derivative κ΅¬ν˜„

def numerical_derivative(f, x):
    # 쀑앙차뢄을 μ΄μš©ν•œ 미뢄값을 계산
    delta_x = 0.0001

    return (f(x + delta_x) - f(x - delta_x)) / (2 * delta_x)

# f(x) = x^2
def my_func(x):
    return x**2

result = numerical_derivative(my_func, 3)
print(result)

κ΅¬ν˜„μ€ κ·Έλ ‡κ²Œ μ–΄λ ΅μ§€ μ•Šμ€λ°, 쀑앙차뢄 곡식을 κ·ΈλŒ€λ‘œ μ½”λ“œλ‘œ μž‘μ„±ν•˜λ©΄ λœλ‹€. μ΄λ•Œ μ£Όμ˜ν•΄μ•Ό ν•  점으둜 delta_x값인데, 이 값은 Hyper Parameter둜, 값을 μ§€μ •ν•  λ•Œ 신경써야 ν•  뢀뢄이 μžˆλ‹€.


πŸ“š delta_x

delta_x
μ»΄ν“¨ν„°λŠ” κ·Ήν•œμ„ 직접 계산할 수 μ—†κΈ° λ•Œλ¬Έμ— 수치 λ―ΈλΆ„μ—μ„œλŠ” delta_xλ₯Ό 맀우 μž‘μ€ κ°’μœΌλ‘œ μ„€μ •ν•˜μ—¬ 미뢄을 κ·Όμ‚¬ν•œλ‹€. κ·ΈλŸ¬λ‚˜ delta_xλ₯Ό λ„ˆλ¬΄ μž‘κ²Œ μ„€μ •ν•˜λ©΄ 반올림 μ˜€μ°¨κ°€ λ°œμƒν•˜μ—¬ 였히렀 미뢄값이 λΆ€μ •ν™•ν•΄μ§ˆ 수 μžˆλ‹€.

κ·Έλ ‡λ‹€λ©΄ delta_x 값을 μ–΄λ–»κ²Œ μ§€μ •ν•˜λ©΄ μ’‹μ„κΉŒ?
=> μ΄λ•ŒλŠ” ν•¨μˆ˜μ˜ λ³€ν™”λŸ‰μ— 따라 μ§€μ •ν•  수 μžˆλ‹€.

  1. λ³€ν™”λŸ‰μ΄ 큰 경우 (ex. y=sin⁑(1/x)y = \sin(1/x))

delta_xκ°€ 크면 두 점 μ‚¬μ΄μ˜ κΈ°μšΈκΈ°κ°€ μ‹€μ œ μˆœκ°„ λ³€ν™”μœ¨κ³Ό 크게 차이가 λ‚  수 μžˆμœΌλ―€λ‘œ, delta_xλ₯Ό μž‘κ²Œ μ„€μ •ν•˜μ—¬ 두 점을 더 κ°€κΉκ²Œ λ§Œλ“€μ–΄μ•Ό ν•œλ‹€.

ex) y=sin⁑(1/x)y = \sin(1/x)

# λ³€ν™”κ°€ μ‹¬ν•œ ν•¨μˆ˜ μ˜ˆμ‹œ: f(x) = sin(1/x)
def f(x):
    return np.sin(1/x)
# μž‘μ€ delta_x κ°’ (예: 0.00001) μ‚¬μš©
delta_x = 0.00001
# x = 0.1 μ—μ„œμ˜ λ―ΈλΆ„κ°’ 계산
x = 0.1
derivative = (f(x + delta_x) - f(x - delta_x)) / (2 * delta_x)
print(f"x = {x} μ—μ„œμ˜ λ―ΈλΆ„κ°’: {derivative}")
# x = 0.1 μ—μ„œμ˜ λ―ΈλΆ„κ°’: 83.90713432193664
  1. λ³€ν™”λŸ‰μ΄ μž‘μ€ 경우 (ex. y=x2y = x^2)

delta_xκ°€ 크더라도 두 점 μ‚¬μ΄μ˜ κΈ°μšΈκΈ°κ°€ μ‹€μ œ μˆœκ°„ λ³€ν™”μœ¨κ³Ό 크게 차이가 λ‚˜μ§€ μ•ŠμœΌλ―€λ‘œ, delta_xλ₯Ό 크게 섀정해도 비ꡐ적 μ •ν™•ν•œ 미뢄값을 얻을 수 μžˆλ‹€.

ex) y=x2y = x^2

# λ³€ν™”κ°€ μ™„λ§Œν•œ ν•¨μˆ˜ μ˜ˆμ‹œ: f(x) = x^2
def f(x):
    return x**2
# 큰 delta_x κ°’ (예: 0.01) μ‚¬μš©
delta_x = 0.01
# x = 2 μ—μ„œμ˜ λ―ΈλΆ„κ°’ 계산
x = 2
derivative = (f(x + delta_x) - f(x - delta_x)) / (2 * delta_x)
print(f"x = {x} μ—μ„œμ˜ λ―ΈλΆ„κ°’: {derivative}")
# x = 2 μ—μ„œμ˜ λ―ΈλΆ„κ°’: 3.999999999999937

그러면 delta_xλ₯Ό ν•¨μˆ˜μ— 따라 μžλ™μœΌλ‘œ μ„€μ •ν•  순 μ—†μ„κΉŒ?
=> autograd 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ μžλ™μœΌλ‘œ 미뢄을 μ§„ν–‰ν•  수 μžˆλ‹€.

import autograd.numpy as np
from autograd import grad

# μžλ™ 미뢄을 μœ„ν•œ ν•¨μˆ˜ μ •μ˜
def f(x):
    return np.sin(1/x)

# μžλ™ λ―ΈλΆ„ ν•¨μˆ˜ 생성
gradient = grad(f)

# x = 0.1 μ—μ„œμ˜ λ―ΈλΆ„κ°’ 계산
x = 0.1
derivative = gradient(x)

print(f"x = {x} μ—μ„œμ˜ λ―ΈλΆ„κ°’: {derivative}")
x = 0.1 μ—μ„œμ˜ λ―ΈλΆ„κ°’: 83.90715290764523

πŸ“š νŽΈλ―ΈλΆ„ (Partial Derivative)

νŽΈλ―ΈλΆ„?
λ…λ¦½λ³€μˆ˜κ°€ 두 개 이상인 λ‹€λ³€μˆ˜ ν•¨μˆ˜μ—μ„œ 미뢄을 ν•  λ•Œ μ‚¬μš©ν•˜λŠ” 미뢄법

f(x,y)=x2+xy+y2f(x,y) = x^2 + xy + y^2

이와 같은 x, y 두 λ³€μˆ˜λ‘œ 이루어진 ν•¨μˆ˜ fκ°€ μžˆλ‹€κ³  ν•˜μž.
이λ₯Ό x와 y에 λŒ€ν•΄ 각각 λ―ΈλΆ„ν•˜λ©΄,

βˆ‚fβˆ‚x=2x+y\frac{\partial f}{\partial x} = 2x + y
βˆ‚fβˆ‚y=x+2y\frac{\partial f}{\partial y} = x + 2y

이와 같이 λ‚˜νƒ€λ‚Ό 수 μžˆλŠ”λ°, 이λ₯Ό νŽΈλ―ΈλΆ„μ΄λΌκ³  ν•œλ‹€.
이제 이λ₯Ό μ½”λ“œλ‘œ κ΅¬ν˜„ν•΄λ³΄μž.


πŸ“š NumPy iter

def iter(x):
    it = np.nditer(x, flags=['multi_index'])

    while not it.finished:
        print(it.multi_index)

        it.iternext()

iter(np.array([[1, 5, 6, 4], [7, 6, 1, 2]]))
# (0, 0)
# (0, 1)
# (0, 2)
# (0, 3)
# (1, 0)
# (1, 1)
# (1, 2)
# (1, 3)

λ¨Όμ € νŽΈλ―ΈλΆ„μ„ μ‰½κ²Œ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄ NumPy의 nditer ν•¨μˆ˜λ₯Ό μ•Œμ•„λ³΄μž. nditerλ₯Ό μ‚¬μš©ν•  경우, λ‹€μ–‘ν•œ μ°¨μ›μ˜ ndarrayλ₯Ό λ³΅μž‘ν•œ for문이 ν•„μš”μ—†μ΄ μ‰½κ²Œ μš”μ†Œλ₯Ό 탐색할 수 μžˆλ‹€.


πŸ“š Partial Derivative κ΅¬ν˜„

def partial_derivative(f, x):
    # f : 미뢄을 ν•˜λ €λŠ” ν•¨μˆ˜ (λ‹€λ³€μˆ˜ ν•¨μˆ˜)
    # x : λͺ¨λ“  λ³€μˆ˜λ₯Ό ν¬ν•¨ν•˜κ³  μžˆλŠ” ndarray(차원 무관) ex) [1.0, 2.0]

    delta_x = 0.0001
    derivative_x = np.zeros(x.shape) # [0.0, 0.0]

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

    while not it.finished: # 첫번째 λ³€μˆ˜μ—λŒ€ν•œ μ˜ˆμ‹œ
        idx = it.multi_index # 1.0

        tmp = x[idx] # 첫번째 index에 λŒ€ν•œ 값을 μž„μ‹œλ‘œ μ €μž₯ (1.0)

        x[idx] = tmp + delta_x # [1.0001, 2.0]
        fx_plus_delta = f(x) # f(x + delta_x)

        x[idx] = tmp - delta_x # [0.9999, 2.0]
        fx_minus_delta = f(x) # f(x - delta_x)

        derivative_x[idx] = (fx_plus_delta - fx_minus_delta) / (2 * delta_x)

        x[idx] = tmp # 원볡
        
		# 이후 x의 λ‹€μŒ μ›μ†Œμ— λŒ€ν•΄μ„œ λ˜‘κ°™μ΄ 반볡
        it.iternext()

    return derivative_x

μš°μ„  쀑앙차뢄 곡식을 μ‚¬μš©ν•˜μ—¬ κ΅¬ν˜„ν•œ μ½”λ“œμΈλ°, iterλ₯Ό μ‚¬μš©ν•˜μ—¬ μž…λ ₯받은 μ›μ†Œ (μ—¬κΈ°μ„  λ…λ¦½λ³€μˆ˜μ˜ 개수)에 상관없이 λ™μž‘μ΄ κ°€λŠ₯ν•˜λ‹€. 각각의 μ›μ†Œλ§ˆλ‹€ delta_xλ₯Ό λ”ν•œ κ°’κ³Ό λΊ€ 값을 κ΅¬ν•˜μ—¬ 쀑앙차뢄을 μ‚¬μš©ν•΄ 미뢄값을 κ΅¬ν•œ ν›„ derivative_x에 μ €μž₯ν•˜λŠ” λ°©μ‹μœΌλ‘œ λ™μž‘ν•œλ‹€.

κ΅¬ν˜„ν•œ μ½”λ“œλ₯Ό 톡해 κ²°κ³Όλ₯Ό 보자.

# 1λ³€μˆ˜ ν•¨μˆ˜
def my_func1(x): # x^2
    return x**2

result1 = partial_derivative(my_func1, np.array([3.0]))
print(result1) # [6.]

# 2λ³€μˆ˜ ν•¨μˆ˜
def my_func2(input_object): # 2x + 3xy + y^3
    x = input_object[0]
    y = input_object[1]
    return 2*x + 3*x*y + np.power(y, 3) 
    

result2 = partial_derivative(my_func2, np.array([1.0, 2.0]))
print(result2) # [ 8. 15.00000001]

# 3λ³€μˆ˜ ν•¨μˆ˜
def my_func3(input_object): # 2xy + 3xy^2z + 7z^3
    x = input_object[0]
    y = input_object[1]
    z = input_object[2]
    return 2*x*z + 3*x*np.power(y, 2)*z + 7*np.power(z, 3)

result3 = partial_derivative(my_func3, np.array([[1.0], [2.0], [3.0]]))
print(result3)
# [[ 42.        ]
#  [ 36.        ]
#  [203.00000007]]

μ΄λ•Œ λ‹€λ³€μˆ˜ ν•¨μˆ˜λ₯Ό μž‘μ„±ν•  λ•Œ μ „λ‹¬ν•˜λŠ” μ›μ†Œλ₯Ό ndarray둜 전달해 μ£Όλ©΄λœλ‹€.


profile
η²Ύι€² "정성을 κΈ°μšΈμ—¬ λ…Έλ ₯ν•˜κ³  λ§€μ§„ν•œλ‹€"

0개의 λŒ“κΈ€