PyTorch 기초 튜토리얼: 딥러닝 모델 개발 시작하기

이 문서는 PyTorch를 사용하여 머신러닝 작업을 수행하는 기본적인 API와 절차를 소개합니다. 각 섹션의 링크를 통해 더 자세한 내용을 확인할 수 있습니다.

데이터 처리

PyTorch에서 데이터 작업을 위한 두 가지 핵심 구성 요소는 torch.utils.data.DataLoadertorch.utils.data.Dataset입니다. Dataset은 샘플과 해당 레이블을 저장하고, DataLoaderDataset에 대한 반복 가능한 인터페이스를 제공합니다.

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

# 필요한 라이브러리 임포트

PyTorch는 TorchText, TorchVision, TorchAudio와 같은 도메인 특화 라이브러리를 제공합니다. 이 예제에서는 TorchVision 데이터셋을 사용합니다.

torchvision.datasets 모듈은 CIFAR, COCO 등 실제 이미지 데이터를 포함하는 다양한 Dataset 객체를 제공합니다. 여기서는 FashionMNIST 데이터셋을 활용합니다. 각 TorchVision Dataset은 샘플과 레이블을 전처리하기 위한 transformtarget_transform 파라미터를 지원합니다.

# 훈련 데이터 다운로드
training_dataset = datasets.FashionMNIST(
    root='data',
    train=True,
    download=True,
    transform=ToTensor(),
)

# 테스트 데이터 다운로드
testing_dataset = datasets.FashionMNIST(
    root='data',
    train=False,
    download=True,
    transform=ToTensor(),
)

다운로드된 데이터는 다음과 같은 출력을 생성합니다:

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to data/FashionMNIST/raw/train-images-idx3-ubyte.gz
Extracting data/FashionMNIST/raw/train-images-idx3-ubyte.gz to data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw/train-labels-idx1-ubyte.gz
Extracting data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz
Extracting data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz
Extracting data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw

Dataset 객체를 DataLoader에 전달하면 배치 생성, 샘플링, 셔플링, 멀티프로세싱 로딩 등의 기능을 자동으로 수행할 수 있습니다. 여기서는 배치 크기를 64로 설정합니다.

batch_size = 64

# 데이터 로더 생성
train_loader = DataLoader(training_dataset, batch_size=batch_size)
test_loader = DataLoader(testing_dataset, batch_size=batch_size)

# 데이터 형태 확인
for images, labels in test_loader:
    print("입력 이미지 차원 [N, C, H, W]: ", images.shape)
    print("레이블 차원: ", labels.shape, labels.dtype)
    break

출력 결과:

입력 이미지 차원 [N, C, H, W]:  torch.Size([64, 1, 28, 28])
레이블 차원:  torch.Size([64]) torch.int64

모델 정의

PyTorch에서 모델을 정의하려면 nn.Module을 상속받는 클래스를 생성해야 합니다. __init__ 메서드에서 네트워크 레이어를 정의하고, forward 메서드에서 데이터가 네트워크를 통과하는 방식을 지정합니다. GPU 사용 가능 여부에 따라 모델을 적절한 장치로 이동시킵니다.

# 학습용 장치 선택
device = 'cuda' if torch.cuda.is_available() else "cpu"
print(f"사용 중인 장치: {device}")

# 신경망 모델 정의
class FashionClassifier(nn.Module):
    def __init__(self):
        super(FashionClassifier, self).__init__()
        self.flatten_layer = nn.Flatten()
        self.network_layers = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )

    def forward(self, input_tensor):
        flattened = self.flatten_layer(input_tensor)
        output_logits = self.network_layers(flattened)
        return output_logits

# 모델 인스턴스 생성 및 장치 할당
classifier_model = FashionClassifier().to(device)
print(classifier_model)

출력 결과:

사용 중인 장치: cuda
FashionClassifier(
  (flatten_layer): Flatten(start_dim=1, end_dim=-1)
  (network_layers): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)

모델 최적화

모델 학습을 위해서는 손실 함수와 옵티마이저가 필요합니다.

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(classifier_model.parameters(), lr=1e-3)

단일 학습 루프에서는 모델이 훈련 데이터에 대한 예측을 수행하고, 예측 오차를 역전파하여 모델 파라미터를 조정합니다.

def train_model(data_loader, model, loss_function, opt):
    dataset_size = len(data_loader.dataset)
    model.train()
    for batch_idx, (input_data, target_labels) in enumerate(data_loader):
        input_data, target_labels = input_data.to(device), target_labels.to(device)
        
        # 예측 오차 계산
        predictions = model(input_data)
        loss_value = loss_function(predictions, target_labels)
        
        # 역전파
        opt.zero_grad()
        loss_value.backward()
        opt.step()
        
        if batch_idx % 100 == 0:
            current_loss, progress = loss_value.item(), batch_idx * len(input_data)
            print(f"손실값: {current_loss:>7f} [{progress:>5d}/{dataset_size:>5d}]")

테스트 데이터셋에서 모델 성능을 평가하여 학습 진행 상황을 확인할 수 있습니다.

