데이터분석의 목표는 어떤 데이터의 예측을 위한 최적의 예측 모형을 찾는 것이다.
최적의 예측 모형은 모수를 잘 선택하여 모형의 성능을 높이는 , 즉 최적화하는 과정과 같다.- 예를들어 어떤 데이터에 대한 선형예측모형의 예측값은 가중치 w와 데이터의 변수 x의 선형조합으로 만들어진다.
- 이 때에 가중치 w가 모수(=계수)가 되며 모수를 어떤 것을 선택하느냐에 따라서 모형의 성능이 달라지게 된다. 모형의 성능은 크기를 비교하기 위한 값이므로 스칼라가 되어야 한다.
- 모수 선택에서 모형의 성능 측정까지의 과정은 다변수함수를 계산하는 과정과 같다. 이렇듯 예측모형의 성능을 높이기 위한 함수를 성능함수 performance function 라고 한다. 성능함수의 값은 클 수록 좋다.
- 반대로 모형의 성능값이 아닌 모형의 예측값과 목표값을 비교하여 얼마나 차이가 있는지 오류를 따져 이 오류를 작게하는 함수로 손실함수 loss function, 비용함수 cost function, 오류함수 error functiion 이 있다.
- 이렇듯 최적화의 대상이 되는 함수들, 성능함수, 손실함수, 비용함수, 오류함수를 목적함수라고 한다. 목적함수를 선택한 후에는 모수를 조절함으로써 목적함수를 크게 또는 작게할 수 있다.
- 목적함수는 모수 w 를 입력받아 성능 또는 손실 값을 출력해주며, 입력변수 인 w를 조정하면 출력값도 변하게 된다. 입력변수의 변화에 따라 출력값의 변화하는 비율을 보여주는 것을 미분 diffrentiation이라고 한다.
즉 데이터 분석에서의 미분의 의미는
, 최적의 예측모형을 판단하는 기준으로 모형의 성능을 높이기위하여 적절한 목적함수를 가지고 입력변수로 가중치 w를 받아 조절함으로써 이와 함께 변하는 성능(손실)인 출력값과의 변화 비율을 나타내는 일종의 신호와 같다.
분석할 데이터 -> 목적함수 선택 -> 입력변수 가중치 w 조절 -> 변화하는 출력값 -> 미분 적용 -> 모수 w의 변화와 성능(손실) 출력값의 변화에 대한 변화율 확인 -> 모형의 성능 확인 -> 성능이 좋은 것 또는 오류가 작은 모형 선택 -> 최적의 예측 모형 도출
- 이러한 과정을 최적화 optimization 이라고 한다.
- 주성분 분석 즉 PCA 의 최적화 과정을 떠올려보면, 원래 행렬과 역변환 행렬을 사용하여 만든 행렬의 차를 가장 작게 하는 문제가 되었다.
- 최적화의 대상이 된 목적함수를 최소화하기 위한 차원축소 벡터 를 찾는 문제였다. 이를 위해 목적함수를 미분하여 얻은 값이 영벡터가 되는 값 즉 역변환 행렬과 변환 행렬의 관계를 푸는 문제가 되었다.
- 처음의 최적화 식에 위의 값을 대입하고 모든 벡터에 대해 적용하면, 최적의 변환 행렬 W 를 찾는 문제인 랭크-k 근사 문제가 되며, W 는 가장 큰 k 번쨰까지의 특잇값에 해당하는 오른쪽특이벡터임을 알 수 있었다.
- 즉 PCA 과정에서 사용된 목적함수와 미분은, 모수의 변화에 대한 예측모형의 성능값의 변화율을 확인하여 최적의 모형을 만드는 모수 W를 찾는 역할을 했다는 것을 알 수 있다.
기울기
: 어떤 함수관계에서의 입력변수 x의 변화에 따른 출력변수 y값의 변화율 정보, 민감도 sensitivity 라고도 한다.- 고장난 라디오의 음량 조절나사의 각도를 x라고 할때 출력되는 음량값을 y라고 할 수 있다. 일상적으로 조절나사를 좌우로 잘 돌리다보면, 어떤 지점에서 소리가 커지는지 경험적으로 파악할 수 있다.
음량이 최대가 되는 조절나사의 각도를 찾는 문제는 변수 x에 대한 최적화문제라고 할 수 있다.
수치적 최적화
는 가장 적은 방법을 시도하여 가장 큰 음량일 때의 최적의 x를 찾는 것을 의미한다. 가장 적은 횟수로 최적의 x를 찾으려면 다음과 같은 정보를 따라야한다.- x1의 위치에서 각도를 증가시켰을 때 음량 y가 커졌다면, 다음 시도인 x2는 x1보다 큰 값이어야 가장 큰 y값에 다가간다.
- x1의 위치에서 각도를 증가시켰을 때 음량 y가 작아졌다면, 다음 시도인 x2는 x1보다 작은 값이어야 가장 큰 y값에 다가간다.
- 즉 기울기는 음량이 가장 큰 y값을 찾기 위해 조절나사의 각도 x를 x1, x2, x3, ... 최소한의 횟수로 변화시키려고 할 때, x1의 다음값을 어떻게 설정하면 좋을지에 대한 정보를 제공해준다.
- 입력변수 x의 변화(x2-x)에 대한 출력변수의 변화(f(x2)-f(x))의 비율
- 그래프에서의 기울기는 x의 변화량인 가 0으로 근접해 갈 때의 변화율이다.
- 한 점에서의 접선의 기울기는 x의 변화량에 따라 달라질 수 있다. x2, x3, x4 과의 tangent 값이 달라지기 때문이다. 따라서 기울기를 통일해 주기 위해, x의 변화량을 무한대로 0에 가깝게 만드는 방식을 사용.
- 기울기를 정확하게 구할 수 있는 방법
- scipy.misc 패키지의 derivative() 명령어를 사용하면 기울기를 구할 수 있다. 정확한 값은 아니다.
- 인수로 함수 f, 좌표 x, 이동할 거리 dx 가 사용된다. dx는 작을 수록 좋지만, 너무 작으면 부동소수점 연산의 오버플로우 오류가 발생하여 역으로 오차가 증폭할 수 있으므로 주의해야한다.
부동소수점 연산의 오버플로우
: 정수는 이진법으로 표현 가능하지만, 소수는 어렵다. 소수는 원래 무한대의 수이다. 파이썬은 소수를 17 자리까지만 나타내준다. 모든 수는 이러한 오차가 있다고 봐야한다.
- 미분 : 어떤 함수로부터 그 함수의 기울기를 출력하는 새로운 함수를 만드는 작업이다. 미분은 동사이다.
- 새로운 함수 = 기울기 출력 함수
- 미분의 표기법
- 모든 함수가 미분 가능한 것은 아니다. 어떤 경우는 미분을 할 수 없는 경우도 있다.
- 미분을 할 수 없는 경우는 미분 불가능 이라고 하고, 미분이 가능한 경우는 미분 가능 이라고 한다.
- ReLU 함수에서 x=0 인 경우는 미분 불가능에 해당한다.
- x >0 이면 기울기는 0 이지만, x < 0 이면 기울기가 1 이다. x = 0 인 경우는 기울기를 구할 수 없다.
-
몇 가지 미분공식을 조합하여 복잡한 함수의 도함수를 구할 수 있다.
- 기본미분공식
상수미분, 거듭제곱미분, 지수미분, 로그미분
- 선형조합법칙, 곱셈법칙, 연쇄법칙
-
기본미분공식
-
선형조합법칙
-
곱셈법칙
-
연쇄법칙 chain rule
-
미분하고자 하는 함수의 입력변수가 다른함수의 출력변수인 경우에 적용할 수 있다.
-
함수가 복잡할 경우에는 f, h, g, y 등의 중간변수를 만들고 함수의 관계에 따라서 구분지은 후 연쇄법칙을 적용한다.
도함수의 기울기
- 도함수를 미분하여 만든 도함수를 말한다. 함수 f의 기울기 출력함수인 도함수를 다시 미분하여 도함수의 기울기를 출력해주는 함수이다.
- 함수, 도함수, 2차 도함수의 관계 (오목과 볼록의 기준은 아래에서 올려다본 시점, 오목은 봉우리, 볼록은 계곡)
- 함수가 오목 concave -> 도함수값 감소 -> 2차 도함수값 음수
- 함수가 볼록 convex -> 도함수값 증가 -> 2차 도함수값 양수
2차 도함수값을 볼록도 convexity 라고도 부른다.
- 다변수 함수의 미분
- 다변수이기때문에 변수 각각에 대해서 따로 미분이 가능하다. 따라서 하나의 함수에서 여러개의 도함수가 나올 수 있다.
- 어떤 하나의 독립변수에 대해 미분을 할 때는 다른 독립변수를 상수 처럼 취급하여 계산한다.
- 다변수함수가 연결 되어 있을때에도 연결법칙을 사용하여 미분할 수 있다.
- N개의 함수 f1, f2, ..., fN 가 입력변수 x를 가질때 출력변수 y1, y2, ..., yN 라고 한다. yN 을 입력받는 함수 g와 출력 z라고 한다. 이때 변수 x값의 변환에 따른 z의 값의 변화
- N개의 함수 f1, f2, ..., fN이 x1, x2, ..., xM 개의 입력변수를 갖는 다변수함수라고 할 때, 변수 x1값의 변화에 따른 z의 변화
- 편미분에 대한 2차 도함수. 즉 다변수 함수의 도함수의 도함수.
- 2차 편미분 방법은 1차 편미분을 하고, 그 결과를 가지고 다시 편미분을 한다. 이 과정에서 여러개의 변수 중 어떤 것을 선택할지는 자유롭게 정할 수 있다.
- 순서데로 선택한 변수를 아래첨자를 써서 표기한다.
슈와르츠 정리
- 연속 함수이고, 미분 가능한 함수인 경우 미분의 순서가 바뀌어도 결과는 같다.
- 변수가 여러개이고 동시에 함수들이 서로 연결되어 있는 경우는 1차, 2차 편미분 과정에서 연쇄법칙과 곱셈법칙이 여러번 사용될 수 있다.
- 심파이는 심볼릭연산 symbolic operation 을 지원하는 파이썬의 패키지이다. 심볼릭 연산은 사람이 손으로 미분을 계산하는 것과 같은 방식으로 연산하는 기능을 말한다.
- 일반적으로 파이썬에서 연산을 하려면 변수 x 에 값을 저장하여 변수선언을 해주어야 한다. 그러나 심볼릭 연산은 변수선언 없이도 x 를 심볼화하여 숫자계산 뿐만 복잡한 미분과 적분의 연산도 처리해준다.
- 딥러닝 deep learning 등에서 사용되는 텐서플로 패키지나 파이토치 패키지에서도 심볼릭 연산을 지원한다.
- 데이터 분석에서는 심파이를 사용하여 미분, 적분을 한다. 손으로 계산하는 경우는 특별히 어떤 공식을 유도할 때만 한다.
def f(x) :
return x ** 3 - 3 * x ** 2 + x
x = np.linspace(-1, 3, 400)
y = f(x)
plt.plot(x, y)
plt.plot(0, 0, 'ro')
plt.plot(x, x, 'r:')
plt.plot(1, -1, 'go')
plt.plot(x, (3*1**2-6*1+1)*(x-1)-1, 'g--')
plt.xlim(-3.5, 5.5)
plt.ylim(-4, 2)
plt.xticks(np.arange(-3, 6))
plt.yticks(np.arange(-4, 2))
# 주석 설정 : xy 는 주석의, xytext 는 주석의 끝점, arrowprops 는 화살표의 속성
plt.annotate('', xy=(1, 0), xytext=(0, 0), arrowprops=dict(facecolor='gray'))
plt.annotate('', xy=(1, 1), xytext=(1, 0), arrowprops=dict(facecolor='gray'))
plt.annotate('', xy=(2, -1), xytext=(1, -1), arrowprops=dict(facecolor='gray'))
plt.annotate('', xy=(2, -3), xytext=(2, -1), arrowprops=dict(facecolor='gray'))
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('함수의 기울기')
plt.show()
- 함수의 기울기를 구해준다.
from scipy.misc import derivative
def f(x) :
return x ** 3 - 3 * x ** 2 + x
x = [-.5, 0, 0.5, 1, 1.5, 2, 2.5]
f_derivative = []
for i in range(7) :
print("x={} 의 기울기 slope={}".format(x[i], derivative(f, x[i], dx=1e-5)))
f_derivative.append(derivative(f, x[i], dx=1e-5))
plt.plot(x, f_derivative)
plt.axhline(0, c='k', ls='--', linewidth=0.7)
plt.axvline(0, c='k', ls='--', linewidth=0.7)
for j in range(7) :
plt.plot(x[j], f_derivative[j], 'ro', ms=3, label='x={} → {:.0f}'.format(x[j], f_derivative[j]))
plt.title('함수 $x^3 - 3x^2 + x$의 도함수 그래프', y=1.04)
plt.legend(fontsize=8)
plt.show()
=====print=====
x=-0.5 의 기울기 slope=4.750000000097732
x=0 의 기울기 slope=1.0000000000999998
x=0.5 의 기울기 slope=-1.2499999998943911
x=1 의 기울기 slope=-1.9999999998965288
x=1.5 의 기울기 slope=-1.24999999986386
x=2 의 기울기 slope=1.000000000139778
x=2.5 의 기울기 slope=4.7500000000422204
- 미분 할 수 없는 경우는 미분 불가능
- 미불 할 수 있는 경우는 미분 가능
def relu(x) :
return np.where(x > 0, x, 0)
xx = np.linspace(-2, 2, 100)
plt.plot(xx, relu(xx))
plt.plot(0, 0, 'go', ms=10)
plt.text(0.2, 0.1, '렐루 함수는 x=0 에서 미분 불가능', fontsize=8)
plt.title('ReLU')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
- 도함수는 함수의 기울기를 출력해주는 함수이다.
- 미분이라는 작업, 행위를 통해서 만들어진다.
def f(x) :
return x ** 3 - 3 * x ** 2 + x
def fprime(x) :
return 3 * x ** 2 - 6 * x + 1
# 2차 방정식의 근 : 도함수의 근
# np.roots([n, n-1, n-2, ..., 0]) : 명령어의 인수는 제곱수의 순서대로 상수항의 값을 입력한다. 없는 제곱수는 0으로 입력해야한다.
x1, x2 = np.roots([3, -6, 1])
x_1, x_2, x_3 = np.roots([1, -3, 1, 0])
x = np.linspace(-1, 3, 400)
plt.figure(figsize=(10, 7))
plt.subplot(211)
plt.plot(x, f(x))
# 3차 함수의 근의 표시
plt.plot(x_1, 0, 'ro', ms=4)
plt.plot(x_2, 0, 'ro', ms=4)
plt.plot(x_3, 0, 'ro', ms=4)
plt.xlim(-2, 4)
plt.xticks(np.arange(-1, 4))
plt.yticks(np.arange(-5, 4))
plt.xlabel('x')
plt.title('함수 f(x)', y=1.06)
# 3차 함수의 최고점과 최저점은 도함수의 근과 같다.
plt.axvline(x1, c='b', ls='--')
plt.axvline(x2, c='b', ls='--')
plt.subplot(212)
plt.plot(x, fprime(x))
plt.xlim(-2, 4)
# 2차 함수의 근의 표시
plt.plot(x1, 0, 'go', ms=4)
plt.plot(x2, 0, 'go', ms=4)
plt.xticks(np.arange(-1, 4))
plt.yticks(np.arange(-3, 11))
plt.xlabel('x')
plt.ylabel('도함수 $f^{\prime}(x)$')
plt.axhline(0, c='r', ls='--')
plt.axvline(x1, c='b', ls='--')
plt.axvline(x2, c='b', ls='--')
plt.tight_layout()
plt.show()
- 상수항을 순서대로 인수로 입력 : np.roots([a, b, c, d, e])
- 0이어도 입력해줘야 함수가 몇 차인지 인식한다.
x1, x2 = np.roots([2, 4, 1])
x1, x2
=====print=====
(-1.7071067811865475, -0.2928932188134525)
x_1, x_2, x_3 = np.roots([1, -3, 1, 0])
x_1, x_2, x_3
=====print======
(2.618033988749895, 0.38196601125010515, 0.0)
def f(x) :
return x ** 3 - 3 * x ** 2 + x
def fprime(x) :
return 3 * x ** 2 - 6 * x + 1
def fprime2(x) :
return 6 * x - 6
# fprime(x) 의 근
x1, x2 = np.roots([3, -6, 1])
x = np.linspace(-1, 3, 400)
plt.figure(figsize=(10, 10))
plt.subplot(311)
plt.plot(x, f(x))
plt.plot()
plt.xlim(-2, 4)
plt.xticks(np.arange(-1, 4))
plt.yticks(np.arange(-5, 4))
plt.title('함수 f(x)')
plt.xlabel('x')
plt.axvline(x1, c='b', ls='--')
plt.axvline(x2, c='b', ls='--')
plt.axvline(1, c='g', ls=':')
plt.subplot(312)
plt.plot(x, fprime(x))
plt.xlim(-2, 4)
plt.xticks(np.arange(-1, 4))
plt.yticks(np.arange(-3, 11))
plt.title('함수 $f^{\prime}(x)$')
plt.xlabel('x')
plt.axhline(0, c='r', ls='--')
plt.axvline(x1, c='r', ls='--')
plt.axvline(x2, c='r', ls='--')
plt.axvline(1, c='g', ls=':')
plt.subplot(313)
plt.plot(x, fprime2(x))
plt.xlim(-2, 4)
plt.xticks(np.arange(-1, 4))
plt.title('2차 도함수 $f^{\prime\prime}(x)$')
plt.axhline(0, c='r', ls='--')
plt.axvline(1, c='g', ls=':')
plt.tight_layout()
plt.show()
- SymPy 는 심볼릭 연산 symbolic operation 을 지원하는 파이썬 패키지이다.
- 심볼릭 연산이란 연필로 계산을 하는 것과 같은 방식의 미분/적분을 말한다.
- 딥러닝 등에 많이 사용되는 파이썬의 텐서플로 패키지나 파이토치 패키지도 심볼릭 연산 기능을 갖추고 있다.
import sympy
# 쥬피터 노트북에서 수학식의 LaTeX 의 표션을 위해 필요한 설정
sympy.init_printing(use_latex='mathjax')
# x 를 심볼로 정의
x = sympy.symbols('x')
x
=====print=====
# 심볼 x 로 함수를 정의
f = x * sympy.exp(x)
f
=====print=====
# diff() 명령어로 미분
sympy.diff(f)
=====print=====
# simplify() 명령어는 함수를 소인수 분해하여 정리해준다.
sympy.simplify(sympy.diff(f))
=====print=====
- diff() 명령어의 인수로 미분할 변수를 넣어준다.
# 심볼 설정
x, y = sympy.symbols('x y')
# 함수 설정
f = x ** 2 + 4 * x * y + 4 * y ** 2
# 변수 x 로 편미분
f_partial_diff_x = sympy.diff(f, x)
f_partial_diff_x
=====print=====
# 변수 y 로 편미분
f_partial_diff_y = sympy.diff(f, y)
f_partial_diff_y
=====print=====
- 심파이가 상수 심볼과 변수 심볼을 구분하지 못하기 때문에 편미분의 방식처럼 diff() 명령어의 인수로 미분할 변수를 지정해 주어야 한다.
# 심볼 설정
x, mu, sigma = sympy.symbols('x, mu, sigma')
# 함수 설정
f = sympy.exp((x-mu) ** 2/ sigma ** 2)
# x 변수로 미분
sympy.diff(f, x)
=====print=====
x = sympy.symbols('x')
f = x ** 3 - 1
f_diff = sympy.diff(f, x)
f_diff_diff = sympy.diff(f, x, x)
f, f_diff, f_diff_diff
=====print=====
x, k = sympy.symbols('x k')
f = sympy.log(x ** 2 - 3 * k)
f_diff = sympy.diff(f, x)
f_diff_diff = sympy.diff(f, x, x)
f, f_diff, f_diff_diff
=====print=====
x, a, b = sympy.symbols('x, a, b')
f = sympy.exp(a * (x ** b))
f_diff = sympy.simplify(sympy.diff(f, x))
f_diff_diff = sympy.simplify(sympy.diff(f, x, x))
f, f_diff, f_diff_diff
=====print=====
x, y = sympy.symbols('x, y')
f = sympy.exp(x ** 2 + 2 * y ** 2)
f_diff_x = sympy.diff(f, x)
f_diff_y = sympy.diff(x, y)
f_diff_x_x = sympy.diff(f, x, x)
f_diff_x_y = sympy.diff(f, x, y)
f_diff_y_y = sympy.diff(f, y, y)
f_diff_y_x = sympy.diff(f, y, x)
f_diff_x, f_diff_y, f_diff_x_x, f_diff_x_y, f_diff_y_y, f_diff_y_x
=====print=====