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.
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)를 가집니다.
# fp32 CPU (기본)x = torch.randn(1000, 1000)
# fp16 GPUy = 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.
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의 계산 그래프가 자동으로 만들어짐.
# 파라미터 (편미분 대상)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 + bloss = (y_pred - y) ** 2
# 역전파 - 이 한 줄로 편미분 다 계산loss.backward()
# 계산된 기울기print(w.grad) # ∂loss/∂wprint(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의 세포 유형 분류기를 실제로 짜봅시다.
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)로 관리할 필요 없음.
호출. 모델은 함수처럼.
x = torch.randn(32, 20000) # 배치 32, 20000 유전자logits = model(x) # forward 자동 호출print(logits.shape) # torch.Size([32, 25])Loss와 Optimizer — 편 #3의 경사하강법
편 #3에서 배운 손실 함수와 옵티마이저.
Loss.
# 분류 문제 - 편 #3 A.1 CEloss_fn = nn.CrossEntropyLoss()
# 회귀 문제 - 편 #3 A.1 MSEloss_fn = nn.MSELoss()Optimizer.
# SGD - 편 #3 A.3optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Adam - 편 #3 A.7optimizer = 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).
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)에폭마다 scheduler.step() 호출로 학습률 자동 감소.
훈련 루프 — 모든 걸 합치기
지금까지의 부품을 하나로. 완전한 훈련 루프.
import torchimport torch.nn as nnfrom torch.utils.data import DataLoader, TensorDataset
# 데이터 준비 (가짜 예시)n_samples = 10000n_genes = 20000n_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 = 10for 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의 그림과 정확히 대응.
초기 파라미터 랜덤 설정 ← 모델 생성 시 자동
반복 (수십만~수백만 번):
미니배치 무작위 선택 ← 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__을 정의한 클래스.
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. 배치·셔플·병렬 로딩 처리.
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.
# 사용 가능한 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으로 계산해 속도·메모리 절감.
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
훈련 중간·최종 상태를 저장.
# 저장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 — 훈련 완료 후 사용
훈련이 끝난 모델로 예측.
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.
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 같은 도구로 실무 편의.
다음 개념
- 편 #13
huggingface-and-openai— HuggingFace 모델 허브와 상용 API 활용. - 편 #14
claude-code-and-cursor— AI 코딩 에이전트 실전. - 편 #15
bio-ai-integration— 이 모든 것을 바이오 파이프라인에 통합.
📐 부록 — 전문가를 위한 실전 팁·수식
난이도: 매우 어려움 (Very Hard) 대상: PyTorch 실전 훈련 최적화·시스템 배경 독자
A.1 Autograd의 내부 동작
requires_grad=True인 tensor를 연산에 사용하면 각 연산이 그래프 노드로 기록됨.
Function 클래스. 각 연산(add·mul·matmul 등)은 torch.autograd.Function 서브클래스. forward와 backward static method 정의.
커스텀 autograd:
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_inputctx.save_for_backward로 backward 시 필요한 tensor 저장. 편 #4의 활성값 저장이 이 메커니즘.
A.2 Gradient Checkpointing
편 #4 A.7. torch.utils.checkpoint.
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 필수):
scaled_loss = loss * scale_factor
scaled_loss.backward() # gradient에 scale_factor 곱해진 상태
# gradient를 unscaled 후 optimizer stepBF16은 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 간 분할.
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로 구현.
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:
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)
같은 실험 반복 시 결과 재현.
import torchimport randomimport 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 감지:
torch.autograd.set_detect_anomaly(True)Backward 시 NaN 발생하면 어느 연산에서인지 트레이스백.
Gradient Clipping (편 #4 A.8):
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 활용을 다룹니다.