🔥 PyTorch
Полный справочник: тензоры и autograd, nn.Module, слои, Dataset и DataLoader, цикл обучения, оптимизаторы и планировщики, GPU и AMP, сохранение, torch.compile, экспорт, отладка и типичные ошибки.
Шпаргалки · ИИ · #pytorch #torch #deep-learning #python #gpu
Что это
PyTorch (Meta, Linux Foundation) фреймворк глубокого обучения: тензоры (как NumPy, но на GPU), автоматическое дифференцирование (autograd) и модули нейросетей (torch.nn). Динамический граф: код выполняется как обычный Python, удобно отлаживать.
pip install torch torchvision torchaudio # CPU; для CUDA команда выбирается на pytorch.org/get-started
python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.backends.mps.is_available())"
import torch, torch.nn as nn, torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader, TensorDataset, random_split
Тензоры
x = torch.tensor([[1., 2.], [3., 4.]]) # из данных (копирует)
torch.zeros(2, 3); torch.ones(2, 3); torch.full((2, 2), 7); torch.eye(3); torch.arange(10); torch.linspace(0, 1, 5)
torch.rand(2, 3); torch.randn(2, 3); torch.randint(0, 10, (2, 3)); torch.zeros_like(x); torch.empty(2, 2)
torch.manual_seed(42) # воспроизводимость
x.shape; x.size(); x.ndim; x.dtype; x.device; x.numel(); x.requires_grad
x.float(); x.long(); x.half(); x.to(torch.float32); x.int() # типы
x.reshape(4); x.view(-1); x.flatten(); x.T; x.permute(1, 0); x.transpose(0, 1); x.unsqueeze(0); x.squeeze(); x.expand(3, 2, 2)
torch.cat([a, b], dim=0); torch.stack([a, b]); torch.split(x, 2); torch.chunk(x, 2)
x[0]; x[:, 1]; x[x > 2]; x[[0, 1]]; torch.where(x > 2, x, 0.); x.masked_fill(mask, 0); torch.gather(x, 1, idx)
a + b; a * b; a @ b; torch.matmul(a, b); a ** 2; x.sum(); x.mean(dim=0); x.max(dim=1); x.argmax(dim=1); x.softmax(dim=-1); torch.topk(x, 3)
x.numpy(); torch.from_numpy(arr); x.item(); x.tolist(); x.clone(); x.detach()
Типы: float32 (по умолчанию), float16 / bfloat16 (экономия памяти), int64 (индексы и метки классов), bool. Операции с суффиксом _ изменяют на месте (x.add_(1)). view требует непрерывной памяти, reshape справляется сам.
Устройства (CPU, CUDA, MPS)
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
x = x.to(device); model = model.to(device) # и данные, и модель на одном устройстве
torch.cuda.device_count(); torch.cuda.get_device_name(0); torch.cuda.memory_allocated() / 1e9; torch.cuda.empty_cache()
Копирование CPU↔GPU медленное: переносите батчи, а не отдельные числа; pin_memory=True и non_blocking=True ускоряют.
Autograd
w = torch.tensor(2.0, requires_grad=True)
loss = (w * 3 - 6) ** 2
loss.backward() # вычисляет градиенты
w.grad # d loss / d w
w.grad.zero_() # градиенты накапливаются: обнуляйте перед следующим шагом
with torch.no_grad(): y = model(x) # без графа: инференс, экономия памяти
y = model(x).detach() # отсоединить от графа
torch.inference_mode() # ещё быстрее для инференса
Модель nn.Module
class Net(nn.Module):
def __init__(self, d_in=10, d_hidden=64, n_classes=3, p=0.2):
super().__init__()
self.net = nn.Sequential(
nn.Linear(d_in, d_hidden), nn.BatchNorm1d(d_hidden), nn.ReLU(), nn.Dropout(p),
nn.Linear(d_hidden, n_classes),
)
def forward(self, x): # вызывайте model(x), не model.forward(x)
return self.net(x)
model = Net().to(device)
sum(p.numel() for p in model.parameters() if p.requires_grad) # число параметров
model.state_dict(); model.parameters(); model.named_parameters(); model.train(); model.eval()
Выход классификатора это логиты (без softmax): CrossEntropyLoss применяет log_softmax сам.
Слои
| Группа | Слои |
|---|---|
| Полносвязные | nn.Linear, nn.Bilinear, nn.Embedding |
| Свёртки | nn.Conv1d/2d/3d, nn.ConvTranspose2d, nn.MaxPool2d, nn.AvgPool2d, nn.AdaptiveAvgPool2d(1) |
| Рекуррентные | nn.LSTM, nn.GRU, nn.RNN (batch_first=True) |
| Внимание | nn.MultiheadAttention, nn.TransformerEncoderLayer, nn.TransformerEncoder, F.scaled_dot_product_attention |
| Нормализация | nn.BatchNorm1d/2d, nn.LayerNorm, nn.GroupNorm, nn.RMSNorm |
| Регуляризация | nn.Dropout, nn.Dropout2d |
| Активации | nn.ReLU, nn.GELU, nn.SiLU, nn.LeakyReLU, nn.Sigmoid, nn.Tanh, nn.Softmax |
| Контейнеры | nn.Sequential, nn.ModuleList, nn.ModuleDict |
| Форма | nn.Flatten, nn.Unflatten |
Списки слоёв кладите в nn.ModuleList, а не в обычный list: иначе параметры не зарегистрируются.
class CNN(nn.Module):
def __init__(self, n=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(1, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.AdaptiveAvgPool2d(1),
)
self.head = nn.Linear(64, n)
def forward(self, x): return self.head(self.features(x).flatten(1))
Форма изображений: (batch, channels, height, width).
Данные
class MyDataset(Dataset):
def __init__(self, X, y, transform=None): self.X, self.y, self.transform = X, y, transform
def __len__(self): return len(self.X)
def __getitem__(self, i):
x = self.X[i]
if self.transform: x = self.transform(x)
return x, self.y[i]
ds = TensorDataset(torch.tensor(X, dtype=torch.float32), torch.tensor(y, dtype=torch.long))
train_ds, val_ds = random_split(ds, [0.8, 0.2], generator=torch.Generator().manual_seed(42))
train_dl = DataLoader(train_ds, batch_size=64, shuffle=True, num_workers=4, pin_memory=True, drop_last=True, persistent_workers=True)
val_dl = DataLoader(val_ds, batch_size=256)
from torchvision import datasets, transforms
tf = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
mnist = datasets.MNIST("data", train=True, download=True, transform=tf)
Аугментации: transforms.RandomHorizontalFlip, RandomCrop, ColorJitter, v2 API; тексты: torchtext, библиотеки Hugging Face datasets.
Цикл обучения
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
for epoch in range(epochs):
model.train()
for xb, yb in train_dl:
xb, yb = xb.to(device, non_blocking=True), yb.to(device, non_blocking=True)
optimizer.zero_grad(set_to_none=True)
loss = criterion(model(xb), yb)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) # против взрыва градиентов
optimizer.step()
scheduler.step()
model.eval()
correct = total = 0
with torch.no_grad():
for xb, yb in val_dl:
pred = model(xb.to(device)).argmax(1).cpu()
correct += (pred == yb).sum().item(); total += len(yb)
print(f"epoch {epoch}: val_acc={correct / total:.3f}")
Порядок шага: обнулить градиенты → прямой проход → потеря → backward → step. model.train() включает Dropout и обновление BatchNorm, model.eval() выключает.
Потери и оптимизаторы
| Задача | Потеря |
|---|---|
| Многоклассовая | nn.CrossEntropyLoss (логиты + индексы классов, label_smoothing, weight) |
| Бинарная | nn.BCEWithLogitsLoss (логиты; численно устойчивее, чем Sigmoid + BCELoss) |
| Регрессия | nn.MSELoss, nn.L1Loss, nn.SmoothL1Loss, nn.HuberLoss |
| Сходство | nn.CosineEmbeddingLoss, nn.TripletMarginLoss, nn.KLDivLoss |
| Последовательности | nn.CTCLoss, CrossEntropyLoss(ignore_index=pad) |
Оптимизаторы: SGD(momentum=0.9), Adam, AdamW (по умолчанию для большинства задач), RMSprop, Adagrad, LBFGS. Планировщики: StepLR, MultiStepLR, ExponentialLR, CosineAnnealingLR, OneCycleLR, ReduceLROnPlateau, LinearLR + SequentialLR (прогрев).
Ускорение: AMP, compile, градиенты
scaler = torch.amp.GradScaler("cuda")
with torch.autocast(device_type="cuda", dtype=torch.float16): # bfloat16 не требует GradScaler
loss = criterion(model(xb), yb)
scaler.scale(loss).backward(); scaler.step(optimizer); scaler.update()
model = torch.compile(model) # PyTorch 2: ускорение через JIT-компиляцию
torch.backends.cudnn.benchmark = True # для постоянных размеров входа
accum = 4 # накопление градиентов: большой «виртуальный» батч
(loss / accum).backward(); (step % accum == 0) and (optimizer.step(), optimizer.zero_grad())
Дальше: DistributedDataParallel и torchrun (несколько GPU), FSDP (большие модели), torch.utils.checkpoint (экономия памяти), Hugging Face Accelerate, PyTorch Lightning.
Сохранение и загрузка
torch.save(model.state_dict(), "model.pt") # рекомендуется: только веса
model = Net(); model.load_state_dict(torch.load("model.pt", map_location=device, weights_only=True)); model.eval()
torch.save({"epoch": e, "model": model.state_dict(), "opt": optimizer.state_dict()}, "ckpt.pt") # чекпоинт для продолжения
scripted = torch.jit.script(model) # TorchScript
torch.onnx.export(model, example_input, "model.onnx", opset_version=17) # ONNX: перенос в другие рантаймы
Файлы .pt используют pickle: не загружайте чужие без weights_only=True. Форматы без pickle: safetensors.
Готовые модели и дообучение
from torchvision import models
m = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
for p in m.parameters(): p.requires_grad = False # заморозить основу
m.fc = nn.Linear(m.fc.in_features, 5) # новая голова
optimizer = torch.optim.AdamW(m.fc.parameters(), lr=1e-3) # обучается только голова
Hugging Face Transformers: AutoModel.from_pretrained("bert-base-multilingual-cased"), PEFT / LoRA для экономного дообучения LLM, timm (модели зрения).
Отладка
torch.autograd.set_detect_anomaly(True) # найти NaN в градиентах (медленно)
for n, p in model.named_parameters(): print(n, p.grad.norm() if p.grad is not None else None)
print(model); from torchinfo import summary; summary(model, input_size=(1, 10))
# проверка кода: обучите модель на одном маленьком батче, потеря должна упасть почти до нуля
Профилирование: torch.profiler, TensorBoard (torch.utils.tensorboard.SummaryWriter), Weights & Biases, MLflow.
Типичные ошибки
| Ошибка | Причина |
|---|---|
Expected all tensors to be on the same device |
модель и данные на разных устройствах |
mat1 and mat2 shapes cannot be multiplied |
неверная размерность входа слоя |
CUDA out of memory |
уменьшите батч, AMP, checkpoint, del, torch.cuda.empty_cache(), накопление градиентов |
element 0 of tensors does not require grad |
выход отсоединён или в no_grad |
Потеря nan |
слишком большой lr, деление на ноль, log(0), нет нормализации входов: уменьшите lr, обрежьте градиенты |
| Потеря не падает | забыт zero_grad, неверные метки или тип (long для классов), нет model.train(), мал lr |
Метки для CrossEntropyLoss |
нужны индексы [0, C) типа long, не one-hot (если не вероятности) |
Модель в eval при обучении |
model.train() перед циклом |
| Разное поведение train и eval | Dropout и BatchNorm: ожидаемо |
Воспроизводимость
torch.manual_seed(s), np.random.seed(s), random.seed(s), torch.use_deterministic_algorithms(True), DataLoader(worker_init_fn=...); полная детерминированность на GPU стоит скорости.
Экосистема
torchvision, torchaudio, Hugging Face (Transformers, Datasets, PEFT, Accelerate), timm, PyTorch Lightning, Ignite, torchmetrics, Captum (интерпретация), ONNX Runtime, TorchServe, vLLM (инференс LLM), DeepSpeed, Optuna, Weights & Biases.