def evaluate_model(data_loader, model, loss_function):
    dataset_size = len(data_loader.dataset)
    batch_count = len(data_loader)
    model.eval()
    total_loss, correct_predictions = 0, 0
    with torch.no_grad():
        for input_data, target_labels in data_loader:
            input_data, target_labels = input_data.to(device), target_labels.to(device)
            predictions = model(input_data)
            total_loss += loss_function(predictions, target_labels).item()
            correct_predictions += (predictions.argmax(1) == target_labels).type(torch.float).sum().item()
    avg_loss = total_loss / batch_count
    accuracy = correct_predictions / dataset_size
    print(f"테스트 결과: \n 정확도: {(100 * accuracy):>0.1f}%, 평균 손실: {avg_loss:>8f}\n")

학습은 여러 에포크(epoch)로 구성됩니다. 각 에포크 동안 모델은 파라미터를 학습하여 더 나은 예측을 수행하게 됩니다.

num_epochs = 5
for epoch in range(num_epochs):
    print(f"에포크 {epoch+1}\n-----------------------")
    train_model(train_loader, classifier_model, criterion, optimizer)
    evaluate_model(test_loader, classifier_model, criterion)
print("학습 완료!")

출력 결과:

코드 보기
에포크 1
-------------------------------
손실값: 2.296330  [    0/60000]
손실값: 2.292454  [ 6400/60000]
손실값: 2.276433  [12800/60000]
손실값: 2.274833  [19200/60000]
손실값: 2.250950  [25600/60000]
손실값: 2.225070  [32000/60000]
손실값: 2.222952  [38400/60000]
손실값: 2.200769  [44800/60000]
손실값: 2.202750  [51200/60000]
손실값: 2.155594  [57600/60000]
테스트 결과:
 정확도: 40.0%, 평균 손실: 2.161115

에포크 2
-------------------------------
손실값: 2.169147  [    0/60000]
손실값: 2.165468  [ 6400/60000]
손실값: 2.118014  [12800/60000]
손실값: 2.129221  [19200/60000]
손실값: 2.074899  [25600/60000]
손실값: 2.022606  [32000/60000]
손실값: 2.033795  [38400/60000]
손실값: 1.976709  [44800/60000]
손실값: 1.982757  [51200/60000]
손실값: 1.881978  [57600/60000]
테스트 결과:
 정확도: 57.4%, 평균 손실: 1.902724

에포크 3
-------------------------------
손실값: 1.934711  [    0/60000]
손실값: 1.906422  [ 6400/60000]
손실값: 1.806563  [12800/60000]
손실값: 1.832814  [19200/60000]
손실값: 1.717731  [25600/60000]
손실값: 1.673628  [32000/60000]
손실값: 1.679022  [38400/60000]
손실값: 1.602205  [44800/60000]
손실값: 1.623030  [51200/60000]
손실값: 1.492521  [57600/60000]
테스트 결과:
 정확도: 61.5%, 평균 손실: 1.529054

에포크 4
-------------------------------
손실값: 1.592845  [    0/60000]
손실값: 1.556097  [ 6400/60000]
손실값: 1.417763  [12800/60000]
손실값: 1.478243  [19200/60000]
손실값: 1.357680  [25600/60000]
손실값: 1.356057  [32000/60000]
손실값: 1.360733  [38400/60000]
손실값: 1.298324  [44800/60000]
손실값: 1.329920  [51200/60000]
손실값: 1.219030  [57600/60000]
테스트 결과:
 정확도: 63.4%, 평균 손실: 1.250318

에포크 5
-------------------------------
손실값: 1.324846  [    0/60000]
손실값: 1.306784  [ 6400/60000]
손실값: 1.145549  [12800/60000]
손실값: 1.245576  [19200/60000]
손실값: 1.123671  [25600/60000]
손실값: 1.150098  [32000/60000]
손실값: 1.164900  [38400/60000]
손실값: 1.111517  [44800/60000]
손실값: 1.147514  [51200/60000]
손실값: 1.059701  [57600/60000]
테스트 결과:
 정확도: 64.6%, 평균 손실: 1.081113

학습 완료!

모델 저장

학습된 모델을 저장하는 일반적인 방법은 내부 상태 사전(state dictionary)을 직렬화하는 것입니다.

torch.save(classifier_model.state_dict(), "fashion_classifier.pth")
print("PyTorch 모델 상태가 fashion_classifier.pth에 저장되었습니다")

모델 로드

저장된 모델을 불러오려면 모델 구조를 재생성하고 상태 사전을 로드해야 합니다.

loaded_model = FashionClassifier()
loaded_model.load_state_dict(torch.load("fashion_classifier.pth"))

이제 로드된 모델을 사용하여 예측을 수행할 수 있습니다.

class_names = [
    "티셔츠/탑",
    "바지",
    "풀오버",
    "드레스",
    "코트",
    "샌들",
    "셔츠",
    "스니커즈",
    "가방",
    "앵클 부츠",
]

loaded_model.eval()
sample_image, true_label = testing_dataset[0][0], testing_dataset[0][1]
with torch.no_grad():
    prediction = loaded_model(sample_image)
    predicted_class = class_names[prediction[0].argmax(0)]
    actual_class = class_names[true_label]
    print(f'예측 결과: "{predicted_class}", 실제 값: {actual_class}')

출력 결과:

예측 결과: "앵클 부츠", 실제 값: "앵클 부츠"

태그: PyTorch deep-learning machine-learning Neural-Networks data-loading

6월 23일 18:19에 게시됨