Перейти к основному содержимому

PyTorch для разработчика

Разработчику

См. также: практикум — цифры MNIST на PyTorch · практикум — тональность отзывов на PyTorch · Классическое ML — scikit-learn · Анализ данных · текст как признаки (TF-IDF) · NumPy — массивы и матрицы (Lab) · перцептрон на NumPy · Keras и TensorFlow · раздел "ИИ"

Маршрут по нейросетям

Теория backprop и ручной перцептрон — в нейроне и первом обучении на NumPy. Здесь те же идеи в синтаксисе PyTorch.


PyTorch - глубокое обучение на Python

PyTorch — фреймворк глубокого обучения на Python — динамические вычислительные графы, GPU, автоматическое дифференцирование. Концепции нейросетей в энциклопедии ИИ описаны отдельно; здесь — практический вход разработчика — установка, тензор, модель, цикл fit, сохранение весов.

Перед PyTorch полезно пройти NumPy/pandas, примеры NumPy, примеры подготовки таблиц и по возможности scikit-learn — те же идеи train/test и метрик.

Если вы только входите в тему, полезная ментальная модель такая:
"scikit-learn отвечает на вопрос "можно ли решить задачу быстро и просто", а PyTorch — "как построить и дообучить собственную нейросеть под сложные данные"."
Это помогает не начинать с избыточной сложности.

pip install torch torchvision

Разбор фрагмента:

  • pip install устанавливает библиотеки в текущее окружение Python.
  • torch даёт базовые тензоры, autograd и API для обучения моделей.
  • torchvision добавляет датасеты, преобразования и модели для задач компьютерного зрения.

Для GPU нужны драйвер NVIDIA и сборка torch с поддержкой CUDA с официального индекса под вашу версию ОС. На Mac с Apple Silicon доступен бэкенд MPS (torch.backends.mps.is_available()).

Выбор устройства для всего скрипта:


import torch

if torch.cuda.is_available():
device = torch.device("cuda")
elif torch.backends.mps.is_available():
device = torch.device("mps")
else:
device = torch.device("cpu")

Тензор

Тензор — многомерный массив с операциями на CPU или GPU.


import torch

x = torch.tensor([[1.0, 2.0], [3.0, 4.0]], dtype=torch.float32)
y = torch.ones(2, 2)
z = x @ y # матричное умножение

print(x.shape) # torch.Size([2, 2])
print(x.dtype) # torch.float32
x_gpu = x.to(device)

Разбор фрагмента:

  • torch.tensor(...) создаёт числовой тензор 2x2.
  • dtype=torch.float32 задаёт тип; для обучения чаще всего float32, метки классов — long.
  • torch.ones(2, 2) формирует матрицу из единиц той же размерности.
  • x @ y выполняет матричное умножение, это оператор matmul.
  • .shape и .dtype описывают размерность и тип данных.
  • x.to(device) переносит данные на выбранное устройство.

От NumPy к PyTorch

Таблицы из pandas/NumPy обычно превращают в тензор перед обучением:


import numpy as np

arr = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32)
t = torch.from_numpy(arr) # общая память с NumPy
t2 = torch.tensor(arr) # копия
arr_back = t.numpy() # обратно в NumPy (на CPU)

Разбор фрагмента:

  • from_numpy создаёт тензор без копирования данных — изменение одного массива затронет другой.
  • torch.tensor(...) всегда копирует значения.
  • .numpy() работает только для тензоров на CPU.

Полезные конструкторы и операции:

a = torch.randn(3, 4) # нормальное распределение
b = torch.zeros_like(a) # нули той же формы
c = a + 1 # broadcasting: скаляр ко всем элементам
row = a[0] # индексация как в NumPy
flat = a.view(-1) # изменение формы без копии (если память непрерывна)

Многие операции совместимы по духу с NumPy; отличие — тензор может участвовать в autograd и жить на GPU.

Динамический граф вычислений

Граф вычислений — схема операций над данными: узлы — сложение, умножение, активация; рёбра — поток тензоров. При обучении по графу идут два прохода:

  • forward — от входа к предсказанию и значению loss;
  • backward — от loss к градиентам весов (backpropagation).

