[kakao x goorm] 생성 AI 응용 서비스 개발자 양성 과정/회고록

[kakao x goorm] PyTorch 핵심 문법 정리

Hoonia 2025. 5. 11. 01:51

딥러닝을 공부하다 보면 자연스럽게 접하게 되는 프레임워크가 PyTorch다. 나 역시 처음에는 “TensorFlow와 무엇이 다를까?”라는 궁금증에서 출발했지만, 실무 프로젝트와 수업을 병행하면서 PyTorch의 구조, 철학, 그리고 내부 작동 방식에 대해 보다 체계적으로 이해할 수 있었다. 단순히 “파이썬으로 딥러닝을 할 수 있는 도구”라는 인상에 머무르지 않고, 실제로는 정교함과 유연성, 그리고 계산 성능까지 고려된 프레임워크라는 점을 실감하게 됐다.

이번 포스팅에서는 PyTorch의 핵심 문법을 실무 관점에서 정리했다. 특히 모델 파라미터, 학습/평가 모드 등 실전에서 자주 혼동되는 부분을 중심으로 경험을 바탕으로 설명한다.

텐서(Tensor)와 어레이(Array) 다루기

PyTorch의 모든 연산은 텐서에서 출발한다. 텐서는 다차원 데이터를 효율적으로 표현하는 핵심 자료구조로, NumPy 배열과 유사하지만 GPU 가속과 자동 미분 기능이 추가되어 딥러닝에 최적화되어 있다.

import torch

# 텐서 생성 예시
a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
b = torch.randn(2, 2)         # 평균 0, 표준편차 1인 랜덤 텐서
c = torch.zeros((2, 2))       # 0으로 초기화된 텐서

print("고정 텐서:", a)
print("랜덤 텐서:", b)
print("제로 텐서:", c)

실무에서 텐서를 활용하면 넘파이, GPU, 딥러닝 연산을 모두 일관된 방식으로 처리할 수 있는 장점이 있다.

모델 정의하기

딥러닝에서 모델은 입력 데이터를 받아 예측 결과를 출력하는 구조다.
PyTorch에서는 nn.Module을 상속받아 신경망 모델을 정의하며, 내부에 계층(layer)과 데이터 흐름(forward 메서드)을 명확하게 작성한다.

import torch.nn as nn

class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc = nn.Linear(10, 2)  # 10개 입력, 2개 출력

    def forward(self, x):
        return self.fc(x)

model = SimpleNN()

실제 프로젝트에서는 모델 구조를 변경하거나 실험할 때 이 클래스를 수정하는 방식으로 다양한 시도를 하게 된다.

계층(Layer), 모듈(Module), 모델(Model)

  • Layer (계층): 입력을 받아 연산을 수행하고 출력을 반환하는 기본 단위 (예: nn.Linear, nn.ReLU)
  • Module (모듈): 여러 계층을 묶은 재사용 가능한 구조
  • Model (모델): 모듈과 계층을 조합해 전체 신경망을 구성한 최상위 구조

이런 계층적 구조 덕분에 복잡한 모델도 효율적으로 관리할 수 있다.

nn.Sequential로 신경망 구성하기

간단한 모델은 nn.Sequential을 활용해 빠르게 정의할 수 있다.

model = nn.Sequential(
    nn.Linear(784, 128),
    nn.ReLU(),
    nn.Linear(128, 10)
)

프로토타입이나 베이스라인 모델을 빠르게 구성할 때 유용하다.

model.modules() vs model.children()

모델 구조를 분석하거나 특정 계층만 확인할 때 사용하는 메서드다.

  • model.modules(): 모든 서브 모듈과 계층을 재귀적으로 반환
  • model.children(): 최상위 수준의 자식 모듈만 반환

복잡한 모델을 다룰 때 구조를 파악하는 데 유용하다.

학습 가능한 파라미터 정의하기

모델의 파라미터(가중치)는 학습 과정에서 자동으로 업데이트된다.
nn.Parameter를 사용하면 직접 학습 가능한 파라미터를 정의할 수 있다.

param = nn.Parameter(torch.randn(3, 3))

이렇게 정의된 파라미터는 model.parameters()에 자동으로 포함된다.

Tensor vs Parameter vs Buffer

   

타입 학습 여부 저장 여부 설명
Tensor X X 일반 데이터
Parameter O O 학습 대상이 되는 가중치
Buffer X O 학습 대상은 아니지만 저장은 필요한 값 (예: BatchNorm의 이동평균)

