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 ** 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()и не получили градиенты; - передали в
CrossEntropyLossone-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 | вероятность после sigmoid | 0.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.
Типичный цикл оптимизации:
- Forward — вход проходит через слои, на выходе считается loss.
- Backward — градиенты распространяются от loss к параметрам.
- Step — оптимизатор обновляет веса.
| Оптимизатор | Когда использовать |
|---|---|
optim.SGD | Базовый градиентный спуск; с momentum=0.9 — стабильнее |
optim.Adam | Универсальный выбор для прототипов и многих задач DL |
optim.AdamW | Adam с корректной 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-learn | Baseline без нейросети |
| Hugging Face Transformers | Готовые LLM поверх PyTorch |
| CUDA | Ускорение на GPU |
Ограничения и этика
- Обучение на личных данных без согласия и утечка модели — риски ИБ.
- Большие модели требуют RAM/VRAM и энергии; для прототипа достаточно малой сети на CPU.
- Метрики на тесте не гарантируют поведение в проде — нужен мониторинг.
Чек-лист перед первым запуском в прод
- Зафиксированы версии
torch,cuda, драйверов и зависимостей. - Валидация и тест не пересекаются с train по сущностям и времени.
- Логируются
loss, ключевые метрики и время эпохи. - Есть fallback на CPU и обработка нехватки памяти GPU.
- Предсказания модели проверены на пограничных кейсах вручную.
Связанные материалы
- Классическое машинное обучение
- Практикум — тональность отзывов
- Анализ данных
- Перцептрон на NumPy
- Keras и TensorFlow
- Разбиение данных
- Введение в ИИ
- Экосистема Python-приложений