헉... 파이썬에 무료로 미분을 해주는 라이브러리가 있었다.
우선 아래 예제를 "sympy" 라는 라이브러리로 미분해보자
import sympy
x = sympy.Symbol('x')
f = (x**2 + 2*x) * sympy.log(x)
df = sympy.diff(f, x)
df
sympy.simplify(df) //df결과를 심플하게 정리해준다
## >>>> x + 2*(x+1)*log(x) + 2
깔끔하진 않지만, 의도한 결과 가 나왔다.
이를 x=1을 대입하여 미분계수를 구하면
import numpy as np
f = lambda x : (x**2 + 2*x)*np.log(x)
df = lambda x : 2 * (x + 1)*np.log(x) + (x + 2) //도함soo
print(f(1))
>> 0.0
print(df(1))
>> 3.0
근데, 이렇게 손으로 써주지 않고도 그냥 미분계수만 필요한 경우 '수치미분' 방식으로 결과값만 구할 수 있다.
수치미분은 독립변수의 변화 로 종속변수의 변화 를 실제로 계산하고 이 둘을 나눠서 특정 점에서 미분계수를 근사하는 방식이다.
그러나, 수치미분을 통해 계산된 값은 근사치이다. 근사치로 인한 오차를 '절단오차(truncation error)'라 하는데, 수치미분 방법의 근사식에 의한 근본적인 한계이다.
이를 해결하기 위해, 의 변화를 더더욱 작게 줄이는 과정에서 컴퓨터가 가진 수치적 한계로 인해 '반올림 오차가(round-off error)'가 발생하게 된다.
수치미분은 전방 차분법과 중앙 차분법이란 근사식을 통해 계산하게 된다.
1) 전방 차분법
2) 중앙 차분법
파이썬 코드로 구현해보자.
## 전방 차분법과 중앙 차분법
import sympy
import numpy as np
def numer_deriv(f, x, h=0.001, method="center"):
"""
{f(x+h) -f(x)} / h 를 수치적으로 계산한다.
f : 미분할 함수로 주어진 위치에서 함숫값 계산을 위해 사용
x : 미분계수를 구할 변수의 위치로
일변수인 경우 int 또는 float
다변수인 경우 넘파이 어레이(d,) 벡터
h : 비율을 구할 작은 구간
"""
if type(x) in (float, int) :
grad = [0.0]
x_ = [x]
var_type = 'scalar'
else :
grad = np.zeros(x.shape)
x_ = x.copy().astype('float32')
var_type = 'vector'
for i, xi in enumerate(x_) :
original_value = x_[i]
if method == 'forward' :
x_[i] = original_value + h
else :
x_[i] = original_value + (h/2)
if var_type == 'scalar' :
gradplus = f(x_[i])
else :
gradplus = f(x_)
if method == 'forward' :
x_[i] = original_value
else :
x_[i] = original_value - (h/2)
if var_type == 'scalar' :
gradminus = f(x_[i])
else :
gradminus = f(x_)
grad[i] = (gradplus - gradminus) / h
if var_type == 'scalar' :
return grad[0]
else :
return grad
f = lambda x : (x**2 + 2*x) * np.log(x)
print(numer_deriv(f, 1))
>> 2.999999999999666
print(numer_deriv(f, 1, h=0.5, method="forward"))
>> 4.257383635135726
print(numer_deriv(f, 1, h=0.5, method="center"))
>> 2.9997299032915508
numer_deriv 함수 자체는, 전방 차분법과 중앙 차분법을 입력값에 따라 나눠 계산하는 함수다.
print로 출력한 첫번째 값이 f 함수에 직접 1을 대입하여 계산한 값이고
두번째가 전방 차분법으로 계산한 근사값, 세번째가 중앙 차분법으로 계산한 근사값이다.
중앙차분법이 실제 미분계수와 거의 유사(하지만 차이가 있다)하다. 비교적 전방 차분법보다 오차가 작다.
위 함수를 통해서 다변수 함수의 편미분계수도 array로 계산할 수 있게 된다.
f_xy = lambda x : (x[0]**2 +2* x[0])*np.log(x[1])
numer_deriv(f_xy, np.array([1, 2]))
>> array([2.77255299, 1.49889143])
위 결과를 sympy로 직접 편미분해보면
편미분계수는 각각 2.7726, 1.5 로 수치미분(중앙차분법)의 값이 실제 편미분계수과의 오차가 현저히 줄어든 것을 확인할 수 있다.
수치미분(결국, 중앙 차분법)을 통한 수치미분은 직접 미분을 하기 힘들더라도 미분계수가 필요한 경우, 가장 먼저 선택할 수 있는 방법이다.
그러나, 루프문을 통해 구현되므로 인공지능 분야에서 한두개의 변수가 아닌 수십, 수백, 수천개의 변수를 루프문으로 구현하기에는 속도 이슈가 발생하게 된다.
따라서, 수치미분은 인공지능 분야에선 '자동미분'을 통한 결과값을 검증할 때 사용한다.
-- 자동미분에서 계속 --