이번 글에서는 Pytorch의 Autograd를 활용해 자동 미분(Auto differentitation)하는 방법과 이전 게시글에서 이론을 살펴보았던 경사하강법의 역전파 과정을 Pytorch로 구현하여 편미분을 쉽게 진행하는 방법을 알아볼 것이다.
이전 게시글
https://daeunnniii.tistory.com/189
인공 신경망을 최적화하는 과정에서 미분은 필수적인 요소이다. 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>)
'AI > 딥러닝' 카테고리의 다른 글
RNN(Recurrent Neural Network) 정리 (0) | 2024.03.17 |
---|---|
MLP(Multi-Layer Perceptron)과 CNN(Convolutional Neural Network) 정리 (0) | 2024.03.17 |
[Pytorch] 데이터 로드하기 - Dataset, DataLoader 정리 (0) | 2023.03.28 |
역전파와 경사하강법 쉽게 이해하기 (0) | 2023.03.26 |
[Pytorch] 텐서(Tensor) 다루기 (0) | 2023.03.26 |