Buffer는 특히 BatchNorm 등의 계층에서 자주 등장하며, 저장/불러오기 시 차이를 이해하고 있어야 한다.

손실 함수 (Loss Function)

모델의 예측과 실제 값의 차이를 수치로 평가하는 함수다. 문제 유형에 따라 적절한 손실 함수를 선택해야 한다.

  • 이진 분류: BCELoss, BCEWithLogitsLoss
  • 다중 클래스 분류: CrossEntropyLoss, NLLLoss
  • 회귀: MSELoss, L1Loss, SmoothL1Loss
criterion = nn.CrossEntropyLoss()
loss = criterion(outputs, targets)

 

옵티마이저 (Optimizer)

옵티마이저는 손실 함수의 기울기를 사용해 모델의 파라미터를 업데이트한다.

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

실무에서는 Adam, SGD, RMSprop 등이 주로 사용된다.

학습률 스케줄러 (Scheduler)

학습률을 동적으로 조절하면 수렴 속도와 안정성을 높일 수 있다.

scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

성능 개선이 정체될 때 스케줄러를 활용하면 추가적인 향상을 기대할 수 있다.

전역 최소점 vs 지역 최적점

딥러닝의 손실 함수는 복잡한 지형을 가진다.

  • 전역 최소점: 손실이 가장 낮은 지점
  • 지역 최적점: 주변에서는 최소지만 전역은 아님

실제 학습에서는 지역 최적점에 도달하는 경우가 많고, 다양한 기법을 통해 더 나은 해를 찾는다.

optimizer.zero_grad()의 필요성

PyTorch는 기본적으로 기울기를 누적하는 방식이다. 따라서 매 학습 단계마다 optimizer.zero_grad()로 초기화해야 한다.

optimizer.zero_grad()

이 과정을 생략하면 학습이 불안정해질 수 있다.

자동 미분: loss.backward()

loss.backward()를 호출하면 파라미터들의 기울기가 자동으로 계산되어 .grad에 저장된다.

loss.backward()

PyTorch의 핵심 기능 중 하나인 autograd 덕분에 복잡한 계산 과정을 수동으로 할 필요가 없다.

텐서보드를 활용한 시각화

학습 과정을 시각적으로 추적할 때 텐서보드를 사용할 수 있다.

from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('runs/experiment_1')
writer.add_scalar('Loss/train', train_loss, epoch)
writer.close()

손실, 정확도, 파라미터 분포 등을 실시간으로 시각화할 수 있다.

model.train() vs model.eval()

PyTorch에서는 학습과 평가 모드를 명시적으로 전환해야 한다.

  • model.train(): 학습 모드 (Dropout 활성화, BatchNorm 학습)
  • model.eval(): 평가 모드 (Dropout 비활성화, BatchNorm은 이동평균 사용)

평가할 때 eval()을 설정하지 않으면 결과가 불안정해질 수 있다.

torch.no_grad()의 의미

검증/테스트 단계에서는 기울기 계산이 필요 없기 때문에 torch.no_grad()로 메모리 사용과 연산을 줄인다.

model.eval()
with torch.no_grad():
    outputs = model(inputs)

 

오늘의 회고

이번 정리를 통해 PyTorch의 기본 구조와 작동 방식을 다시 한번 탄탄히 다져볼 수 있었다. 텐서 생성부터 모델 정의, 손실 함수 설정, 옵티마이저 선택, 그리고 학습/평가 모드 전환까지 전부 딥러닝 프로젝트에서 반복적으로 사용하는 구성 요소들이지만, 막상 코드를 짤 때는 의외로 디테일한 부분에서 실수가 자주 생긴다.

특히 model.train()과 model.eval()의 구분, optimizer.zero_grad()의 위치, 그리고 텐서보드를 통한 시각화 같은 부분은 실습을 하면서 중요성을 체감하게 되는 요소였다. 단순히 문법을 외우는 것을 넘어서, 왜 이렇게 설계되어 있는지 이해하고 실제로 어떻게 활용하는지가 결국 실력을 가르는 기준이 된다고 느꼈다.

PyTorch는 단순한 도구가 아니라, 잘 다루면 매우 강력한 딥러닝 개발 환경이다. 이번 회고를 통해 기본기를 다시 다지고, 이후 더 복잡한 모델이나 최적화 전략을 다룰 때도 탄탄한 기반 위에서 확장할 수 있을 것 같다.