AI/딥러닝

[Pytorch] Autograd 자동 미분 및 역전파 적용하기

daeunnniii 2023. 3. 26. 21:30
728x90
반응형

 

이번 글에서는 Pytorch의 Autograd를 활용해 자동 미분(Auto differentitation)하는 방법과 이전 게시글에서 이론을 살펴보았던 경사하강법의 역전파 과정을 Pytorch로 구현하여 편미분을 쉽게 진행하는 방법을 알아볼 것이다.

 

이전 게시글

https://daeunnniii.tistory.com/189

 

역전파와 경사하강법 쉽게 이해하기

역전파(BackPropagation)는 한마디로 신경망 모델에서 오차를 이용하여 가중치를 업데이트하는 방법이다. 1. 인공 신경망의 이해 예제로 사용할 인공 신경망은 다음과 같이 입력층, 은닉층, 출력층 3

daeunnniii.tistory.com

 

인공 신경망을 최적화하는 과정에서 미분은 필수적인 요소이다. Pytorch에서는 최적화 과정인 역전파(Backpropagation)을 쉽게 진행할 수 있도록 자동 미분 계산을 제공한다.

 

1. Pytorch 자동 미분(automatic differentiation)

 

우선 매우 간단한 예제로 함수 $ J(a, b, c) $를 살펴볼 것이다.

$ J(a, b, c) = 3(a+bc), a=5, b=3, c=2 $

우선 torch를 import해준 뒤, 텐서 a, b, c를 생성한다. type의 경우 float으로 지정하기 위해 값 뒤에 소수점 .을 붙여준다. 또한 미분을 가능하게 하기 위해선 tensor의 옵션이 requires_grad=True로 설정되어있어야 한다.

import torch
a = torch.tensor(5., requires_grad=True)
b = torch.tensor(3., requires_grad=True)
c = torch.tensor(2., requires_grad=True)

 

함수 J를 선언하고 J를 각 변수에 대해 편미분하기 위해 .backward()를 호출한다.

J = 3 * (a + b * c)
J.backward()

a, b, c로 편미분한 $ \frac{\partial J}{\partial a}, \frac{\partial J}{\partial b}, \frac{\partial J}{\partial c} $를 출력해보면 3, 6, 9로 잘 계산된 것을 확인할 수 있다.

print(a.grad, b.grad, c.grad)

>> Result:
tensor(3.) tensor(6.) tensor(9.)

 

2. 역전파 과정 Pytorch로 구현

이전 게시글에서 경사 하강법을 진행하면서 보았던 역전파의 복잡한 미분식을 torch.autograd를 사용하여 자동미분코드를 구현해볼 것이다.

우선 input 값 x1, x2와 실제 output 값(target)에 해당하는 y1, y2를 각각 텐서로 생성한다. 물론 하나의 변수에 행렬로 생성할 수도 있겠지만, 각 변수를 알아보기 쉽게 각각 별도의 scalar 값을 갖는 텐서를 생성해주었다.

x1 = torch.tensor(0.1, requires_grad=True)
x2 = torch.tensor(0.2, requires_grad=True)

y1 = torch.tensor(0.4, requires_grad=True)
y2 = torch.tensor(0.6, requires_grad=True)

위 그림을 참고하여 가중치 $ W_{1} $부터 $ W_{8} $까지 텐서로 생성한다.

w1 = torch.tensor(0.3, requires_grad=True)
w2 = torch.tensor(0.25, requires_grad=True)
w3 = torch.tensor(0.4, requires_grad=True)
w4 = torch.tensor(0.35, requires_grad=True)
w5 = torch.tensor(0.45, requires_grad=True)
w6 = torch.tensor(0.4, requires_grad=True)
w7 = torch.tensor(0.7, requires_grad=True)
w8 = torch.tensor(0.6, requires_grad=True)

1) 순전파(Forward Propagation) 구현

