인지야공/딥러닝 기초 정리/1번째 글
PyTorch 치트시트 — 실제로 돌려서 검증한 것만 남겼다
치트시트 5종(공식 ptcheat, learnpytorch.io, DataCamp, ZeroToMastery, SlingAcademy)을 조회해 겹치는 핵심만 추린 뒤, 전부 실제로 돌려서 검증한 코드입니다. 실행:
python 01_pytorch_cheatsheet.py(검증 환경: torch 2.8.0+cu129, Python 3.13.5, CUDA 사용 가능)
0. 모든 스크립트의 시작 3줄
import torch, torch.nn as nn, torch.nn.functional as F
torch.manual_seed(42) # 재현성
torch.cuda.manual_seed_all(42)
device = ("cuda" if torch.cuda.is_available()
else "mps" if torch.backends.mps.is_available()
else "cpu")
1. 텐서 만들기
| 코드 | 뜻 |
|---|---|
torch.tensor([[1,2],[3,4]]) | 리스트 → 텐서 |
torch.zeros(2,3) / torch.ones(2,3) | 0 / 1 채우기 |
torch.rand(2,3) | 균등분포 [0,1) |
torch.randn(2,3) | 정규분포 N(0,1) |
torch.arange(0,10,2) | 0,2,4,6,8 |
torch.linspace(0,1,5) | 0~1 을 5등분 |
torch.eye(3) | 단위행렬 |
torch.zeros_like(x) | x 와 같은 모양 |
텐서를 보면 항상 3가지를 확인한다.
x.dtype # torch.float32 / torch.int64 ...
x.shape # torch.Size([2, 3])
x.device # cpu / cuda:0
dtype 변환은 .float(), .long(). numpy 는 torch.from_numpy() ↔ .numpy().
2. 모양 바꾸기 — 버그의 90%가 여기서 난다
x = torch.arange(12).reshape(3, 4)
x.view(2, 6) # (2,6) 연속 메모리일 때만 가능
x.reshape(-1, 2) # (6,2) 안전한 버전. -1 은 자동 계산
x.transpose(0, 1) # (4,3) 축 2개만 교환
x.permute(2, 0, 1) # 축 전체 재배열
x.unsqueeze(0) # (1,3,4) 차원 추가 - 배치 차원 만들 때
x.squeeze() # 크기 1인 차원 제거
torch.cat([x, x], 0) # (6,4) 기존 축에 이어붙임
torch.stack([x, x], 0) # (2,3,4) 새 축을 만들어 쌓음
t.flatten(1) # (8,3,32,32) -> (8,3072) CNN → FC 연결
cat 과 stack 의 차이가 핵심: cat 은 차원 수가 그대로, stack 은 차원이 하나 늘어난다.
3. 연산 / 브로드캐스팅 / 행렬곱
A * B # 원소별 곱 (모양이 같아야 함)
A @ B # 행렬곱 = torch.matmul(A, B)
torch.zeros(3,4) + torch.ones(4) # (3,4) 브로드캐스팅
torch.rand(8,5,3) @ torch.rand(8,3,7) # (8,5,7) 배치 행렬곱 ★어텐션의 핵심
A.sum(dim=0) # 행 방향으로 합치기(=열별 합)
A.argmax(dim=1) # 분류 예측 라벨 뽑기
F.softmax(A, dim=1)
dim 은 “그 축을 없앤다” 로 외우면 헷갈리지 않는다.
4. autograd
w = torch.tensor(2.0, requires_grad=True)
y = 3*w**2 + 5*w
y.backward()
w.grad # 17.0 (손계산 6w+5 = 6*2+5 = 17 과 일치 ✔ 실행 확인)
with torch.no_grad(): # 기울기 추적 끄기
...
5. 모델 정의
class MLP(nn.Module):
def __init__(self, in_dim=4, hidden=16, out_dim=3):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, hidden), nn.ReLU(), nn.Dropout(0.1),
nn.Linear(hidden, out_dim),
)
def forward(self, x):
return self.net(x) # ★ logits 그대로. 끝에 softmax 넣지 말 것
파라미터 수 세기: sum(p.numel() for p in model.parameters() if p.requires_grad)
(위 모델 = 131개, 손계산 4*16+16 + 16*3+3 과 일치 ✔)
주요 레이어 입출력 shape (실행 확인값)
| 레이어 | 입력 | 출력 |
|---|---|---|
nn.Linear(10,5) | (2,10) | (2,5) |
nn.Conv2d(3,16,3,padding=1) | (2,3,32,32) | (2,16,32,32) |
nn.MaxPool2d(2) | (2,16,32,32) | (2,16,16,16) |
nn.BatchNorm2d(16) | (2,16,8,8) | (2,16,8,8) |
nn.Embedding(100,8) | (1,3) | (1,3,8) |
nn.LSTM(10,20,batch_first=True) | (2,7,10) | (2,7,20) |
nn.TransformerEncoderLayer(32,4,batch_first=True) | (2,7,32) | (2,7,32) |
6. Dataset / DataLoader
from torch.utils.data import TensorDataset, DataLoader, random_split
ds = TensorDataset(X, y)
train_ds, test_ds = random_split(ds, [240, 60])
train_dl = DataLoader(train_ds, batch_size=32, shuffle=True)
test_dl = DataLoader(test_ds, batch_size=32, shuffle=False) # 평가는 shuffle=False
커스텀 Dataset 은 메서드 3개면 끝:
class MyDataset(Dataset):
def __init__(self, X, y): self.X, self.y = X, y
def __len__(self): return len(self.X)
def __getitem__(self, i): return self.X[i], self.y[i]
7. 손실함수 / 옵티마이저 / 스케줄러
| 문제 | 손실함수 | 모델 마지막 출력 | 정답 dtype |
|---|---|---|---|
| 회귀 | nn.MSELoss(), nn.L1Loss() | 실수값 | float |
| 다중분류 | nn.CrossEntropyLoss() | logits (softmax 금지) | long (정수 인덱스) |
| 이진분류 | nn.BCEWithLogitsLoss() | logits (sigmoid 금지) | float (0.0/1.0) |
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-2, weight_decay=1e-4)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30)
- 기본은
AdamW(Adam + 올바른 weight decay) - 스케줄러는 매 epoch 끝에
scheduler.step()
8. 학습 루프 — 이 6줄 순서가 PyTorch의 전부
def train_one_epoch(model, dl, loss_fn, optimizer, device):
model.train() # (1) Dropout/BN 켜기
for xb, yb in dl:
xb, yb = xb.to(device), yb.to(device)
pred = model(xb) # (2) 순전파
loss = loss_fn(pred, yb) # (3) 손실
optimizer.zero_grad() # (4) 기울기 초기화 ★빼먹으면 누적
loss.backward() # (5) 역전파
optimizer.step() # (6) 가중치 갱신
@torch.inference_mode() # no_grad 보다 빠른 추론 전용
def evaluate(model, dl, loss_fn, device):
model.eval() # Dropout 끄기, BN 통계 고정
...
실제 실행 결과 (3클래스 분류, 30 epoch):
epoch 1 | train loss 0.9838 acc 0.579 | test loss 0.8224 acc 0.733 | lr 0.00997
epoch 10 | train loss 0.2262 acc 0.950 | test loss 0.4134 acc 0.883 | lr 0.00750
epoch 20 | train loss 0.1578 acc 0.963 | test loss 0.3966 acc 0.933 | lr 0.00250
epoch 30 | train loss 0.1819 acc 0.942 | test loss 0.3953 acc 0.950 | lr 0.00000
9. 저장 / 불러오기
torch.save({"model": model.state_dict(),
"optim": optimizer.state_dict(),
"epoch": 30}, "ckpt.pt")
ckpt = torch.load("ckpt.pt", map_location=device, weights_only=True) # 보안상 권장
model2.load_state_dict(ckpt["model"])
model2.eval()
모델 객체 전체를 torch.save(model) 하지 말 것 — 클래스 정의에 묶여 이식성이 없다.
10. 자주 나는 에러 4개
| 에러 | 원인 / 해결 |
|---|---|
Expected all tensors to be on the same device | 데이터·모델 둘 다 .to(device) 했는지 |
mat1 and mat2 shapes cannot be multiplied | nn.Linear(in,out) 의 in 이 앞 레이어 출력과 다름. flatten(1) 확인 |
expected scalar type Long but found Float | CrossEntropyLoss 의 정답은 .long() 정수여야 함 |
| loss 가 안 줄어듦 | optimizer.zero_grad() 누락 / lr 과다 / 모델 끝에 softmax 를 또 씌움 |