Ключови моменти
Успехът в PyTorch идва от правилна инсталация, стабилен training loop и измерване на скорост/качество.
За да използваш PyTorch ефективно, ти трябват три неща: правилна инсталация (CPU/GPU), ясна „рамка“ за работа с tensors и устройства (CPU/CUDA/ROCm) и стабилен тренировъчен цикъл (данни → модел → loss → backward → optimizer). PyTorch е най-силен, когато пишеш „нормален“ Python, но измерваш всичко като инженер: скорост, памет и качество. По-долу е практичен път от нулата до първи работещ модел, плюс навици за по-сериозни проекти.
PyTorch е deep learning framework, който е удобен за учене и мощен за реални системи. Той работи с tensors (многомерни масиви), автоматични градиенти (autograd) и модулен начин за дефиниране на модели (nn.Module). В 2.x линията PyTorch добави torch.compile, което може да оптимизира изпълнението (особено на GPU) без да пренаписваш модела.
Какво ще научиш:
Ако не можеш да повториш резултата си (seed, версии, данни), не си „тренирал модел“, а си направил демо.
Направи си чиста среда, за да не се бориш със зависимости:
venv или conda envrequirements.txt или pyproject.tomltrain.pydata.pymodel.pyeval.pyТова ти спестява хаос още на втория ден.
Най-сигурният начин е да ползваш selector-а на официалния сайт и да копираш командата за твоята ОС и compute платформа.
Практически сценарии:
Мини-чек след инсталация:
torch без грешки?Пусни:
import torch
print(torch.__version__)
print('cuda available:', torch.cuda.is_available())
if torch.cuda.is_available():
print('gpu:', torch.cuda.get_device_name(0))
После тествай tensor:
x = torch.rand(5, 3)
print(x.shape, x.dtype, x.device)
Основна идея: почти всичко „живее“ или на CPU, или на GPU. Ако входът е на GPU, моделът трябва да е на GPU.
Полезен шаблон:
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = model.to(device)
X = X.to(device)
Това са навиците, които спестяват дни дебъг:
shape (напр. batch x features или batch x channels x height x width)dtype (float32, float16, bfloat16)x.deviceЧести операции:
reshape/view за промяна на размериpermute за размяна на осиcat/stack за слепванеПример:
x = torch.randn(32, 100) # batch=32
w = torch.randn(100, 10)
logits = x @ w # (32, 10)
autograd пази граф на операциите и може да сметне градиенти.
Минимумът:
loss.backward()step()Важно:
optimizer.zero_grad(set_to_none=True)torch.no_grad()Стандартният подход:
__init__ със слоевеforwardПример (MLP):
import torch
import torch.nn as nn
class MLP(nn.Module):
def __init__(self, in_dim=100, hidden=256, out_dim=10):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, hidden),
nn.ReLU(),
nn.Linear(hidden, out_dim)
)
def forward(self, x):
return self.net(x)
Стабилният модел започва със стабилен вход: валидирай размерите и типовете още в началото.
Полезен трик: още в forward понякога е ок да сложиш assert-и при разработка (после ги махаш).
Dataset описва как взимаш пример по индекс, а DataLoader управлява batching, shuffle и паралелно зареждане.
Скелет:
from torch.utils.data import Dataset, DataLoader
class MyDataset(Dataset):
def __init__(self):
self.X = ...
self.y = ...
def __len__(self):
return len(self.X)
def __getitem__(self, idx):
return self.X[idx], self.y[idx]
loader = DataLoader(MyDataset(), batch_size=64, shuffle=True, num_workers=2)
Съвети:
num_workers=0, после увеличиpin_memory=Truetorchvision.datasets + transformsМинимален, но реалистичен цикъл:
import torch
import torch.nn as nn
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = MLP().to(device)
opt = torch.optim.AdamW(model.parameters(), lr=3e-4)
loss_fn = nn.CrossEntropyLoss()
for epoch in range(5):
model.train()
total_loss = 0.0
for X, y in loader:
X, y = X.to(device), y.to(device)
opt.zero_grad(set_to_none=True)
logits = model(X)
loss = loss_fn(logits, y)
loss.backward()
opt.step()
total_loss += loss.item()
model.eval()
with torch.no_grad():
# сметни метрики на validation
pass
print('epoch', epoch, 'loss', total_loss/len(loader))
Правила за здрав разум:
model.train() включва dropout/batchnorm поведениеmodel.eval() + torch.no_grad() за стабилна оценкаНай-чистият навик в PyTorch е да пазиш state_dict:
torch.save(model.state_dict(), 'model.pt')
И да зареждаш така:
model = MLP()
model.load_state_dict(torch.load('model.pt', map_location='cpu'))
model.eval()
За inference:
model.eval()torch.no_grad()На много GPU-и mixed precision дава голям speedup. Подходът е:
torch.cuda.amp.autocast() за forwardGradScaler за стабилни градиенти при fp16В PyTorch 2.x можеш да компилираш модела с:
model = torch.compile(model)
Документацията описва, че torch.compile използва стека TorchDynamo/TorchInductor и може да ускорява тренировка и inference без промяна на модела. Важно е да знаеш, че поддръжката на най-новите версии на Python може да изостава, затова за production избирай поддържан Python и тествай на реални данни.
Първо направи модела правилен, после го прави бърз. Оптимизация върху грешна задача е загуба на време.
Когато нещо „не учи“:
Полезен тест: вземи 128 примера и виж дали моделът може да overfit-не. Ако не може, проблемът е в модела/данните/кода.
AdamW като базов optimizer и започни с малък learning rate.shape-ове (особено при CNN/Transformer входове).model.eval() при оценка, което влошава метриките.torch.compile (въведение): https://docs.pytorch.org/get-started/pytorch-2.0/torch.compiler документация (обновена 2025): https://docs.pytorch.org/docs/main/user_guide/torch_compiler/torch.compiler.htmlПовторяемостта е разликата между „случайно се получи“ и инженерство. Минимален чеклист:
Пример за seed (не гарантира 100% детерминизъм, но помага):
import random
import numpy as np
import torch
seed = 42
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
Добра практика е да пазиш checkpoint при най-добра validation метрика. Така не губиш време, ако последните епохи започнат да overfit-ват:
best_acc = -1.0
for epoch in range(num_epochs):
# train ...
val_acc = ...
if val_acc > best_acc:
best_acc = val_acc
torch.save({
"epoch": epoch,
"model": model.state_dict(),
"opt": opt.state_dict(),
"best_acc": best_acc,
}, "checkpoint.pt")
Ако ти свършва GPU паметта:
detach() и no_grad() при оценка)dtype (float32 vs float16/bfloat16)Практичен подход: първо направи базовия модел да работи стабилно на малък batch, после увеличавай и оптимизирай.
В практиката често не тренираш „от нула“. Вместо това взимаш pretrained backbone (например за изображения или текст), замразяваш част от слоевете и дообучаваш само последните. Това дава по-добро качество с по-малко данни и по-малко време. Типичен подход:
p.requires_grad = FalseАко loss спре да пада, scheduler често помага. В PyTorch имаш torch.optim.lr_scheduler с готови стратегии (step, cosine, reduce-on-plateau). Важно: логвай текущия learning rate, за да знаеш какво се случва и защо.
Когато вече разбираш базовия training loop, high-level библиотека може да ти спести boilerplate (логване, чекпойнти, distributed training). Ползвай я, ако имаш стандартен supervised pipeline и искаш бърза итерация. Ако правиш нетипичен алгоритъм или много custom логика, остани на „чист“ PyTorch, докато стабилизираш идеята.
Бележка: Започни с малък модел, измервай, и итерай системно.