Deep learning

PyTorch 기초 (데이터, 모델, 학습, 저장)

taeeyeong 2024. 5. 3. 17:55

PyTorch를 사용한 기본적인 뉴럴 네트워크 구축 방법에 대해 알아보겠습니다. PyTorch는 사용자 친화적인 API와 강력한 기능으로 인해 머신 러닝 연구와 개발에 널리 사용되고 있습니다. PyTorch를 활용해서 모델을 구성하는 기본적인 구조를 설명하고자 합니다. 

 

PyTorch는 데이터를 다루는 데 두 가지 기본 도구를 제공합니다: `torch.utils.data.DataLoader`와 `torch.utils.data.Dataset`. `Dataset`은 샘플과 그에 해당하는 레이블을 저장하며, `DataLoader`는 `Dataset` 주위에 반복 가능한 객체를 감싸 줍니다.

PyTorch는 `TorchText`, `TorchVision`, `TorchAudio` 등 도메인 특화 라이브러리를 제공하며, 이들 모두 다양한 데이터셋을 포함하고 있습니다. 이 예시에서는 `TorchVision` 데이터셋을 사용할 예정입니다.

 

# Download training data from open datasets.
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)

# Download test data from open datasets.
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)

batch_size = 64

# Create data loaders.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print(f"Shape of X [N, C, H, W]: {X.shape}")
    print(f"Shape of y: {y.shape} {y.dtype}")
    break



`torchvision.datasets` 모듈은 CIFAR, COCO 등 실제 비전 데이터를 위한 다양한 `Dataset` 객체를 포함하고 있습니다. 여기서는 `FashionMNIST` 데이터셋을 사용합니다. 모든 `TorchVision Dataset`은 샘플과 레이블을 각각 수정하기 위해 `transform`과 `target_transform` 두 가지 인수를 포함합니다.

이 `Dataset`을 `DataLoader`의 인수로 전달합니다. 이렇게 하면 데이터셋 주위에 반복 가능한 객체가 생성되고, 자동 배치, 샘플링, 셔플링 및 멀티프로세스 데이터 로딩을 지원합니다. 여기서는 배치 크기를 64로 정의합니다. 즉, 데이터로더 반복자의 각 요소가 64개의 특징과 레이블의 배치를 반환하게 됩니다.

 

 

뉴럴 네트워크를 PyTorch에서 정의하기 위해서는 `nn.Module`을 상속받는 클래스를 생성해야 합니다. 네트워크의 각 층(layer)은 `__init__` 함수 안에서 정의되며, 데이터가 네트워크를 어떻게 통과할지는 `forward` 함수에서 명시합니다. 뉴럴 네트워크의 연산을 가속화하기 위해, 가능한 경우 GPU나 MPS(애플의 Metal Performance Shaders)로 네트워크를 이동시킬 수 있습니다.

 

# Get cpu, gpu or mps device for training.
device = (
    "cuda"
    if torch.cuda.is_available()
    else "mps"
    if torch.backends.mps.is_available()
    else "cpu"
)
print(f"Using {device} device")

# Define model
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)



이 과정을 간단히 살펴보겠습니다. 우선, `nn.Module`을 상속받아 사용자 정의 클래스를 만듭니다. 이 클래스 내에서는 `__init__` 메서드를 통해 모델의 구조를 구성하는 레이어들을 초기화합니다. 예를 들어, 컨볼루션 레이어, 풀링 레이어, 풀리 커넥티드 레이어(또는 선형 레이어) 등을 포함시킬 수 있습니다. 그 다음, `forward` 메서드에서는 이 레이어들을 통해 입력 데이터가 어떻게 전달되는지를 정의합니다. 이 메서드는 모델에 데이터를 입력했을 때 실행되는 코드로, 입력 데이터가 각 레이어를 순차적으로 통과하면서 최종 출력까지 도달하는 과정을 구체적으로 기술합니다.


마지막으로, 모델의 속도를 높이기 위해 `.to(device)` 메서드를 사용하여 모델을 GPU나 MPS로 옮길 수 있습니다. 이는 해당 디바이스에서 모델 연산이 더 빠르게 처리될 수 있도록 해 줍니다. 이 과정은 특히 대규모 데이터를 처리하거나 복잡한 모델을 학습할 때 매우 중요합니다.

 

모델을 훈련시키기 위해서는 손실 함수와 옵티마이저가 필요합니다.

 

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

def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)

        # Compute prediction error
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
            
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
    
epochs = 5
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model, loss_fn)
print("Done!")


모델은 훈련 데이터셋에 대한 예측을 수행합니다(배치 단위로 제공됨). 그리고 예측 오류를 역전파하여 모델의 파라미터를 조정합니다.

또한, 모델이 학습하고 있는지 확인하기 위해 테스트 데이터셋에 대한 성능을 검사합니다.

훈련 과정은 여러 번의 반복(에폭)을 거치며 수행됩니다. 각 에폭 동안 모델은 더 나은 예측을 하기 위해 파라미터를 학습합니다. 각 에폭마다 모델의 정확도와 손실을 출력하며, 에폭이 거듭될수록 정확도는 증가하고 손실은 감소하도록 합니다. 

이렇게 하여 모델의 학습 과정을 진행하면서, 학습의 진행을 정기적으로 모니터링하게 됩니다. 이 과정은 모델이 제대로 학습하고 있는지, 과적합이나 부적합 없이 예측 성능이 향상되고 있는지를 확인하는 데 중요한 역할을 합니다. 따라서, 각 에폭마다의 성과를 면밀히 관찰하며 필요한 조정을 실시하는 것이 중요합니다. 이러한 방법으로, 모델의 최적화 과정을 체계적으로 관리하고, 효율적인 학습 경로를 설정할 수 있습니다.

 

모델을 저장하는 일반적인 방법은 내부 상태 사전(모델 매개변수를 포함)을 직렬화하는 것입니다. PyTorch에서는 이를 위해 `torch.save` 함수를 사용하여 모델의 `state_dict`를 파일로 저장할 수 있습니다. `state_dict`는 모델의 각 레이어에 대한 매개변수(가중치와 편향)를 포함하는 파이썬 사전입니다.

모델을 저장하는 과정은 매우 간단합니다. 먼저, 훈련이 완료된 모델의 `state_dict`를 추출하고, `torch.save` 함수를 사용하여 이를 파일로 저장합니다. 예를 들어, 모델을 'model.pth'라는 파일로 저장하고 싶다면 다음과 같은 코드를 사용할 수 있습니다:

torch.save(model.state_dict(), 'model.pth')



이렇게 저장된 모델은 나중에 필요할 때 언제든지 불러와 다시 사용할 수 있습니다. 모델을 다시 사용하기 위해서는 먼저 같은 모델 구조의 인스턴스를 생성하고, `torch.load` 함수로 저장된 `state_dict`를 불러온 후, 이를 모델에 로드합니다. 다음은 저장된 모델을 불러와 사용하는 방법에 대한 예시입니다:

 

model = MyModel()  # 동일한 모델 구조의 인스턴스를 생성
model.load_state_dict(torch.load('model.pth'))



이 방법을 사용하면 모델의 파라미터를 정확하게 복원할 수 있어, 훈련을 중단한 지점에서 다시 시작하거나, 모델을 다른 시스템으로 이전하여 사용하는 것이 가능합니다.

 

전체 노트북 파일도 공유합니다. 

모든 내용은 pytorch 공식문서 내용을 바탕으로 작성되었습니다. 

 

template.ipynb
0.01MB