1. Тензоры, устройства и основы автодифференцирования
Тензоры, устройства и основы автодифференцирования
Эта статья — старт курса: разберём базовые строительные блоки PyTorch, без которых невозможно уверенно писать нейросетевые модели. Вы научитесь:
Официальная документация, на которую будем опираться:
Тензоры как основной тип данных
Тензор — это обобщение вектора и матрицы на произвольное число измерений. В PyTorch тензор хранит:
float32, int64В нейросетях тензоры используются для входов, весов, выходов и градиентов.
Создание тензоров
Чаще всего тензоры создают из Python-структур или фабричными функциями.
| Задача | Пример | Комментарий |
|---|---|---|
| Из списка | torch.tensor([1, 2, 3]) | Тип выводится автоматически |
| Нули | torch.zeros(2, 3) | Форма (2, 3) |
| Единицы | torch.ones(2, 3) | Удобно для масок и инициализаций |
| Случайные значения | torch.randn(2, 3) | Нормальное распределение |
| Диапазон | torch.arange(0, 10) | Аналог range, но тензор |
| Как другой тензор | torch.zeros_like(x) | Сохраняет shape, dtype, device |
Пример:
Форма (shape) и размерности
В PyTorch принято хранить батч данных первым измерением. Например:
(batch, channels, height, width)(batch, length, features)Полезные операции:
x.ndim — число измеренийx.shape — формаx.numel() — количество элементовx.view(...) или x.reshape(...) — изменить форму (при совместимости)Пример изменения формы:
Тип данных (dtype)
dtype влияет на:
Частые типы:
torch.float32 — стандарт для обученияtorch.float16 / torch.bfloat16 — ускорение на GPU (подробнее будет в темах про оптимизацию)torch.int64 — часто для индексов и меток классовЗадать dtype можно при создании или преобразовать:
Индексация и срезы
Индексация похожа на NumPy:
Важно: многие срезы возвращают представление (view) на те же данные. Если вы изменяете такой тензор на месте, вы можете поменять исходный.
Broadcasting (автоматическое расширение размеров)
Broadcasting позволяет выполнять операции над тензорами разных форм, если размеры совместимы. Упрощённая идея:
Пример: прибавим смещение (bias) к каждой строке:
Операции in-place и почему с ними аккуратно
Операции in-place меняют тензор на месте (обычно имеют суффикс _), например add_, relu_.
Они экономят память, но могут мешать автодифференцированию, если PyTorch нужно сохранить промежуточные значения для градиентов.
Пример:
Устройства: CPU и GPU
У каждого тензора есть device. Основные варианты:
cpucuda:0, cuda:1, ... — видеокарты NVIDIA (если доступно)Проверка доступности GPU:
Перенос тензоров на нужное устройство
Главное правило: все тензоры, участвующие в одной операции, должны быть на одном устройстве.
Также можно переносить и менять dtype одной командой:
Практические советы по устройствам
Expected all tensors to be on the same device).to("cuda") внутри тесного цикла без необходимостиОсновы автодифференцирования (autograd)
Чтобы обучать нейросеть, нужно минимизировать функцию потерь и вычислять градиенты параметров. PyTorch делает это автоматически через автодифференцирование.
Идея:
backward(), PyTorch проходит по графу в обратном направлении и вычисляет производные!Иллюстрация того, как PyTorch строит граф и распространяет градиенты назад
requires_grad и градиенты
Если вы хотите, чтобы PyTorch считал градиенты по тензору, нужно включить флаг requires_grad=True.
Что здесь происходит математически:
x.gradВажно: backward() требует, чтобы итог был скаляром (одним числом). Если результат — вектор/матрица, обычно сводят к скаляру через mean() или sum().
Граф вычислений и grad_fn
Тензоры, полученные в результате операций, обычно имеют поле grad_fn — ссылку на функцию/узел графа, который создал этот тензор.
Накопление градиентов и обнуление
Градиенты в PyTorch накапливаются в .grad. Если вы несколько раз вызовете backward(), градиенты суммируются.
Это полезно, например, для градиентного накопления, но в стандартном обучении перед новым шагом нужно обнулять градиенты оптимизатора (к оптимизаторам вернёмся в следующих статьях).
Ключевой факт на будущее:
.grad не сбрасывается автоматическиОтключение вычисления градиентов: no_grad и detach
Иногда градиенты не нужны: например, при валидации модели или при подготовке данных.
torch.no_grad() отключает построение графа внутри блока:
detach() создаёт новый тензор, который разделяет те же данные, но не участвует в графе:
Практическое различие:
no_grad удобен как режим выполнения кодаdetach удобен, когда нужно «отрезать» конкретный тензор от графаЧастые ошибки новичков
.gradbackward() на не-скаляре без сведения к скаляруМини-чеклист перед тем как писать модель
shape входов и выходов (и что такое батч)dtype (обычно float32 для входов и весов, int64 для индексов)devicerequires_grad, no_grad, detach)В следующей статье логично перейти к nn.Module, слоям (nn.Linear, nn.Conv2d), контейнерам (nn.Sequential) и тому, как параметры модели связываются с autograd.