저번시간에 입력이 2차원인 합성함수의 도함수를 구하였다.
1. 다시 복습을 해보자.
입력 X, W는 아래와 같다.
g(X, W)함수를 아래와 같이 정의한다.
σ(X) 함수를 아래와 같이 정의한다.
h(X) 함수를 아래와 같이 정의한다.
합성함수 f(X, W) = h(σ(g(X, W)))를 정의한다. 합성함수의 정의와 연산의 결과는 아래와 같다.
이 때 도함수 ∂f/∂X 는 아래와 같이 성립한다고 유도하였다.
∂f/∂W 는 아래와 같다.
이번 실습은 ∂f/∂X 으로 진행한다.
2. 합성함수의 도함수 계산이 맞는지 직접 계산하여 확인해보기
위의 식을 numpy를 이용하여 적은 것이다. f(X, W)는 forward 함수, ∂f/∂X는 backward 함수로 정의하였다.
import numpy as np
# 직접 계산하여 합성함수의 도함수 계산이 맞는지 확인해보기
X = np.array([[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9]])
W = np.array([[1, 2],
[3, 4],
[5, 6]])
X2 = np.array([[0.1001, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9]])
def f_forward(X, W):
#g(X, W) = X × W
g_XW = np.dot(X, W)
#σ(x) = 1 / (1 + exp^(-x))
s_XW = 1 / (1 + np.exp(-g_XW))
#h(x) = Σx
h_XW = np.sum(s_XW)
return h_XW
def deriv(func, input, delta=0.001):
return (func(input + delta) - func(input)) / delta
def sigmoid(x):
return 1 / (1+np.exp(-x))
def f_backward(X, W, sig):
#g(X, W) = X × W, (3, 2) shape
g_XW = np.dot(X, W)
#dσ(g(X, W))(/dg(X, W)
dsdg = deriv(sig, g_XW)
#dg(X, W)/dX
dgdx = np.transpose(W, (1, 0))
#dh(σ(g(X, W)))/dX
dfdx = np.dot(dsdg, dgdx)
return dfdx
print('X : ', X)
print('f(X) : ', f_forward(X, W))
print('df(x)/dX : ', f_backward(X, W, sigmoid))
print(' ')
print('X2 : ', X2)
print('f(X2) : ', f_forward(X2, W))
"""
<출력 결과>
f(x) : 5.833329393581208
df(x)/dX : 0.197792792
f(x2) : 5.833349179920838
delta_x = 0.0001
f(x2) - f(x) = 0.000019786
df(x)/dX * delta_x = 0.0000197792
"""
출력결과를 해석하겠다.
1. x(11)의 변화를 0.0001로 더해준 것을 X2로 정의한다.
2. backward 계산으로 X에서의 기울기를 구한다. 이를 ∂f/∂X 라고 하자.
3. f(X2, W) - f(X, W) = ∂f/∂X * deltaX 와 최대한 같아야 식이 올바른 것이다.
4. f(x2) - f(x) = 0.000019786
5. ∂f/∂X * deltaX = 0.197792792 * 0.0001 = 0.0000197792
4번의 결과와 5번의 결과를 비교해 보자. 거의 같다. 그렇다면 식이 올바르다고 볼 수 있다.
3. pytorch를 이용하여 직접 확인해 보기
pytorch로 식을 계산하여 기울기를 구했을 때
2에서 구한 기울기 ∂f/∂X = 0.197792792 와 최대한 비슷하거나 같다면
도함수를 올바르게 유도했다고 볼 수 있을 것이다. 아래는 pytorch로 표현한 코드이다.
(reference : https://wikidocs.net/60754)
import numpy as np
import torch
# 파이토치로 계산해보기
X = np.array([[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9]])
W = np.array([[1., 2.],
[3., 4.],
[5., 6.]])
X = torch.tensor(X, requires_grad = True)
W = torch.tensor(W, requires_grad=False)
#g(X, W) = X × W
g_XW = torch.matmul(X, W)
#σ(x) = 1 / (1 + exp^(-x))
s_XW = 1 / (1 + torch.exp(-g_XW))
#h(x) = Σx
h_XW = torch.sum(s_XW)
h_XW.backward()
"""
print(X.grad)
tensor([[1.9788e-01, 4.8555e-01, 7.7323e-01],
[1.0649e-02, 2.8635e-02, 4.6621e-02],
[5.9074e-04, 1.6814e-03, 2.7721e-03]], dtype=torch.float64)
"""
forward 연산을 하고 최종 출력값인 f(X, W) = h_XW를 구한다.
여기서 X의 requires_grad = True로 설정하여 기울기를 저장해 놓았다. 즉 변수 X에 대한 변화를 기록하는 것이다!
여기서 W의 변화는 관심없으므로 False로 설정함.
최종출력(h_XW)에 대해 backward()를 시행하면 위 함수에 대한 도함수를 구한다.
마지막에 X.grad를 호출하면 X에 따른 h_XW의 변화율을 알 수 있다.
1행 1열의 값에 주목하자. 0.197788이다.
위에서 직접 유도하여 구한 ∂f/∂X = 0.197792792과 비교해 보자. 비슷한가? 비슷한 것 같다.
따라서 직접 유도하여 구한 합성함수의 도함수는 타당하다고 볼 수 있다.
'파이썬 프로그래밍 > 딥러닝과 수학' 카테고리의 다른 글
지수가중이동평균(Exponentially Weighted Moving Average)-2 (0) | 2022.01.24 |
---|---|
지수가중이동평균(Exponentially Weighted Moving Average)-1 (2) | 2022.01.24 |
6. 2차원 행렬을 입력받는 합성함수의 도함수(이론) (0) | 2021.12.31 |
5. 벡터 합성함수의 도함수 표현 (0) | 2021.12.29 |
4. 벡터 입력에 대한 합성함수 표현 (0) | 2021.12.29 |