iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up
Next »

Summary

A final-page summary of the PyTorch track: core abstractions, training loop, GPU patterns, and what to revisit when starting a new project.

PyTorch — track summary

EXAMPLE
# ===== The three abstractions =====
# 1. Tensor  - n-dimensional array with autograd hooks
# 2. nn.Module - layer / model with .parameters() and .forward()
# 3. DataLoader - batched iterator with parallelism and shuffling

# ===== The training loop (the one you memorise) =====
import torch
from torch import nn
from torch.utils.data import DataLoader

device = 'cuda' if torch.cuda.is_available() else 'cpu'

model = MyNet().to(device)
optim = torch.optim.AdamW(model.parameters(), lr=3e-4)
loss_fn = nn.CrossEntropyLoss()
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True, num_workers=4, pin_memory=True)
val_loader   = DataLoader(val_ds, batch_size=128, num_workers=4, pin_memory=True)

for epoch in range(10):
    model.train()
    for x, y in train_loader:
        x = x.to(device, non_blocking=True); y = y.to(device, non_blocking=True)
        optim.zero_grad(set_to_none=True)
        out = model(x)
        loss = loss_fn(out, y)
        loss.backward()
        optim.step()

    model.eval()
    correct = total = 0
    with torch.no_grad():
        for x, y in val_loader:
            x = x.to(device); y = y.to(device)
            pred = model(x).argmax(1)
            correct += (pred == y).sum().item(); total += y.numel()
    print(f'epoch {epoch} val_acc {correct/total:.4f}')

torch.save(model.state_dict(), 'model.pt')

# ===== GPU patterns =====
# - .to(device, non_blocking=True) with pin_memory=True DataLoader
# - autocast + GradScaler for mixed precision:
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for x, y in train_loader:
    x, y = x.to(device, non_blocking=True), y.to(device, non_blocking=True)
    optim.zero_grad(set_to_none=True)
    with autocast(dtype=torch.float16):
        loss = loss_fn(model(x), y)
    scaler.scale(loss).backward()
    scaler.step(optim)
    scaler.update()

# ===== Reproducibility =====
import random, numpy as np
def seed_everything(s):
    random.seed(s); np.random.seed(s)
    torch.manual_seed(s); torch.cuda.manual_seed_all(s)
seed_everything(42)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

# ===== When to reach for what =====
# - DataLoader with collate_fn for ragged inputs (text, graphs)
# - Lightning if you want the loop abstracted but keep PyTorch underneath
# - torch.compile(model) on PyTorch 2 for kernel fusion
# - DDP for multi-GPU; FSDP for very large models
# - ONNX export for cross-platform serving

# ===== Patterns to internalise =====
# - Train / Eval mode toggles dropout + batchnorm behaviour
# - zero_grad(set_to_none=True) is faster than zeros
# - amp + pin_memory + non_blocking is the perf trinity
# - Save state_dict, never the whole module (brittle across refactors)

# ===== Pitfalls =====
# - Forgetting model.eval() in val -> dropout pollutes metrics
# - Logging loss.item() in the hot path -> GPU/CPU sync every step
# - DataLoader with num_workers > 0 on Windows without if __name__ == '__main__'
# - Float16 underflow without GradScaler
# - Saving the whole model across refactors -> unpickling errors later

Why it matters

Memorise the training loop, layer the perf patterns on top (amp + pin_memory + non_blocking), and reach for Lightning or compile only when the basics are second nature. Most PyTorch wins come from the same five reflexes practised over many projects.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Next: torch.compile, FSDP, FlashAttention, custom CUDA kernels, ExecuTorch.
Try it Yourself »

Discussion

Loading…

Next »