PyTorch 입문 — Keras와 무엇이 다를까?

nn.Module로 모델을 직접 설계하고 zero_grad, backward, step으로 이어지는 학습 루프를 처음부터 구현하며 PyTorch의 핵심 흐름 정리

2026.04.10

PyTorchnn.ModuleforwardDataLoaderautogradtraining loopMNISTdeep learning

0. 시리즈

제목역할
1편NumPy 완벽 정리 — 숫자 배열 다루기데이터 기초
2편Pandas 완벽 정리 — 데이터 불러오고 정리하기데이터 전처리
3편Matplotlib / Seaborn — 데이터 시각화시각화
4편Scikit-learn — 머신러닝 실습 입문ML 실습
5편TensorFlow / Keras — 딥러닝 모델 만들기DL 실습
6편⬅️PyTorch 입문 — Keras와 무엇이 다를까?DL 심화

1. PyTorch란?

1-1. PyTorch가 뭔가요?

PyTorch(파이토치) 는 Meta(Facebook)가 2017년에 공개한 딥러닝 프레임워크입니다. 현재 AI 연구 분야에서 가장 많이 쓰이는 프레임워크로, 전 세계 AI 논문의 70% 이상이 PyTorch 기반 코드로 공개됩니다.

Keras가 "쉽고 빠르게 모델을 만드는 도구"라면, PyTorch는 "모델 내부를 직접 설계하고 제어하는 도구" 입니다.

1-2. Keras와 결정적인 차이

비교 항목KerasPyTorch
그래프 방식정적 그래프 (미리 설계 후 실행)동적 그래프 (실행하면서 즉시 생성)
코드 방식선언형 (레이어를 쌓음)명령형 (클래스로 직접 설계)
디버깅불편함일반 Python처럼 쉬움
자유도낮음높음
학습 루프자동 (model.fit)직접 구현

💡 동적 그래프 란 코드가 실행될 때마다 연산 그래프가 새로 만들어지는 방식입니다. 덕분에 일반 Python 디버거로 모델 내부를 바로 들여다볼 수 있습니다.

1-3. 왜 연구자들은 PyTorch를 선호하는가?

  • 유연성 — 표준 패턴을 벗어난 실험적 구조도 자유롭게 구현 가능
  • 디버깅 편의printbreakpoint()로 중간 값을 바로 확인
  • 논문 구현 — 대부분의 최신 논문 공식 코드가 PyTorch 기반
  • 커뮤니티 — HuggingFace, Lightning 등 주요 생태계가 PyTorch 중심

1-4. 설치 및 임포트

bash# CPU 버전
pip install torch torchvision

# GPU 버전 (CUDA 버전에 맞게 설치 — pytorch.org에서 확인)
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
pythonimport torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

print(torch.__version__)          # 예: 2.2.0
print(torch.cuda.is_available())  # GPU 사용 가능 여부: True / False

2. PyTorch 핵심 개념 — Tensor

2-1. Tensor란?

PyTorch의 기본 자료구조는 Tensor(텐서) 입니다. 1편에서 배운 NumPy의 ndarray와 거의 동일하지만, 결정적인 차이가 있습니다.

비교 항목NumPy ndarrayPyTorch Tensor
GPU 연산❌ 불가✅ 가능
자동 미분❌ 불가✅ 가능 (autograd)
딥러닝 학습

2-2. Tensor 생성하는 방법

pythonimport torch

a = torch.tensor([1, 2, 3, 4, 5])
print(a)          # tensor([1, 2, 3, 4, 5])
print(a.dtype)    # torch.int64

b = torch.tensor([1.0, 2.0, 3.0])
print(b.dtype)    # torch.float32

torch.zeros((3, 3))      # 0으로 채운 3×3
torch.ones((2, 4))       # 1로 채운 2×4
torch.rand((3, 3))       # 0~1 난수
torch.arange(0, 10, 2)  # [0, 2, 4, 6, 8]

print(b.shape)    # torch.Size([3])
print(b.ndim)     # 1

2-3. NumPy 배열 ↔ Tensor 변환

pythonimport numpy as np
import torch

# NumPy → Tensor
arr = np.array([1, 2, 3])
tensor = torch.from_numpy(arr)
print(tensor)  # tensor([1, 2, 3])

# Tensor → NumPy
arr2 = tensor.numpy()
print(arr2)    # [1 2 3]

⚠️ from_numpy로 변환하면 메모리를 공유합니다. 하나를 수정하면 다른 것도 바뀝니다. 독립적인 복사본이 필요하면 tensor.clone().numpy()를 사용하세요.

2-4. GPU로 Tensor 올리기

