본문 바로가기

ML/PyTorch

2. 역전파

역전파

 

- 모델내부의 파라미터의 최적화는 미분의 성질과 연쇄법칙(Chain rule)을 기반으로 한 역전파를 통해 진행된다.

- 역전파는 모델이 복잡할 수록 계산 과정이 복잡해져 코드를 직접 구현하기에는 어려움이 있다.

- 하지만 파이토치에는 간단하게 사용할 수 있는 다양한 최적화 방법을 제공하고 있다.

 

1. 그레디언트 텐서

- 일반적으로 인공 신경망의 최적화는 손실 함수의 최솟값(Global minimum)이 나오게 하는 신경망의 최적 가중치를 찾는 과정이다.

- 따라서 최적화를 위해 변화량을 나타내는 미분은 필수적인 요소이다.

- 인공신경망의 구조는 다음과 같이 입력 값이 들어와 여러 레이어를 지나 출력 값을 산출하는 합성 함수의 형태이다. 

인공신경망의 구조 [출처: https://commons.wikimedia.org/wiki/File:Artificial_neural_network.svg]

- 따라서 연쇄법칙을 통해 원하는 변수에 대한 미분값을 구할 수 있다.

import torch 
x = torch.ones(2,2, requires_grad = True)
y = x+1
z = 2*y**2
r = z.mean()
print("Result:", r)

requires_grad = True는 해당 텐서를 기준으로 모든 연산들을 추적하여 그래디언트를 계산할 수 있게 한다.

- 즉 x에 대해 연쇄 법칙을 이용한 미분이 가능하다는 것이다.'

 

 

2. 그레디언트 텐서

- 일단 r를 기준으로 역전파를 진행해보자.

- 아래 코드는 $\frac{dr}{dx}$를 의미한다.

r.backward()
x.grad

output:
tensor([[2., 2.],
        [2., 2.]])

- 고급 코딩으로 가게 되면 이렇게 하나씩 그레디언트를 구해야 하는 일이 찾아온다. 하지만 지금은 이 방식에 대한 깊은 이해 보다는.

- 이러한 컨셉으로 역전파를 진행한다는 점을 이해하는 것에 중점을 두자.

 

 

3. 자동 미분 - 선형 회귀식

-  x를 입력값, y를 실제 값이라고 하였을 때, 가중치 w와 b를 loss 값을 감소시키는 방향으로 100번 동안 학습하는 과정을 코드로 구현해 보겠다.

 

입력값, 실제 값 정의

- 우선 입력값 x와 실제 값 y를 정의해준다. 

import torch 
from matplotlib import pyplot as plt

x = torch.FloatTensor(range(5)).unsqueeze(1)
y = 2*x + torch.rand(5,1)
num_feature = x.shape[1]

- torch.FloatTensor는 리스트 range(5)를 이용해 텐서로 만든다. 

- 이때 원래 크기는 1차원(torch.Tensor(5))이라서 행렬 계산을 위해 5,1로  만들어 주어야 한다.

- 그러기 위해 unsqueeze(1)를 사용해서 1번째 위치의 차원을 늘려주는 역할을 하여 최종적으로 x의 크기는 torch.Size(5,1)이 된다.

(만약 unsqueeze(0)을 하면 (1,5)가 되겠지?)

- y는 실제값으로 임의의 값을 만들어 주자.

 

가중치와 바이어스 정의

w =torch.randn(num_feature, 1, requires_grad=True) # 가중치
b =torch.randn(1, requires_grad=True) # 바이어스

- 선형 식은 $y = wx + b$로 표현된다.

- 바이어스는 모든 인스턴스에 동일한 숫자를 더해주는 것이므로 크기가 1인 텐서로 정의된다.

입력값 과 실제값 x와 y는 변하지 않은 값으로서 업데이트가 필요하지 않은 반면,
w,b값은 역전파를 통해 최적값을 찾는 것이므로 w, bd에 requires_grad를 True로 활성화 시킨다.

 

학습률, 옵티마이저 정의

- 옵티 마이저는 모델을 학습시키는 과정에서 사용되는 최적화 알고리즘이다. 

- 모델이 예측한 출력값과 실제 정답의 차이를 최소화하는 방향으로 모델읠 가중치(weight)와 편항(bias)를 업데이트하여 학습이 진행된다. 

- 가중치를 업데이트하는 최적화 방법은 매우 다양하다.

- 그중 가장 널리 사용되는 방법이 경사 하강법이다.

- 이 예시에서는 확률적 경사하강법SGD를 사용한다.

경사 하강법 : 목적 함수인 손실 함수를 기준으로 그래디언트를 계산하여 변수를 최적화하는 기법이다.
learning_rate = 1e-3
optimizer = torch.optim.SGD([w,b], lr=learning_rate)

 

학습

loss_stack = [] #에포크마다 loss값을 저장하는 리스트

for epoch in range(1001): # epoch = 1001
    optimizer.zero_grad() # 최적화 과정에서 누적된 값 초기화
    y_hat = torch.matmul(x,w) + b # 선형식
    loss = torch.mean((y_hat - y) ** 2) # 손실 함수 계산(여기서는 MSE)
    loss.backward() # 역전파의 기주을 손실함수로
    optimizer.step() # optimizer로 최적화
    loss_stack.append(loss.item()) #loss값 저장.

    if epoch % 100 == 0: # 에포크가 100번 누적될때마다 loss값 출력
        print(f'Epoch {epoch}:{loss.item()}')

학습된 모델로 예측값 산출

- with torch.no_grad():를 이용하여 requires_grad가 작동하지 않도록 한다. 

with torch.no_grad():
    y_hat = torch.matmul(x,w) + b
    
    
 Output: 
 tensor([[1.0460],
        [2.8785],
        [4.7109],
        [6.5433],
        [8.3758]])

 

시각화

plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.plot(loss_stack)
plt.title("Loss")
plt.subplot(122)
plt.plot(x, y,'.b') # 실제 값
plt.plot(x, y_hat, 'r-') # 예측 값
plt.legend(['ground truth', 'prediction']) 
plt.title("Prediction") 
plt.show()

 

'ML > PyTorch' 카테고리의 다른 글

1. 텐서  (0) 2023.02.24