В ранних фреймворках (Theano, TensorFlow 1.x) граф описывали и компилировали до запуска (статический граф). PyTorch собирает граф в момент выполнения (define-by-run, динамический граф): каждый вызов model(x) строит свою цепочку для loss.backward().

Практические следствия для разработчика:

  • в forward можно ставить print(x.shape) и обычные if по размеру батча;
  • последовательности разной длины в NLP обрабатываются без перекомпиляции графа;
  • для продакшена готовую модель иногда экспортируют в TorchScript или ONNX — см. применение ИИ.

Подробнее про autograd ниже; практика на тексте — практикум по тональности, на изображениях — MNIST.


autograd — градиенты

PyTorch строит граф вычислений во время выполнения (dynamic graph): каждая операция над тензором с requires_grad=True запоминается, чтобы потом применить цепное правило и получить производные.

x = torch.tensor([2.0, 3.0], requires_grad=True)
y = (x ** 2).sum()
y.backward()
print(x.grad) # tensor([4., 6.])
print(y.grad_fn) # <SumBackward0 object at ...>

Разбор фрагмента:

  • requires_grad=True включает отслеживание операций для вычисления градиента.
  • (x &#42;&#42; 2).sum() строит скалярную функцию потерь y.
  • backward() запускает обратное распространение и считает производные dy/dx.
  • x.grad хранит итоговые градиенты по каждому элементу тензора.
  • y.grad_fn указывает, какая операция породила тензор — так строится цепочка backprop.

Отключить градиенты для инференса или промежуточных вычислений:

with torch.no_grad():
pred = model(x)

frozen = x.detach() # тензор без связи с графом

In-place операции (x.add_(1)) могут сломать autograd, если тензор участвует в графе. Для обучаемых параметров безопаснее писать x = x + 1.

При обучении сети градиенты идут от функции потерь к весам слоёв; оптимизатор обновляет веса в противоположную сторону градиента. Интерактивная схема backprop — в статье Нейрон.

На практике держите в памяти три типичных ошибки новичков:

  • забыли optimizer.zero_grad() и градиенты накапливаются между шагами;
  • сделали loss.backward() внутри torch.no_grad() и не получили градиенты;
  • передали в CrossEntropyLoss one-hot метки вместо индексов классов.

Функции активации и потерь

Нейросеть — цепочка линейных преобразований и нелинейных активаций. Функция потерь (loss) сравнивает предсказание с эталонной меткой; её минимизирует градиентный спуск. Выбор активации и loss должен совпадать с формой выхода модели и типом меток.

Активации

Активация — нелинейная функция после линейного слоя. Без неё несколько слоёв Linear эквивалентны одному — сеть не выразит сложные границы решений (см. перцептрон и XOR).

ФункцияДиапазон выходаГде применяют
torch.sigmoidот 0 до 1Один выход, интерпретация как вероятность
torch.tanhот −1 до 1Исторически в RNN; на глубоких сетях редко
nn.ReLUот 0 до +∞Скрытые слои MLP и CNN
nn.LeakyReLU, PReLUотрицательный наклон при x < 0Когда часть нейронов ReLU "замирает" на нуле
nn.Softmaxсумма выходов = 1Интерпретация вероятностей классов на инференсе

Затухание градиента — на насыщенных участках sigmoid и tanh производная близка к нулю, и сигнал обучения слабо доходит до ранних слоёв. ReLU обычно обучается быстрее; у неё своя крайность — "мёртвый" ReLU, когда нейрон всегда выдаёт 0.

Типичный выбор слоёв:

  • скрытые слои — nn.ReLU();
  • бинарная классификация, один выход — логит + BCEWithLogitsLoss (sigmoid внутри loss);
  • много классов — логиты + CrossEntropyLoss (softmax внутри loss).

Функции потерь и формат меток

LossЧто подаётся на вход lossФормат меток yТип задачи
nn.MSELossвещественное предсказаниеfloat, та же формарегрессия
nn.BCELossвероятность после sigmoid0.0 или 1.0бинарная классификация
nn.BCEWithLogitsLossлогит (сырой выход слоя)0.0 или 1.0бинарная; устойчивее в float32
nn.CrossEntropyLossлогиты по классамlong, индекс класса 0…K-1многоклассовая

Логит — значение до sigmoid/softmax. Положительный логит → класс "1" вероятнее при бинарной задаче; порог 0 на логите соответствует вероятности 0,5 после sigmoid.

CrossEntropyLoss внутри объединяет log_softmax и отрицательное лог-правдоподобие. Перед ним достаточно логитов; отдельный Softmax и one-hot метки не нужны.

Для тональности отзыва (два класса, один выходной нейрон) удобна связка логит + BCEWithLogitsLossпрактикум 336. Для MNIST (десять цифр) — CrossEntropyLoss и метки-индексы — практикум 335.

import torch.nn as nn

criterion = nn.BCEWithLogitsLoss()
logit = model(x).squeeze(1) # форма (batch,)
loss = criterion(logit, y.float()) # y — 0.0 или 1.0
pred = (logit > 0).float() # порог на логите

Обратное распространение и оптимизация

Backpropagation — алгоритм вычисления градиентов функции потерь по всем весам сети через цепное правило. В PyTorch его вызывает loss.backward(); вручную градиенты для одного нейрона разобраны в перцептроне на NumPy.

Типичный цикл оптимизации:

  1. Forward — вход проходит через слои, на выходе считается loss.
  2. Backward — градиенты распространяются от loss к параметрам.
  3. Step — оптимизатор обновляет веса.
ОптимизаторКогда использовать
optim.SGDБазовый градиентный спуск; с momentum=0.9 — стабильнее
optim.AdamУниверсальный выбор для прототипов и многих задач DL
optim.AdamWAdam с корректной weight decay — часто для трансформеров

Подробнее про SGD, Adam и переобучение — в обзоре ML и Принцип работы современных ИИ-систем.


Градиентный спуск — ручной и автоматический

На простой задаче линейной регрессии видно, что делает backward() и optimizer.step().

Ручное обновление — вы сами вычитаете градиент, умноженный на learning rate:

Код ITЗагрузка примера кода…

Автоматическое обновление — ту же логику берёт на себя оптимизатор:

w = torch.tensor(0.0, requires_grad=True)
optimizer = torch.optim.SGD([w], lr=0.1)

for _ in range(50):
optimizer.zero_grad()
y_pred = w * x
loss = ((y_pred - y_true) ** 2).mean()
loss.backward()
optimizer.step()

print(w.item())

Разбор:

  • optimizer.zero_grad() соответствует w.grad.zero_().
  • optimizer.step() выполняет w -= lr * w.grad для всех переданных параметров.
  • В реальной сети параметров тысячи — вручную обновлять каждый неудобно, поэтому используют model.parameters().

Ручной мини-batch SGD на NumPy для сравнения — в справочнике алгоритмов ML.


Модель как nn.Module

Код ITЗагрузка примера кода…

Разбор фрагмента:

  • Класс TinyNet(nn.Module) описывает архитектуру модели как переиспользуемый модуль.
  • nn.Linear задаёт полносвязные слои, nn.ReLU() добавляет нелинейность.
  • nn.Sequential(...) соединяет слои в один прямой конвейер вычислений.
  • Метод forward(self, x) определяет путь входа через сеть.
  • .to(device) переносит параметры модели на CPU или GPU.

forward вызывается при model(x). Параметры (weight, bias) регистрируются автоматически — их передают оптимизатору.


Dataset и DataLoader

Код ITЗагрузка примера кода…

Разбор фрагмента:

  • Dataset инкапсулирует логику доступа к одному элементу данных.
  • __len__ сообщает размер выборки, __getitem__ возвращает пару x, y.
  • random_split делит выборку на train и val без пересечения индексов.
  • DataLoader собирает элементы в батчи и итерирует их в цикле обучения.
  • batch_size=64 определяет размер мини-батча.
  • shuffle=True перемешивает порядок примеров между эпохами.
  • num_workers > 0 подгружает батчи в фоновых процессах (на Windows иногда начинают с 0).

Изображения и transforms

Для CV используют torchvision — готовые датасеты и цепочку преобразований:

from torchvision import datasets, transforms

transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)),
])

