미분
10분동안 2km를 달렸다고 가정
1분에 0.2km만큼의 속도로 뛰었다고 할 수 있음
10분에 2km를 뛰었다는 것은, 정확하게는 10분 동안의 '평균 속도'를 구한 것임.
미분은 '특정 순간'의 변화량을 뜻함
수식으로 나타내면 아래와 같음
좌변 : f(x)의 x에 대한 미분 (x에 대한 f(x)의 변화량)
결국, x의 '작은 변화'가 함수 f(x)를 얼마나 변화시키느냐를 의미
시간의 작은 변화, h를 한없이 0에 가깝게 한다는 의미로 limit으로 표현
간단히 미분 구현
def numerial_diff(f, x):
h = 10e-50
return (f(x + h) - f(x)) / h
위에는 개선해야 할 점이 2개 있음
h에 가급적 작은 값을 대입하고 싶었기 때문에 h를 무한히 작은 값인 10e-50으로 표현
이는 0이 50개라는 의미
그러나 반올림 오차의 문제를 일으킨다
아래는 반올림 오차 예시
import numpy as np
print(np.float32(1e-50))
결과
0.0
1e-50을 float32형으로 나타내면 0.0이 되어 올바르게 표현할 수가 없다.
너무 작은 값을 이용하면 컴퓨터로 계산하는 데 문제가 발생
이 미세한 값을 10^-4 정도로 이용하면 좋은 결과를 낼 수 있음
또한 f의 차분과 관련된 문제
x + h와 x 사이의 함수 f의 차분을 계산하고 있지만, 애당초 이 계산에는 오차가 있다.
'진정한 미분'은 x 위치의 함수의 기울기(접선)에 해당하지만
구현에서는 미분은 (x+h)와 x사이의 기울기에 해당.
그래서 진정한 미분과 구현의 값은 엄밀히는 일치하지 않음.
이는 오차를 발생시킬 수 있음.
이 오차를 줄이기 위해 (x + h)와 (x - h)일 때의 함수 f의 차분을 계산하는 방법을 쓰기도 함.
x를 중심으로 그 전후의 차본을 계산한다는 의미에서 중심 차분 혹인 중앙 차분이라 함.
다시 구현
def numerial_diff(f, x):
h = 1e-4
return (f(x + h) - f(x - h)) / (2 * h)
아주 작은 차분으로 미분을 구하는 것을 수치 미분
수치 미분의 예
import numpy as np
import matplotlib.pyplot as plt
def numerial_diff(f, x):
h = 1e-4
return (f(x + h) - f(x - h)) / (2 * h)
def function_1(x):
return 0.01 * x ** 2 + 0.1 * x
x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x, y)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
def numerial_diff(f, x):
h = 1e-4
return (f(x + h) - f(x - h)) / (2 * h)
def function_1(x):
return 0.01 * x ** 2 + 0.1 * x
print(numerial_diff(function_1, 5))
print(numerial_diff(function_1, 10))
결과
0.1999999999990898
0.2999999999986347
위 계산한 미분 값이 x에 대한 f(x)의 변화량 → 함수의 기울기에 해당.
수치 미분 값을 기울기로 하는 직선
import numpy as np
import matplotlib.pylab as plt
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
def function_1(x):
return 0.01*x**2 + 0.1*x
def tangent_line(f, x):
d = numerical_diff(f, x)
print(d)
y = f(x) - d*x
return lambda t: d*t + y
x = np.arange(0.0, 20.0, 0.1)
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()
편미분
아래 변수 2개인 함수가 있다면
아래와 같이 함수 구현 가능
def function_2(x):
return x[0] ** 2 + x[1] ** 2
x는 넘파이 배열이라 가정
위 식은 아래와 같은 3차원 그래프와 같은 형태로 나올 수 있다.
# 코드 참고 : https://jangpiano-science.tistory.com/114
import numpy as np
import matplotlib.pyplot as plt
def function_1(x):
return x[0]**2 +x[1]**2
X = np.arange(-3, 4, 0.1)
Y = np.arange(-3, 4, 0.1)
X, Y = np.meshgrid(X, Y)
Z = function_1(np.array([X, Y]))
fig = plt.figure()
ax = fig.gca(projection ="3d")
surf = ax.plot_wireframe(X, Y, Z , alpha = 0.5)
plt.xlabel("x0")
plt.ylabel("x1")
plt.show()
위와 같은 함수에서 미분을 구한다고 해봄
주의할 점은 변수가 2개라는 것.
따라서 '어느 변수에 대한 미분이냐', 즉 x_0과 x_1 중 어느 변수에 대한 미분이냐를 구별해야 한다.
이와 같이 변수가 여럿인 함수에 대한 미분을 편미분
편미분 수식은 아래와 같이 표현 가능
코드로 구현
x_0이 3이고 x_1 = 4일 때, x_0에 대한 편미분
def numerial_diff(f, x):
h = 1e-4
return (f(x + h) - f(x - h)) / (2 * h)
def function_tmp1(x0):
return x0 * x0 + 4.0 ** 2.0
print(numerial_diff(function_tmp1, 3.0))
출력
6.00000000000378
x_0이 3이고 x_1 = 4일 때, x_1에 대한 편미분
def numerial_diff(f, x):
h = 1e-4
return (f(x + h) - f(x - h)) / (2 * h)
def function_tmp2(x1):
return 3.0 * 2.0 + x1 * x1
print(numerial_diff(function_tmp2, 4.0))
출력
7.999999999999119
이런 편미분 문제는 변수가 하나인 함수를 정의하고, 그 함수에 대한 미분을 구하는 형태로 구현
편미분은 변수가 하나인 미분과 마찬가지로 특정 장소의 기울기를 구한다.
단, 여러 변수 중 목표 변수 하나에 초점을 맞추고 다른 변수는 값을 고정
기울기 계산
위에서는 x_0과 x_1의 편미분을 변수별로 따로 계산.
그럼 x_0과 x_1의 편미분을 동시에 계산하고 싶다면
각 변수의 편미분을 벡터로 정의 한다.
이 벡터를 기울기라고 표현
기울기를 코드로 구현
import numpy as np
def function_2(x):
return x[0] ** 2 + x[1] ** 2
def numerical_gradient(f, x):
h = 1e-4
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
# f(x+h) 계산
x[idx] = tmp_val + h # 편미분 구할 변수의 변화량만 검사
fxh1 = f(x)
# f(x-h) 계산
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val # 값 복원
return grad
print(numerical_gradient(function_2, np.array([3.0, 4.0]))) # (3, 4)에서의 기울기
print(numerical_gradient(function_2, np.array([0.0, 2.0]))) # (0, 2)에서의 기울기
print(numerical_gradient(function_2, np.array([3.0, 0.0]))) # (3, 0)에서의 기울기
결과
[6. 8.]
[0. 4.]
[6. 0.]
기울기가 의미하는 것
아래의 코드로 구현한 그래프 참고
# coding: utf-8
# cf.http://d.hatena.ne.jp/white_wheels/20100327/p3
import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
def _numerical_gradient_no_batch(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x) # x와 형상이 같은 배열을 생성
for idx in range(x.size):
tmp_val = x[idx]
# f(x+h) 계산
x[idx] = float(tmp_val) + h
fxh1 = f(x)
# f(x-h) 계산
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val # 값 복원
return grad
def numerical_gradient(f, X):
if X.ndim == 1:
return _numerical_gradient_no_batch(f, X)
else:
grad = np.zeros_like(X)
for idx, x in enumerate(X):
grad[idx] = _numerical_gradient_no_batch(f, x)
return grad
def function_2(x):
if x.ndim == 1:
return np.sum(x**2)
else:
return np.sum(x**2, axis=1)
def tangent_line(f, x):
d = numerical_gradient(f, x)
print(d)
y = f(x) - d*x
return lambda t: d*t + y
if __name__ == '__main__':
x0 = np.arange(-2, 2.5, 0.25)
x1 = np.arange(-2, 2.5, 0.25)
X, Y = np.meshgrid(x0, x1)
X = X.flatten()
Y = Y.flatten()
grad = numerical_gradient(function_2, np.array([X, Y]) )
plt.figure()
plt.quiver(X, Y, -grad[0], -grad[1], angles="xy",color="#666666")#,headwidth=10,scale=40,color="#444444")
plt.xlim([-2, 2])
plt.ylim([-2, 2])
plt.xlabel('x0')
plt.ylabel('x1')
plt.grid()
plt.legend()
plt.draw()
plt.show()
기울기 그림은 위 그림처럼 방향을 가진 벡터로 그려진다.
그림을 보면 기울기는 함수의 '가장 낮은 장소(최솟값)'을 가리키는 것처럼 보임
마치, 나침반처럼 화살표들은 한 점을 향하고 있다.
또한 '가장 낮은 곳에서 멀어질수록' 화살표의 크기가 커짐을 알 수 있다.
하지만 실제로는 항상 가장 낮은 장소를 가리킨다고 볼 수 없다.
각 지점에서 낮아지는 방향을 가리킨다고 표현하는 것이 더 옳다.
더 정확히 말하면 기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 줄이는 방향
내용 참고
book.naver.com/bookdb/book_detail.nhn?bid=11492334
'딥러닝관련 > 기초 이론' 카테고리의 다른 글
기초 행렬 이론 정리 (0) | 2021.05.27 |
---|---|
신경망 정리 6 (경사법, 경사 하강법) (0) | 2021.05.24 |
신경망 정리 4 (손실 함수 설정) (0) | 2021.05.23 |
신경망 정리 3 (신경망 학습, MSE, Cross entropy loss ....) (0) | 2021.05.12 |
MNIST 인식(밑바닥부터 시작하는 딥러닝) (0) | 2021.05.04 |