pythondevice = "cuda" if torch.cuda.is_available() else "cpu"
print(f"사용 장치: {device}")

tensor = torch.tensor([1.0, 2.0, 3.0])
tensor = tensor.to(device)

model = model.to(device)

X_batch = X_batch.to(device)
y_batch = y_batch.to(device)

3. Keras와 코드 방식 비교

3-1. Keras — 선언형 (레이어를 쌓는 방식)

pythonfrom tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Dropout

model = Sequential([
    Dense(128, activation="relu", input_shape=(784,)),
    Dropout(0.3),
    Dense(64, activation="relu"),
    Dense(10, activation="softmax")
])

구조가 고정되어 있고, 학습은 model.fit 한 줄로 끝납니다. 빠르고 간결하지만 내부를 커스터마이징하기 어렵습니다.

3-2. PyTorch — 명령형 (클래스로 직접 설계)

pythonimport torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 128)
        self.dropout = nn.Dropout(0.3)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = MyModel()

코드는 더 길지만, 데이터 흐름을 직접 제어할 수 있어 복잡한 구조도 자유롭게 구현 가능합니다.

3-3. 같은 모델, 두 가지 코드 나란히 비교

# Keras                          # PyTorch
Sequential([                     class MyModel(nn.Module):
  Dense(128, activation="relu")    def __init__(self):
  Dropout(0.3)                       self.fc1 = nn.Linear(784, 128)
  Dense(64, activation="relu")       self.drop = nn.Dropout(0.3)
  Dense(10, activation="softmax")    self.fc2 = nn.Linear(128, 64)
])                                   self.fc3 = nn.Linear(64, 10)

                                   def forward(self, x):
                                     x = F.relu(self.fc1(x))
                                     x = self.drop(x)
                                     x = F.relu(self.fc2(x))
                                     return self.fc3(x)

4. PyTorch로 모델 만들기

4-1. nn.Module — 모든 모델의 기본 클래스

PyTorch의 모든 모델은 nn.Module을 상속받아 만듭니다.

pythonclass MyModel(nn.Module):
    def __init__(self):   # 레이어 정의
        super().__init__()
        ...

    def forward(self, x): # 데이터 흐름 정의
        ...
        return x
  • __init__ — 사용할 레이어들을 미리 선언
  • forward — 데이터가 모델을 통과하는 순서를 직접 작성

4-2. 주요 레이어

pythonimport torch.nn as nn
import torch.nn.functional as F

nn.Linear(in, out)       # 완전 연결층 (Keras의 Dense)
nn.ReLU()                # 활성화 함수
nn.Dropout(p=0.3)        # 드롭아웃
nn.BatchNorm1d(128)      # 배치 정규화
nn.Sigmoid()             # 이진 분류 출력
nn.Softmax(dim=1)        # 다중 분류 출력

# 함수형으로 사용 (forward 안에서)
F.relu(x)
F.dropout(x, p=0.3, training=self.training)

4-3. 모델 구조 확인

pythonmodel = MyModel()
print(model)

total = sum(p.numel() for p in model.parameters())
print(f"전체 파라미터 수: {total:,}")

5. 학습 루프 직접 구현하기

5-1. Keras vs PyTorch 학습 방식

python# Keras — 한 줄
model.fit(X_train, y_train, epochs=20, batch_size=64)

# PyTorch — 직접 구현 (하지만 내부를 완전히 제어 가능)
for epoch in range(20):
    for X_batch, y_batch in dataloader:
        optimizer.zero_grad()
        output = model(X_batch)
        loss = criterion(output, y_batch)
        loss.backward()
        optimizer.step()

5-2. Dataset & DataLoader

데이터를 배치 단위로 나눠 공급합니다.

pythonfrom torch.utils.data import TensorDataset, DataLoader
import torch

X_tensor = torch.tensor(X_train, dtype=torch.float32)
y_tensor = torch.tensor(y_train, dtype=torch.long)

dataset = TensorDataset(X_tensor, y_tensor)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)

5-3. 손실 함수와 옵티마이저 설정

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

5-4. 전체 학습 루프

pythonimport torch

device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)

train_losses = []

for epoch in range(20):
    model.train()
    running_loss = 0.0

    for X_batch, y_batch in dataloader:
        X_batch = X_batch.to(device)
        y_batch = y_batch.to(device)

        optimizer.zero_grad()       # 1. 기울기 초기화 (반드시 먼저!)
        output = model(X_batch)     # 2. 순전파 (Forward)
        loss = criterion(output, y_batch)  # 3. 손실 계산
        loss.backward()             # 4. 역전파 (Backward)
        optimizer.step()            # 5. 가중치 업데이트

        running_loss += loss.item()

    avg_loss = running_loss / len(dataloader)
    train_losses.append(avg_loss)
    print(f"Epoch {epoch+1:2d}/20 — Loss: {avg_loss:.4f}")

