BioPlayground

🧬
목록으로

PyTorch 기본 — 원리를 코드로 옮기는 첫 걸음

텐서·autograd·nn.Module·DataLoader·optimizer로 편 #2~4 원리를 실제 코드로. 세포 유형 분류기를 처음부터 짜면서 훈련 루프의 실체를 이해합니다.

중급
|
22
|
검증 완료 (2026-07)
진행률0/15 (0%)

PyTorch 기본 — 원리를 코드로 옮기는 첫 걸음

이 토픽을 마치면

편 #2~4에서 배운 신경망의 원리(다이얼·경사하강법·역전파)를 실제 PyTorch 코드로 구현할 수 있게 됩니다. Phase 3 도구 편의 시작. 이 편이 편 #13(HuggingFace·API)와 편 #14(Claude Code·Cursor) 실습의 기초.

목표는 짧은 코드 조각을 순회하는 것이 아니라, 하나의 완전한 훈련 루프를 처음부터 끝까지 짜는 것. 세포 유형 분류기(편 #3의 시나리오)를 예시로.


PyTorch가 무엇인가

PyTorch 는 Meta AI가 개발한 딥러닝 프레임워크. 신경망 훈련·추론에 필요한 부품들을 파이썬 API로 제공합니다.

핵심 부품 4개.

  • Tensor: 다차원 배열. NumPy와 유사하지만 GPU 가속 + 자동 미분 지원.
  • Autograd: 편 #4의 역전파를 자동화. 계산 그래프를 백그라운드에 만들고 backward() 한 줄로 모든 편미분 계산.
  • nn.Module: 신경망 층·모델을 정의하는 클래스 인터페이스.
  • Optimizer: 편 #3의 Adam·AdamW 등 옵티마이저 구현.

이 부품들을 조립하면 훈련 루프가 됩니다.

설치. Colab에는 이미 있음. 로컬은 pip install torch. GPU 사용 시 CUDA 버전에 맞는 wheel 지정.

대안. TensorFlow, JAX, MLX(Apple). 최근 연구·실무 표준은 PyTorch. 대부분의 오픈소스 모델(LLaMA·Mistral·Qwen 등)이 PyTorch로 배포.


Tensor — 편 #2의 다이얼과 픽셀을 담는 그릇

Tensor 는 다차원 실수 배열. 편 #2에서 이야기한 층 활성값·가중치·편향이 모두 tensor.

python
import torch
# 스칼라 (0차원)
x = torch.tensor(3.14)
# 벡터 (1차원)
v = torch.tensor([1.0, 2.0, 3.0])
# 행렬 (2차원)
m = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
# 3차원 (예: 배치 32, 시퀀스 100, 임베딩 512)
t = torch.randn(32, 100, 512)
print(t.shape) # torch.Size([32, 100, 512])

dtype과 device. Tensor는 dtype(자료형)과 device(CPU/GPU)를 가집니다.

python
# fp32 CPU (기본)
x = torch.randn(1000, 1000)
# fp16 GPU
y = torch.randn(1000, 1000, dtype=torch.float16, device="cuda")
# device 이동
x_gpu = x.to("cuda")
x_cpu = y.to("cpu")

GPU에서 훨씬 빠릅니다. 실무 훈련은 대부분 GPU.

tensor 연산. NumPy와 거의 동일 API.

python
a = torch.randn(3, 4)
b = torch.randn(4, 5)
c = a @ b # 행렬곱, shape (3, 5)
d = a + 1 # 브로드캐스트
e = a.sum(dim=1) # 행별 합, shape (3,)
f = a.mean() # 스칼라
g = a.relu() # 원소별 ReLU

편 #2·5에서 이야기한 활성 함수(ReLU·GELU·Sigmoid 등)가 모두 tensor 메서드로 제공.


Autograd — 편 #4의 역전파를 한 줄로

Tensor에 requires_grad=True를 붙이면 PyTorch가 그 tensor를 편미분 대상으로 추적. 편 #4의 계산 그래프가 자동으로 만들어짐.

python
# 파라미터 (편미분 대상)
w = torch.randn(3, requires_grad=True)
b = torch.randn(1, requires_grad=True)
# 데이터
x = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor(10.0)
# 순전파
y_pred = w @ x + b
loss = (y_pred - y) ** 2
# 역전파 - 이 한 줄로 편미분 다 계산
loss.backward()
# 계산된 기울기
print(w.grad) # ∂loss/∂w
print(b.grad) # ∂loss/∂b

편 #4의 4단계가 여기 다 들어 있음.

  • 순전파 (y_pred = w @ x + b, loss = ...): 계산 그래프 자동 구축.
  • 손실 계산 ((y_pred - y) ** 2).
  • 역전파 (loss.backward()): 그래프 역방향 순회, 모든 편미분 계산.
  • 파라미터 업데이트: 다음에 다룸.

중요한 관례. .backward() 호출 후 파라미터의 .grad누적됩니다. 다음 스텝 전에 .grad를 0으로 초기화해야 함(뒤의 optimizer.zero_grad()).


nn.Module — 편 #5의 모델 정의

큰 모델은 nn.Module 클래스를 상속해 정의합니다. 편 #3의 세포 유형 분류기를 실제로 짜봅시다.

python
import torch.nn as nn
class CellTypeClassifier(nn.Module):
def __init__(self, n_genes, n_types, hidden=512):
super().__init__()
self.encoder = nn.Sequential(
nn.Linear(n_genes, hidden),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(hidden, hidden // 2),
nn.ReLU(),
nn.Linear(hidden // 2, n_types)
)
def forward(self, x):
return self.encoder(x)
model = CellTypeClassifier(n_genes=20000, n_types=25)
print(sum(p.numel() for p in model.parameters()))
# 파라미터 수 출력

해설.

  • __init__: 층 정의. nn.Linear(in, out)y = W·x + b. 편 #2의 완전 연결층.
  • nn.Sequential: 층들을 순차 연결.
  • nn.ReLU(), nn.Dropout(0.3): 활성 함수·정규화.
  • forward: 순전파 로직. PyTorch가 자동으로 이 함수를 호출.

파라미터 자동 추적. nn.Linear 안의 가중치·편향이 자동으로 requires_grad=True가 되고 model.parameters()에 포함됩니다. 직접 torch.tensor(..., requires_grad=True)로 관리할 필요 없음.

호출. 모델은 함수처럼.

python
x = torch.randn(32, 20000) # 배치 32, 20000 유전자
logits = model(x) # forward 자동 호출
print(logits.shape) # torch.Size([32, 25])

Loss와 Optimizer — 편 #3의 경사하강법

편 #3에서 배운 손실 함수와 옵티마이저.

Loss.

python
# 분류 문제 - 편 #3 A.1 CE
loss_fn = nn.CrossEntropyLoss()
# 회귀 문제 - 편 #3 A.1 MSE
loss_fn = nn.MSELoss()

Optimizer.

python
# SGD - 편 #3 A.3
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Adam - 편 #3 A.7
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# AdamW - 편 #3 A.8 (LLM 훈련 표준)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)

학습률 스케줄러 (편 #3 A.9).

python
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)

에폭마다 scheduler.step() 호출로 학습률 자동 감소.


훈련 루프 — 모든 걸 합치기

지금까지의 부품을 하나로. 완전한 훈련 루프.

python
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
# 데이터 준비 (가짜 예시)
n_samples = 10000
n_genes = 20000
n_types = 25
X = torch.randn(n_samples, n_genes)
y = torch.randint(0, n_types, (n_samples,))
dataset = TensorDataset(X, y)
train_loader = DataLoader(dataset, batch_size=128, shuffle=True)
# 모델
model = CellTypeClassifier(n_genes, n_types)
model = model.to("cuda")
# 손실·옵티마이저
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)
# 훈련 루프
n_epochs = 10
for epoch in range(n_epochs):
model.train()
total_loss = 0.0
for batch_x, batch_y in train_loader:
batch_x = batch_x.to("cuda")
batch_y = batch_y.to("cuda")
# 순전파 (편 #2)
logits = model(batch_x)
loss = loss_fn(logits, batch_y)
# 역전파 (편 #4)
optimizer.zero_grad() # 이전 스텝 grad 초기화
loss.backward() # autograd
# 파라미터 업데이트 (편 #3)
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch}: loss = {total_loss / len(train_loader):.4f}")

이 루프가 편 #3의 그림과 정확히 대응.

text
초기 파라미터 랜덤 설정             ← 모델 생성 시 자동
반복 (수십만~수백만 번):
    미니배치 무작위 선택            ← DataLoader
    미니배치에 대해 전방 전파       ← logits = model(batch_x)
    예측과 정답으로 비용 계산       ← loss = loss_fn(logits, batch_y)
    역전파 → 기울기 계산           ← loss.backward()
    옵티마이저로 파라미터 업데이트  ← optimizer.step()

PyTorch가 편 #2~4의 원리를 그대로 API로 노출.


Dataset과 DataLoader — 편 #3의 미니배치

편 #3에서 미니배치 SGD가 왜 필요한지 이야기했습니다. PyTorch가 이걸 Dataset + DataLoader로 지원.

Dataset. __len____getitem__을 정의한 클래스.

python
from torch.utils.data import Dataset
class ScRNADataset(Dataset):
def __init__(self, expression_matrix, labels):
self.expr = expression_matrix
self.labels = labels
def __len__(self):
return len(self.labels)
def __getitem__(self, idx):
return self.expr[idx], self.labels[idx]

DataLoader. 배치·셔플·병렬 로딩 처리.

python
train_loader = DataLoader(
dataset,
batch_size=128,
shuffle=True, # 매 epoch 순서 섞음
num_workers=4, # 병렬 로딩 (CPU 코어 활용)
pin_memory=True, # GPU 전송 가속
drop_last=True # 마지막 불완전 배치 제외
)

바이오 실무. scRNA-seq · 이미지 · 서열 데이터 등 도메인별 커스텀 Dataset 구현. Scanpy·AnnData·PyTorch Geometric 등이 도메인 특화 wrapper 제공.


GPU 활용 — 빠른 훈련

GPU는 CPU 대비 수십~수백 배 빠름. 실무 훈련은 대부분 GPU.

python
# 사용 가능한 GPU 확인
print(torch.cuda.is_available())
print(torch.cuda.device_count())
# 텐서·모델 이동
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
batch_x = batch_x.to(device)

Multi-GPU. 큰 모델·데이터 셋은 여러 GPU 활용.

  • DataParallel (DP): 한 노드 여러 GPU. 간단하지만 병목 있음.
  • DistributedDataParallel (DDP): 표준. 각 GPU가 별도 프로세스, 그래디언트 all-reduce.
  • Fully Sharded Data Parallel (FSDP): 파라미터·옵티마이저 상태도 분산. 70B+ 모델 훈련에 필수.

Apple Silicon. mps device 지원. device = "mps". 개인 랩탑에서 소형 실험 가능.


Mixed Precision — 훈련 속도·메모리 절감

FP32 대신 FP16 또는 BF16으로 계산해 속도·메모리 절감.

python
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for batch_x, batch_y in train_loader:
optimizer.zero_grad()
with autocast(dtype=torch.bfloat16):
logits = model(batch_x)
loss = loss_fn(logits, batch_y)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()

BF16 vs FP16.

  • FP16: 반정밀도. 오버플로 위험 있어 GradScaler 필요.
  • BF16 (bfloat16): 지수 범위가 FP32와 같음. 오버플로 위험 낮음. A100·H100 계열 GPU 지원. 실무 표준으로 이동 중.

효과. 훈련 시간 2~4배 빠름, 메모리 절반. 정확도 손실 미미.


저장·로드 — Checkpoint

훈련 중간·최종 상태를 저장.

python
# 저장
torch.save({
"epoch": epoch,
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
"loss": total_loss,
}, "checkpoint.pt")
# 로드
checkpoint = torch.load("checkpoint.pt")
model.load_state_dict(checkpoint["model_state_dict"])
optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
start_epoch = checkpoint["epoch"] + 1

훈련이 며칠 걸리면 반드시 정기 checkpoint. 크래시·정전 대비.


Inference — 훈련 완료 후 사용

훈련이 끝난 모델로 예측.

python
model.eval() # dropout·batchnorm을 추론 모드로
with torch.no_grad(): # 편미분 계산 비활성화 (메모리·속도)
for batch_x, _ in test_loader:
batch_x = batch_x.to(device)
logits = model(batch_x)
preds = logits.argmax(dim=-1)
# ... preds 사용

no_grad 안에서는 autograd가 계산 그래프를 만들지 않아 메모리·속도 절약.

Deployment. 훈련된 모델을 프로덕션에 배포하는 방법 여러 가지.

  • TorchScript: PyTorch 모델을 JIT 컴파일. C++·모바일 배포.
  • ONNX: 프레임워크 무관 포맷. TensorRT·OpenVINO 최적화.
  • HuggingFace Hub: 모델 공유·서빙.
  • vLLM·TGI·SGLang: LLM 서빙 특화 프레임워크.

바이오 실무 팁

데이터 로딩

scRNA-seq: scanpy + AnnData → PyTorch Dataset wrapper. 단백질 서열: biopython 파싱 후 Dataset. 이미지(병리·현미경): torchvision.datasets.ImageFolder 활용, augmentation 적용.

훈련 최적화

  • Gradient accumulation: GPU 메모리 부족 시 큰 배치 시뮬레이션.
  • Gradient checkpointing (편 #4 A.7): 활성값 재계산으로 메모리 절약.
  • DeepSpeed·FSDP: 큰 모델 훈련.

실험 관리

  • Weights & Biases (wandb): 실험 로그·시각화.
  • MLflow: 오픈소스 실험 추적.
  • Lightning: 훈련 루프 boilerplate 감소.

PyTorch Lightning. 훈련 루프의 반복 코드를 감춰주는 wrapper.

python
import pytorch_lightning as pl
class CellClassifierLightning(pl.LightningModule):
def __init__(self, model, lr=1e-3):
super().__init__()
self.model = model
self.lr = lr
self.loss_fn = nn.CrossEntropyLoss()
def training_step(self, batch, batch_idx):
x, y = batch
logits = self.model(x)
loss = self.loss_fn(logits, y)
self.log("train_loss", loss)
return loss
def configure_optimizers(self):
return torch.optim.AdamW(self.parameters(), lr=self.lr)
trainer = pl.Trainer(max_epochs=10, accelerator="gpu", devices=1)
trainer.fit(model_lightning, train_loader)

Multi-GPU·mixed precision·checkpoint를 자동으로. 프로덕션 훈련에 편함.


바이오 응용 시나리오

시나리오 1 — 세포 유형 분류기 실전

편 #3 시나리오 1을 완전 구현. AnnData → PyTorch Dataset → 훈련. Scanpy tutorial 참조.

시나리오 2 — 단백질 접촉 예측 (mini AlphaFold)

단백질 서열 → 어텐션 기반 신경망 → 아미노산 쌍 접촉 확률. 편 #5·6 개념 실습. 소형 데이터셋(예: CATH)으로 훈련 가능.

시나리오 3 — 조직 병리 이미지 분류

편 #2·6의 병리 슬라이드 시나리오 실전. ResNet-50 fine-tuning. Torchvision.datasets 활용.


핵심 정리

  • PyTorch 4부품: Tensor·Autograd·nn.Module·Optimizer. 편 #2~4의 원리를 그대로 API로.
  • 훈련 루프 5단계: forward · loss · backward · zero_grad · step. 편 #3의 그림 그대로.
  • DataLoader로 배치 처리, to(device) 로 GPU 활용.
  • Mixed precision(BF16)으로 훈련 2~4배 가속.
  • Checkpoint 정기 저장 필수.
  • Multi-GPU (DDP·FSDP)로 대형 모델 훈련.
  • Lightning·wandb 같은 도구로 실무 편의.

다음 개념


📐 부록 — 전문가를 위한 실전 팁·수식

난이도: 매우 어려움 (Very Hard) 대상: PyTorch 실전 훈련 최적화·시스템 배경 독자

A.1 Autograd의 내부 동작

requires_grad=True인 tensor를 연산에 사용하면 각 연산이 그래프 노드로 기록됨.

Function 클래스. 각 연산(add·mul·matmul 등)은 torch.autograd.Function 서브클래스. forwardbackward static method 정의.

커스텀 autograd:

python
class MyReLU(torch.autograd.Function):
@staticmethod
def forward(ctx, x):
ctx.save_for_backward(x)
return x.clamp(min=0)
@staticmethod
def backward(ctx, grad_output):
x, = ctx.saved_tensors
grad_input = grad_output.clone()
grad_input[x < 0] = 0
return grad_input

ctx.save_for_backward로 backward 시 필요한 tensor 저장. 편 #4의 활성값 저장이 이 메커니즘.

A.2 Gradient Checkpointing

편 #4 A.7. torch.utils.checkpoint.

python
from torch.utils.checkpoint import checkpoint
class MyBlock(nn.Module):
def forward(self, x):
# 이 함수의 활성값을 저장하지 않고 backward 때 재계산
return checkpoint(self._forward, x, use_reentrant=False)
def _forward(self, x):
# 실제 계산
return self.layers(x)

메모리 O(√L) 수준. 시간 1.5배. 대형 모델 필수.

A.3 Mixed Precision의 수치

FP32: exp 8, mantissa 23. 범위 ~1e-38 ~ 1e+38, 정밀도 ~7 자릿수.

FP16: exp 5, mantissa 10. 범위 ~6e-5 ~ 6.5e+4, 정밀도 ~3 자릿수. 오버플로 위험.

BF16: exp 8, mantissa 7. 범위 FP32와 같음, 정밀도 ~2 자릿수. 오버플로 안전.

Loss Scaling (FP16 필수):

text
scaled_loss = loss * scale_factor
scaled_loss.backward()  # gradient에 scale_factor 곱해진 상태
# gradient를 unscaled 후 optimizer step

BF16은 loss scaling 불필요.

A.4 DDP 동기화

All-Reduce 그래디언트 동기화. 각 GPU가 자기 미니배치의 그래디언트 계산 후, 모든 GPU에서 그래디언트 평균 계산.

시간 복잡도: O(P / bandwidth). P = 파라미터 수.

NCCL 백엔드. NVIDIA 최적화 라이브러리. GPU 간 통신에 InfiniBand·NVLink 활용.

Bucketing. 그래디언트를 청크로 묶어 통신·계산 오버랩. torch.nn.parallel.DistributedDataParallel 자동.

A.5 FSDP (Fully Sharded Data Parallel)

파라미터·optimizer state를 GPU 간 분할.

python
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
model = FSDP(model, sharding_strategy=ShardingStrategy.FULL_SHARD)

메모리 절감. 각 GPU가 파라미터 1/N만 유지. 70B 파라미터 모델이 8 × 40GB GPU에 들어감.

통신 오버헤드. Forward·backward 시 파라미터 gather·release. NCCL 최적화 필수.

A.6 커스텀 CUDA Kernel

일부 연산은 PyTorch 내장으로 최적이 아님. Triton·CUDA C로 커스텀 커널 작성.

Flash Attention 2. 편 #6 A.8의 online softmax를 Triton·CUDA로 구현.

python
from flash_attn import flash_attn_func
output = flash_attn_func(q, k, v, causal=True)

nn.functional.scaled_dot_product_attention의 2~4배 빠름.

최근 트렌드. torch.compile (PyTorch 2.0)이 자동으로 커널 최적화. model = torch.compile(model). 대부분 케이스 2배+ 개선.

A.7 Profiling

PyTorch Profiler:

python
with torch.profiler.profile(
activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA],
schedule=torch.profiler.schedule(wait=1, warmup=1, active=3),
on_trace_ready=torch.profiler.tensorboard_trace_handler("./log")
) as prof:
for step, batch in enumerate(train_loader):
# ... 훈련 스텝
prof.step()

TensorBoard로 시각화. 병목(느린 커널·CPU-GPU 전송·데이터 로딩) 식별.

A.8 재현성 (Reproducibility)

같은 실험 반복 시 결과 재현.

python
import torch
import random
import numpy as np
def set_seed(seed):
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
random.seed(seed)
np.random.seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

완전 재현은 어려움 (일부 CUDA 연산의 비결정성). 실무는 seed 고정 + hardware·PyTorch 버전 기록.

A.9 Debugging Tips

NaN 감지:

python
torch.autograd.set_detect_anomaly(True)

Backward 시 NaN 발생하면 어느 연산에서인지 트레이스백.

Gradient Clipping (편 #4 A.8):

python
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

loss.backward() 후, optimizer.step() 전.

Batch size finder (Lightning): 최대 가능한 배치 크기 자동 탐색.

A.10 최신 트렌드

torch.compile (PyTorch 2.0+): JIT 컴파일로 자동 최적화.

torch.export: 모델을 그래프로 추출 (ONNX·TensorRT·모바일 배포).

AOT autograd: 컴파일 시점에 backward 그래프 생성.

Nested Tensor: 가변 길이 시퀀스 처리 (padding 없이).

FunctorchModule → torch.func: JAX 스타일 함수형 API.

이 도구들이 최신 대형 모델 훈련·서빙의 표준이 되어가는 중.


참고 자료

본 편의 콘텐츠·시나리오·비유·수치는 모두 BioPlayground 자체 개발이며, 아래는 개념 학습에 도움이 되는 외부 참고 자료입니다.

  • PyTorch 공식 문서: pytorch.org/docs
  • PyTorch 60분 튜토리얼: pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
  • PyTorch Lightning: lightning.ai
  • HuggingFace Transformers: huggingface.co/docs/transformers
  • Flash Attention: github.com/Dao-AILab/flash-attention
  • DeepSpeed: deepspeed.ai
  • PyTorch Profiler: pytorch.org/tutorials/recipes/recipes/profiler_recipe.html
  • Scanpy: scanpy.readthedocs.io — scRNA-seq 표준 라이브러리
  • PyTorch Geometric: pyg.org — 그래프 신경망 (분자·단백질)
  • TorchVision: pytorch.org/vision — 이미지 도메인

편 #12로 PyTorch 기본이 정리되었습니다. 편 #13에서는 HuggingFace 모델 허브와 OpenAI·Anthropic API 활용을 다룹니다.