이 문서는 PyTorch를 사용하여 머신러닝 작업을 수행하는 기본적인 API와 절차를 소개합니다. 각 섹션의 링크를 통해 더 자세한 내용을 확인할 수 있습니다.
데이터 처리
PyTorch에서 데이터 작업을 위한 두 가지 핵심 구성 요소는 torch.utils.data.DataLoader와 torch.utils.data.Dataset입니다. Dataset은 샘플과 해당 레이블을 저장하고, DataLoader는 Dataset에 대한 반복 가능한 인터페이스를 제공합니다.
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은 샘플과 레이블을 전처리하기 위한 transform과 target_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}')
출력 결과:
예측 결과: "앵클 부츠", 실제 값: "앵클 부츠"