💡 zero_grad()를 왜 먼저 하나요? PyTorch는 기울기를 자동으로 누적합니다. 매 배치마다 초기화하지 않으면 이전 배치의 기울기가 남아서 학습이 잘못됩니다.

5-5. 학습 곡선 시각화

pythonimport matplotlib.pyplot as plt

plt.plot(range(1, 21), train_losses, marker="o", color="blue")
plt.title("PyTorch 학습 손실 변화")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid(True)
plt.show()

6. 분류 실습 — MNIST 손글씨 인식

6-1. torchvision으로 MNIST 데이터 불러오기

pythonfrom torchvision import datasets, transforms
from torch.utils.data import DataLoader

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

train_dataset = datasets.MNIST(root="./data", train=True,
                                transform=transform, download=True)
test_dataset  = datasets.MNIST(root="./data", train=False,
                                transform=transform)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader  = DataLoader(test_dataset,  batch_size=64, shuffle=False)

6-2. 모델 설계 및 학습

pythonclass MNISTModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 10)
        self.dropout = nn.Dropout(0.3)

    def forward(self, x):
        x = x.view(-1, 784)
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = MNISTModel().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

for epoch in range(10):
    model.train()
    for X_batch, y_batch in train_loader:
        X_batch, y_batch = X_batch.to(device), y_batch.to(device)
        optimizer.zero_grad()
        loss = criterion(model(X_batch), y_batch)
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}/10 완료")

6-3. Keras와 비교

항목KerasPyTorch
모델 정의Sequential([...])class 상속
학습model.fit(...)직접 루프 작성
코드 길이짧음길음
정확도 (MNIST)약 97~98%약 97~98% (동일)
디버깅 편의낮음높음

6-4. 정확도 평가 및 시각화

pythonimport numpy as np
import matplotlib.pyplot as plt

model.eval()
correct = 0
total = 0
all_preds = []
all_labels = []

with torch.no_grad():
    for X_batch, y_batch in test_loader:
        X_batch, y_batch = X_batch.to(device), y_batch.to(device)
        output = model(X_batch)
        preds = output.argmax(dim=1)
        correct += (preds == y_batch).sum().item()
        total += y_batch.size(0)
        all_preds.extend(preds.cpu().numpy())
        all_labels.extend(y_batch.cpu().numpy())

print(f"테스트 정확도: {correct / total:.4f}")

test_images, test_labels = next(iter(test_loader))
model.eval()
with torch.no_grad():
    preds = model(test_images.to(device)).argmax(dim=1).cpu()

fig, axes = plt.subplots(2, 5, figsize=(14, 6))
for i, ax in enumerate(axes.flat):
    ax.imshow(test_images[i].squeeze(), cmap="gray")
    color = "blue" if preds[i] == test_labels[i] else "red"
    ax.set_title(f"예측:{preds[i].item()} / 정답:{test_labels[i].item()}", color=color)
    ax.axis("off")
plt.suptitle("파란색: 정답, 빨간색: 오답")
plt.show()

7. PyTorch vs Keras 최종 정리

7-1. 언제 무엇을 쓰는가?

상황추천
빠르게 프로토타입 만들기✅ Keras
서비스 배포용 모델✅ Keras (TFLite, TF Serving 지원)
논문 구현 / 재현✅ PyTorch
커스텀 학습 루프 필요✅ PyTorch
연구 및 실험적 모델✅ PyTorch
HuggingFace 모델 사용✅ PyTorch

7-2. 입문자 학습 경로 추천

입문      → Keras로 전체 흐름 파악
          (모델 설계 → 학습 → 평가)
              ↓
중급      → PyTorch로 내부 구조 이해
          (Tensor, 학습 루프, 자동 미분)
              ↓
심화      → CNN, RNN, Transformer 구현
              ↓
최신 트렌드 → HuggingFace로 LLM Fine-tuning

8. 마무리

8-1. 오늘 배운 것 한눈에 정리

개념핵심 내용
TensorNumPy처럼 사용하지만 GPU + 자동 미분 지원
nn.Module모든 PyTorch 모델의 기본 클래스
forward데이터 흐름을 직접 정의하는 메서드
학습 루프zero_grad → forward → loss → backward → step
model.eval()평가 시 Dropout 비활성화 필수
torch.no_grad()평가 시 기울기 계산 끄기 (메모리 절약)