mnist_train = datasets.MNIST("./data", train=True, download=True, transform=transform)
mnist_loader = DataLoader(mnist_train, batch_size=128, shuffle=True)

ToTensor() переводит PIL/NumPy в тензор [C, H, W]; Normalize вычитает среднее и делит на std — те же шаги, что нормализация признаков для таблиц.


Сквозной ML-пайплайн

Ниже — полный каркас от сырых данных до сохранённых весов (классификация Iris, 4 признака, 3 класса):

Код ITЗагрузка примера кода…

Этапы пайплайна:

ШагДействие
Подготовкаsplit, scaler, тензоры
ЗагрузкаDataLoader, батчи
Обучениеforward → loss → backward → step
Оценкаmodel.eval(), метрика на val
Артефактstate_dict в файл

Тот же каркас применим к CSV через pandas (анализ данных) и к изображениям через torchvision. Разбиение выборок и утечки — в статье про train/test.


Цикл обучения

Код ITЗагрузка примера кода…

Разбор фрагмента:

  • criterion = nn.CrossEntropyLoss() задаёт функцию потерь для многоклассовой классификации.
  • optim.Adam(model.parameters(), lr=1e-3) настраивает оптимизатор по параметрам модели.
  • model.train() включает режим обучения (важно для Dropout и BatchNorm).
  • Внутри цикла loss.backward() считает градиенты, optimizer.step() обновляет веса.
  • optimizer.zero_grad() очищает старые градиенты перед новым шагом.
