Урок по треку Глубокое обучение 8 мин

PyTorch на практике за 8 минут

Тензоры, autograd, nn.Module, DataLoader, цикл обучения и валидации, GPU, память и типичные баги.

На собесе ML-инженера часто дают кусок кода обучения и просят найти ошибки. Почти все они из короткого списка: забытый zero_grad, eval или no_grad, softmax перед CrossEntropyLoss, накопление тензора loss, тензоры на разных устройствах.

Тензоры и autograd

Тензор это многомерный массив на CPU или GPU. Если у тензора requires_grad=True, операции над ним записываются в динамический граф. loss.backward() проходит граф назад и прибавляет градиенты к .grad параметров. Граф строится заново на каждом forward.

nn.Module

import torch
from torch import nn

class MLP(nn.Module):
    def __init__(self, d_in, d_hidden, n_classes):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(d_in, d_hidden), nn.ReLU(), nn.Dropout(0.1),
            nn.Linear(d_hidden, n_classes),
        )

    def forward(self, x):
        return self.net(x)  # логиты, без softmax

Модель вызывают как model(x). Параметры доступны через model.parameters(), сохраняют state_dict.

Цикл обучения и валидации

device = "cuda" if torch.cuda.is_available() else "cpu"
model = MLP(784, 256, 10).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()

for epoch in range(epochs):
    model.train()
    for x, y in train_loader:                 # shuffle=True
        x, y = x.to(device), y.to(device)
        optimizer.zero_grad()
        loss = criterion(model(x), y)
        loss.backward()
        optimizer.step()
        running += loss.item()

    model.eval()
    correct = 0
    with torch.no_grad():
        for x, y in val_loader:
            pred = model(x.to(device)).argmax(1)
            correct += (pred == y.to(device)).sum().item()
    acc = correct / len(val_loader.dataset)

Чек-лист багов

ОшибкаПоследствие
Нет zero_gradградиенты копятся между шагами
softmax перед CrossEntropyLossдвойной softmax, слабое обучение
Нет model.eval()dropout и BatchNorm в режиме обучения на валидации
Нет no_grad на валидациилишняя память и время
total += loss без .item()держим графы всех шагов, утечка памяти
shuffle=False на трейнеоднородные батчи
Метки на CPU, модель на GPUошибка устройств
Деление на число батчейневерная метрика при неполном последнем батче

model.eval() и torch.no_grad() это разные вещи: первое меняет поведение слоёв, второе отключает граф. torch.inference_mode() ещё строже для чистого инференса.

Заморозка слоёв

requires_grad = False у параметров бэкбона и оптимизатор только с параметрами головы. model.eval() веса не замораживает.

Данные

  • Dataset возвращает один объект, DataLoader собирает батчи.
  • num_workers > 0 и pin_memory=True, если GPU простаивает в ожидании данных.
  • Предобработка (нормализация, target encoding) обучается только на трейне.

Память GPU

На параметр при AdamW в fp32: вес 4 байта, градиент 4, два момента 8. Около 16 ГБ на миллиард параметров без активаций. При нехватке памяти:

  • меньше батч и накопление градиентов;
  • bf16 через torch.autocast;
  • gradient checkpointing;
  • поиск утечек (тензоры с графом в списках);
  • 8-битные оптимизаторы, LoRA, FSDP.

Скорость

  • torch.compile захватывает граф и генерирует слитые ядра; первый запуск медленный из-за компиляции.
  • Эффективные ядра attention, mixed precision, отсутствие лишних синхронизаций (.item() в горячем цикле).

Воспроизводимость

Фиксируйте сиды (torch, numpy, random), сохраняйте конфиг, версии и чекпоинты: state_dict модели, оптимизатора, scheduler и номер эпохи.

Что спросят на собесе

  • Найдите ошибки в цикле обучения.
  • Разница model.eval() и torch.no_grad().
  • Почему растёт память во время обучения.
  • Как заморозить часть модели.
  • Сколько памяти нужно для обучения модели на N параметров.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.