파이토치 딥러닝 핵심 개념: 역전파, 계산 그래프 및 텐서 활용법

1 역전파 알고리즘

신경망 학습의 목적은 비용 함수(cost function)를 최적화하여 전역 또는 지역 최솟값을 찾는 것입니다. 비용 함수를 최대한 0에 가깝게 만들어야 가장 좋은 가중치(weight)와 편향(bias)을 얻을 수 있습니다. 매개변수를 지속적으로 조정하여 비함수가 수렴하도록 만드는데, 비용 함수가 기울기(gradient)가 가장 빠르게 감소하는 방향으로 움직이도록 해야 합니다. 이러한 이유로 역전파(BP) 알고리즘이 제시되었으며, 이는 가중치와 편향의 기울기를 계산하여 학습 속도를 높이고 비효율적인 학습을 방지합니다.

역전파 기울기 계산은 연쇄 미분법(chain rule)을 사용하며, 이는 고등학교 때 배운 개념으로 이해하기 쉽습니다.

  • 역전파의 장점: 단 한 번의 순전파(forward propagation)와 역전파를 통해 모든 매개변수의 편미분을 동시에 계산할 수 있습니다. 역전파의 계산량은 순전파와 거의 비슷하며, 순전파 과정에서 계산된 결과를 효율적으로 활용합니다. 순전파의 주요 계산량은 가중치 행렬과 입력 벡터의 곱셈 계산에서 발생하며, 역전파는 주로 행렬과 입력 벡터의 전치(transpose) 곱셈 계산에서 발생합니다.

2 연쇄 미분법

신경망에서 역전파를 이해하기 위해서는 연쇄 미분법에 대한 이해가 필수적입니다. 이는 복합 함수의 미분을 계산하는 방법으로, 각 계층에서의 오차를 이전 계층으로 전파하는 데 사용됩니다.

3 계산 그래프

계산 그래프는 신경망 구축의 복잡성을 줄여주는 도구입니다. 과거에는 각 신경망에 대해 별도의 역전파 알고리즘을 작성해야 했습니다.

(1) 계산 그래프는 방향성 비순환 그래프(DAG)입니다. (2) 파이토치(PyTorch)는 동적 계산 그래프를 사용하며, 텐서플로우(TensorFlow)는 초기에는 정적 계산 그래프를 사용했지만 이후 동적 계산 그래프도 지원하게 되었습니다. (3) 파이토치의 동적 계산 그래프는 메모리 절약을 위해 한 번의 반복이 완료되면 메모리에서 해제되므로, 매번 새로운 계산 그래프를 구축해야 합니다. 계산 그래프는 프로그램 내 변수 간의 관계를 나타냅니다. (4) 파이토치 계산 그래프에는 데이터(Tensor)와 연산의 두 가지 요소만 존재합니다. 텐서는 리프 노드(leaf node)와 비리프 노드(non-leaf node)로 나뉩니다. backward() 함수를 사용하여 텐서의 기울기를 계산할 때, 모든 텐서의 기울기를 계산하는 것이 아니라 다음 조건을 만족하는 텐서의 기울기만 계산합니다: 1. 리프 노드 유형, 2. requires_grad=True, 3. 해당 텐서에 의존하는 모든 텐서의 requires_grad=True. 사용자가 정의한 텐서에서 requires_grad 속성은 기본적으로 False이며, 신경망의 가중치 w 텐서에서는 기본적으로 True로 설정됩니다. (5) autograd 패키지는 텐서 모든 작업의 자동 미분 방법을 제공합니다. torch.Tensor가 이 패키지에서 가장 중요한 클래스입니다. requires_grad를 True로 설정하면 텐서에서 수행되는 모든 작업을 추적하기 시작합니다. 계산이 완료되면 backward()를 호출하면 모든 기울기가 자동으로 계산됩니다. 그러면 이 텐서의 기울기는 grad 속성에 자동으로 누적됩니다.

4 텐서

텐서에서 기울기를 계산하도록 지정하려면 requires_grad=True로 설정해야 합니다.

w는 Tensor(텐서 타입)이며, Tensor에는 data와 grad가 포함되어 있습니다. data와 grad도 Tensor입니다. grad는 초기에 None이며, l.backward() 메서드를 호출한 후 w.grad는 Tensor가 됩니다. 따라서 w.data를 업데이트할 때는 w.grad.data를 사용해야 합니다. w에 기울기를 계산해야 한다면, 구축되는 계산 그래프에서 w와 관련된 모든 텐서는 기본적으로 기울기를 계산해야 합니다. backward()를 호출하면 계산해야 하는 모든 기울기가 계산되어 해당하는 w.grad.data에 저장됩니다.

  • torch.tensor()와 torch.Tensor()의 차이:

5 코드 예제

import matplotlib.pyplot as plt
import torch
import numpy as np