역전파를 구현하기에 앞서 먼저 순전파를 진행한다. 입력값을 각 입력에 해당하는 가중치와 곱하여 z1, z2를 구한다.

z1 = w1*x1 + w2*x2
z2 = w3*x1 + w4*x2

>> Result:
(tensor(0.0800, grad_fn=<AddBackward0>),
 tensor(0.1100, grad_fn=<AddBackward0>))

z1과 z2를 활성화 함수인 시그모이드 함수에 적용하여 h1, h2를 구하는 과정이다.

sigmoid = torch.nn.Sigmoid()
h1 = sigmoid(z1)
h2 = sigmoid(z2)
print(h1, h2)

>> Result:
(tensor(0.5200, grad_fn=<SigmoidBackward0>),
 tensor(0.5275, grad_fn=<SigmoidBackward0>))

다시 h1과 h2 두 값을 각각의 값에 해당하는 가중치와 곱해 가중합 z3, z4를 구한 뒤 다시 시그모이드 함수에 적용하여 o1, o2를 계산하는 과정이다.

z3 = w5*h1 + w6*h2
z4 = w7*h1 + w8*h2
o1 = sigmoid(z3)
o2 = sigmoid(z4)

print(z3, z4)
print()
print(o1, o2)

>> Result:
(tensor(0.4450, grad_fn=<AddBackward0>),
 tensor(0.6805, grad_fn=<AddBackward0>))

(tensor(0.6094, grad_fn=<SigmoidBackward0>),
 tensor(0.6638, grad_fn=<SigmoidBackward0>))

예측값 $ o_{1}, o_{2} $와 실제값의 오차를 계산하기 위해 평균 제곱 오차 MSE 함수에 적용하여 $ E_{total} $을 구하는 과정이다.

E_o1 = 1/2*(y1 - o1)**2
E_o2 = 1/2*(y2 - o2)**2
E_total = E_o1 + E_o2
print(E_o1, E_o2, E_total)

>> Result:
(tensor(0.0219, grad_fn=<MulBackward0>),
 tensor(0.0020, grad_fn=<MulBackward0>),
 tensor(0.0240, grad_fn=<AddBackward0>))

 

2) 역전파 구현

이전 게시글에서 살펴보았듯이, 역전파 과정에서 편미분하는 과정을 직접 계산하려면 매우 복잡하다.

이 과정을 torch.autograd를 사용하여 한번에 쉽게 구할 수 있다.

a = 0.5
E_total.backward()
print(w5.grad, w6.grad, w7.grad, w8.grad)

w5_update = w5 - a*w5.grad
w6_update = w6 - a*w6.grad
w7_update = w7 - a*w7.grad
w8_update = w8 - a*w8.grad

print(w5_update, w6_update, w7_update, w8_update)

>> Result:
tensor(0.0259) tensor(0.0263) tensor(0.0074) tensor(0.0075)
tensor(0.4370, grad_fn=<SubBackward0>) tensor(0.3869, grad_fn=<SubBackward0>) tensor(0.6963, grad_fn=<SubBackward0>) tensor(0.5962, grad_fn=<SubBackward0>)

마찬가지로 역전파 2단계에서 살펴보았던 과정도 다음과 같이 자동 미분으로 쉽게 계산할 수 있다.

print(w1.grad, w2.grad, w3.grad, w4.grad)

w1_update = w1 - a*w1.grad
w2_update = w2 - a*w2.grad
w3_update = w3 - a*w3.grad
w4_update = w4 - a*w4.grad

print(w1_update, w2_update, w3_update, w4_update)

>> Result:
tensor(0.0008) tensor(0.0016) tensor(0.0007) tensor(0.0014)
tensor(0.2996, grad_fn=<SubBackward0>) tensor(0.2492, grad_fn=<SubBackward0>) tensor(0.3996, grad_fn=<SubBackward0>) tensor(0.3493, grad_fn=<SubBackward0>)

 

728x90
반응형