ШагНазначение
zero_gradОбнулить градиенты с прошлого шага
forward + lossПредсказание и ошибка
backwardВычислить градиенты
stepОбновить веса

Для оценки на валидации: model.eval() и with torch.no_grad(): — градиенты не нужны.

Минимальный шаблон валидации после эпохи:

Код ITЗагрузка примера кода…

Разбор фрагмента:

  • model.eval() переключает модель в режим валидации.
  • torch.no_grad() отключает расчёт градиентов и экономит память.
  • logits.argmax(dim=1) выбирает предсказанный класс с максимальным логитом.
  • correct и total накапливают статистику точности по всем батчам.
  • Формула correct / max(total, 1) защищает от деления на ноль.

Сохранение и загрузка

torch.save(model.state_dict(), "model_weights.pt")

model2 = TinyNet(10, 32, 2)
model2.load_state_dict(torch.load("model_weights.pt", map_location=device))
model2.eval()

Разбор фрагмента:

  • state_dict() возвращает словарь обученных весов и смещений.
  • torch.save(..., "model_weights.pt") сохраняет веса в файл.
  • torch.load(..., map_location=device) загружает файл на нужное устройство.
  • load_state_dict(...) применяет веса к новой копии той же архитектуры.
  • model2.eval() включает режим инференса для предсказаний.

Для полного checkpoint (веса + optimizer + epoch) сохраняют словарь. В продакшен иногда экспортируют в ONNX или TorchScript — отдельная тема развёртывания, см. применение ИИ.

Пример checkpoint:

checkpoint = {
"epoch": epoch,
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
}
torch.save(checkpoint, "checkpoint.pt")

Разбор фрагмента:

  • В checkpoint сохраняются эпоха, веса модели и состояние оптимизатора.
  • Такой формат позволяет продолжить обучение с того же места.
  • torch.save(...) пишет полный снимок тренировки в один файл.

Связь с экосистемой

КомпонентРоль
NumPy / pandasПодготовка таблиц до тензора
scikit-learnBaseline без нейросети
Hugging Face TransformersГотовые LLM поверх PyTorch
CUDAУскорение на GPU

Ограничения и этика

  • Обучение на личных данных без согласия и утечка модели — риски ИБ.
  • Большие модели требуют RAM/VRAM и энергии; для прототипа достаточно малой сети на CPU.
  • Метрики на тесте не гарантируют поведение в проде — нужен мониторинг.

Чек-лист перед первым запуском в прод

  • Зафиксированы версии torch, cuda, драйверов и зависимостей.
  • Валидация и тест не пересекаются с train по сущностям и времени.
  • Логируются loss, ключевые метрики и время эпохи.
  • Есть fallback на CPU и обработка нехватки памяти GPU.
  • Предсказания модели проверены на пограничных кейсах вручную.

Связанные материалы