# SGD (확률적 경사 하강법) 구현

# 입력 데이터 생성
input_values = np.arange(1.0, 200.0, 1.0)
target_values = np.arange(2.0, 400.0, 2.0)

# 예측 함수 정의
def prediction(input_val, weight):
    return input_val * weight

# 손실 함수 정의
def calculate_loss(input_val, target_val, weight):
    predicted = prediction(input_val, weight)
    return (predicted - target_val) ** 2

# 가중치 초기화
weight = torch.Tensor([1.0])
weight.requires_grad = True

# 학습률 설정
learning_rate = 0.00001

# 학습 과정에서 손실 기록
epoch_records = []
loss_records = []

# 학습 전 예측값 출력
print("학습 전 예측값(400): ", prediction(torch.Tensor([400.]), weight).data.item())

# 학습 루프
for iteration in range(100):
    # 무작위로 데이터 선택
    random_index = np.random.choice(range(len(input_values)))
    
    # 손실 계산
    current_loss = calculate_loss(input_values[random_index], 
                                  target_values[random_index], 
                                  weight)
    
    # 역전파 수행
    current_loss.backward()
    
    # 가중치 업데이트
    weight.data -= learning_rate * weight.grad.data
    
    # 기울기 초기화
    weight.grad.data.zero_()
    
    # 학습 과정 출력
    print("반복: ", iteration, " 손실: ", current_loss.data.item(), " 가중치: ", weight.data.item())
    
    # 기록 저장
    epoch_records.append(iteration)
    loss_records.append(current_loss.data.item())
    
    # 수렴 조건 확인
    if (current_loss < 1e-7):
        break

# 학습 후 예측값 출력
print("학습 후 예측값(400): ", prediction(torch.Tensor([400.]), weight).data.item())

# 손실 변화 시각화
plt.plot(epoch_records, loss_records)
plt.xlabel("반복 횟수")
plt.ylabel("손실")
plt.savefig("./data/pytorch_loss_curve.png")
import numpy as np
import matplotlib.pyplot as plt
import torch

# 2차 함수 모델: 3 * x^2 + 2 * x + 2
input_data = [1.0, 2.0, 3.0]
target_data = [7.0, 18.0, 35.0]

# 예측 함수 정의
def forward(input_val, coef1, coef2, bias):
    return (coef1 * input_val ** 2 + coef2 * input_val + bias)

# 손실 함수 정의
def loss_function(input_val, target_val, coef1, coef2, bias):
    predicted = forward(input_val, coef1, coef2, bias)
    return (predicted - target_val) ** 2

# 매개변수 초기화
coef1 = torch.Tensor([1.0])  # 초기 계수 1
coef1.requires_grad = True  # 기울기 계산 활성화
coef2 = torch.Tensor([1.0])  # 초기 계수 2
coef2.requires_grad = True
bias = torch.Tensor([1.0])   # 초기 편향
bias.requires_grad = True

# 학습률 설정
learning_rate = 0.001

# 학습 과정 기록
iteration_records = []
loss_records = []

# 학습 전 예측값 출력
print("학습 전 예측값(4): ", forward(torch.Tensor([4.]), coef1, coef2, bias).data.item())

# 학습 루프
for iteration in range(10000):
    # 무작위로 데이터 선택
    random_index = np.random.choice(range(len(input_data)))
    
    # 손실 계산
    current_loss = loss_function(input_data[random_index], 
                               target_data[random_index], 
                               coef1, coef2, bias)
    
    # 역전파 수행
    current_loss.backward()
    
    # 매개변수 업데이트
    coef1.data -= learning_rate * coef1.grad.data
    coef2.data -= learning_rate * coef2.grad.data
    bias.data -= learning_rate * bias.grad.data
    
    # 기울기 초기화
    coef1.grad.data.zero_()
    coef2.grad.data.zero_()
    bias.grad.data.zero_()
    
    # 학습 과정 출력
    print("반복: ", iteration, " 손실: ", current_loss.data.item(), 
          " 계수1: ", coef1.data.item(), " 계수2: ", coef2.data.item(), 
          " 편향: ", bias.data.item())
    
    # 기록 저장
    iteration_records.append(iteration)
    loss_records.append(current_loss.data.item())
    
    # 수렴 조건 확인
    if (current_loss < 1e-7):
        break

# 학습 후 예측값 출력
print("학습 후 예측값(4): ", forward(torch.Tensor([4.]), coef1, coef2, bias).data.item())

# 손실 변화 시각화
plt.plot(iteration_records, loss_records)
plt.xlabel("반복 횟수")
plt.ylabel("손실")
plt.savefig("./data/quadratic_model_loss.png")

태그: 파이토치 역전파 계산 그래프 텐서 딥러닝

6월 1일 21:09에 게시됨