- Устанавливать PyTorch в виртуальное окружение и различать сборки для CPU и CUDA
- Создавать тензоры, читать и менять их shape и dtype
- Применять правила транслирования и матричного умножения и заранее определять форму результата
- Переносить тензоры между CPU и GPU и преобразовывать их в массивы NumPy и обратно
Массивы NumPy отлично подходят для численных расчётов, но для глубокого обучения им не хватает двух вещей: они не работают на GPU и не умеют автоматически вычислять производные. PyTorch закрывает именно эти два пробела. Его главный объект — тензор — очень похож на массив NumPy, но может находиться на видеокарте и отслеживать свои градиенты. В этом уроке ты научишься свободно работать с тензорами, а следующий урок посвящён автоматическому дифференцированию.
Что такое PyTorch и как его установить
PyTorch — библиотека глубокого обучения с открытым исходным кодом, созданная в лаборатории искусственного интеллекта компании Meta (ранее Facebook); с 2022 года ею управляет PyTorch Foundation в составе Linux Foundation. Исследователи и компании широко используют её, чтобы создавать, обучать и внедрять нейронные сети. PyTorch не работает в браузере, поэтому код PyTorch из этого модуля запускай на своём компьютере; результат показан под каждым примером.
- 1Создай виртуальное окружение
В папке проекта выполни
python -m venv .venvи активируй окружение — так PyTorch не будет мешать другим проектам. - 2Выбери сборку для CPU или CUDA
Если видеокарты NVIDIA нет, сборки для CPU для этого модуля вполне достаточно. Для сборки с GPU (CUDA) возьми точную команду из конфигуратора на сайте pytorch.org, выбрав свою ОС и версию CUDA.
- 3Установи и проверь
Выполни
pip install torch, затем в Python напишиimport torchи выведи версию.
python -m venv .venv
# Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate
pip install torch numpy
# CPU-only build (smaller download):
pip install torch --index-url https://download.pytorch.org/whl/cpuimport torch
print(torch.__version__)
print(torch.cuda.is_available())2.14.0+cpu False
True.Многомерный массив чисел одного типа. При числе измерений (ndim) 0 это скаляр, 1 — вектор, 2 — матрица, 3 и больше — тензор общего вида. У каждого тензора есть shape (длина по каждому измерению), dtype (тип элементов) и device (CPU или GPU).
| Данные | shape | Пояснение |
|---|---|---|
| значение потерь | () | скаляр, 0 измерений |
| табличные данные | (N, D) | N примеров, D признаков |
| цветное изображение | (3, H, W) | каналы RGB, высота, ширина |
| пакет изображений | (N, C, H, W) | стандартный порядок осей для изображений в PyTorch |
| пакет текстов | (N, T, E) | T токенов, каждый — вектор размерности E |
Создание тензоров: shape и dtype
import torch
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(a)
print(a.shape, a.ndim, a.dtype)
print(torch.tensor([1.5, 2.0]).dtype)
print(torch.zeros(2, 3))
print(torch.arange(0, 10, 3))
torch.manual_seed(42)
print(torch.rand(2, 2))
print(a.float().dtype, a.to(torch.float16).dtype)tensor([[1, 2, 3],
[4, 5, 6]])
torch.Size([2, 3]) 2 torch.int64
torch.float32
tensor([[0., 0., 0.],
[0., 0., 0.]])
tensor([0, 3, 6, 9])
tensor([[0.8823, 0.9150],
[0.3829, 0.9593]])
torch.float32 torch.float16torch.tensor(...)сам определяет тип: целые →int64, дробные →float32. А в NumPy для дробных по умолчаниюfloat64!torch.zeros,torch.ones,torch.arange,torch.linspaceсоздают готовые тензоры, аtorch.rand(равномерное распределение от 0 до 1) иtorch.randn(стандартное нормальное) — случайные.torch.manual_seed(42)фиксирует генератор случайных чисел — результаты становятся воспроизводимыми..float(),.long()или.to(torch.float16)меняют тип и возвращают новый тензор.
| dtype | Байт | Где используется |
|---|---|---|
torch.float32 | 4 | веса, входы, потери — тип по умолчанию |
torch.float16 / torch.bfloat16 | 2 | быстрое обучение со смешанной точностью (mixed precision) на GPU |
torch.int64 | 8 | метки классов, индексы, номера токенов |
torch.bool | 1 | маски (например, x > 0) |
- d₁ … dₖразмеры из shape
- numelчисло элементов (
x.numel()) - sразмер одного элемента в байтах (
x.element_size())
Пакет из 64 цветных изображений размером 224 × 224 хранится в типе float32. Какова форма тензора и сколько байт он занимает? А в float16?
Показать решениеСкрыть решение
numel = 64 · 3 · 224 · 224 = 9 633 792.
Память = 9 633 792 · 4 = 38 535 168 байт ≈ 38,5 МБ.
В
float16 каждый элемент занимает 2 байта, поэтому памяти нужно вдвое меньше: ≈ 19,3 МБ. Именно поэтому смешанная точность экономит память GPU.Изменение формы и индексация
import torch
m = torch.arange(12).reshape(3, 4)
print(m)
print(m[0], m[:, 1])
print(m[1, 2].item())
print(m[m > 8])
v = m.view(2, 6)
print(v.shape, m.T.shape)
print(m.unsqueeze(0).shape, m.flatten().shape)
v[0, 0] = 100
print(m[0, 0])tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
tensor([0, 1, 2, 3]) tensor([1, 5, 9])
6
tensor([ 9, 10, 11])
torch.Size([2, 6]) torch.Size([4, 3])
torch.Size([1, 3, 4]) torch.Size([12])
tensor(100)- Индексация как в NumPy:
m[0]— первая строка,m[:, 1]— второй столбец,m[m > 8]— выбор по маске..item()превращает тензор из одного элемента в обычное число Python. reshape(2, 6)иview(2, 6)меняют форму, не меняя числа элементов; если вместо одного размера написать-1, PyTorch вычислит его сам:x.view(-1, 784).viewне копирует данные, а смотрит на ту же память через другое «окно». Поэтомуv[0, 0] = 100изменило иm!unsqueeze(0)добавляет новую ось размера 1 (например, чтобы превратить одно изображение в пакет),squeeze()удаляет такие оси,.Tтранспонирует матрицу, аpermuteпереставляет оси в любом порядке.
Операции: транслирование и матричное умножение
Операторы +, -, *, /, ** работают поэлементно. Редукции sum, mean, max считаются вдоль оси, заданной аргументом dim: a.mean(dim=0) — среднее по каждому столбцу. Тензоры разной формы согласуются по правилам транслирования (broadcasting):
- Формы сравниваются справа налево; более короткая форма дополняется слева осями размера 1.
- Два размера совместимы, если они равны или один из них равен 1.
- Ось размера 1 «растягивается» до размера другой (без копирования данных).
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
b = torch.tensor([10., 20.])
print(a + b)
print(a * a)
print(a @ a)
print(a.sum(), a.mean(dim=0))
X = torch.ones(64, 3)
W = torch.ones(3, 5)
print((X @ W).shape)
print((torch.ones(4, 1) + torch.ones(3)).shape)tensor([[11., 22.],
[13., 24.]])
tensor([[ 1., 4.],
[ 9., 16.]])
tensor([[ 7., 10.],
[15., 22.]])
tensor(10.) tensor([2., 3.])
torch.Size([64, 5])
torch.Size([4, 3])- A, Bматрицы размеров m × n и n × p
- nвнутренний размер — должен совпадать у обеих матриц
- Cᵢⱼскалярное произведение i-й строки A и j-го столбца B
Матричное умножение — главная операция нейросетей: умножая входы размера (N, D) на матрицу весов (D, K), мы обрабатываем весь пакет за один раз. Для тензоров с большим числом осей @ действует на две последние оси, а остальные считаются пакетными.
Найди форму результата каждой операции или укажи, что будет ошибка: а) (64, 3) @ (3, 5); б) (4, 1) + (3,); в) (32, 10) + (10,); г) (2, 3) @ (2, 3); д) (8, 1, 6) + (7, 1).
Показать решениеСкрыть решение
б) (3,) → (1, 3); 4 с 1 и 1 с 3 совместимы: (4, 3).
в) (10,) → (1, 10) и прибавляется к каждой строке (так работает вектор смещений): (32, 10).
г) Внутренние размеры 3 ≠ 2 — ошибка. Нужно было
A @ B.T: (2, 2).д) (7, 1) → (1, 7, 1); по осям: 8 и 1 → 8, 1 и 7 → 7, 6 и 1 → 6: (8, 7, 6).
Работа с GPU и NumPy
В видеокарте тысячи простых ядер, которые считают матричные произведения параллельно; обучение больших моделей на GPU часто в разы, а то и в десятки раз быстрее, чем на CPU. Стандартный приём: в начале скрипта выбрать device, а затем переносить туда модель и данные с помощью .to(device). На компьютерах Apple Silicon для GPU есть устройство 'mps'.
import numpy as np
import torch
device = 'cuda' if torch.cuda.is_available() else 'cpu'
x = torch.ones(2, 2, device=device)
print(device, x.device)
arr = np.array([1.0, 2.0, 3.0])
t = torch.from_numpy(arr)
arr[0] = 99
print(t)
back = (t * 2).numpy()
print(back, type(back).__name__)cpu cpu tensor([99., 2., 3.], dtype=torch.float64) [198. 4. 6.] ndarray
cuda cuda:0.torch.from_numpy(arr) разделяет память массива: изменение arr[0] = 99 видно и в тензоре. Обрати внимание, что dtype остался float64 — перед обучением переведи его во float32 через .float(). В обратную сторону .numpy() работает только для тензоров на CPU: тензор с GPU сначала верни через .cpu(), а если он отслеживает градиенты, сначала вызови .detach(): t.detach().cpu().numpy().
Главное
- Тензор — многомерный массив со свойствами shape, dtype и device; изображения в PyTorch хранятся в форме (N, C, H, W).
- Тип по умолчанию для дробных чисел —
float32, для меток и индексов —int64. viewразделяет память и требует непрерывной памяти; если не уверен, используйreshape.- Транслирование сравнивает формы справа: размеры должны быть равны или один из них равен 1.
@— матричное произведение: (m × n) @ (n × p) → (m × p). - Модель и данные переносятся на одно устройство через
.to(device);from_numpyи.numpy()разделяют память.
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
torch.tensor([1.5, 2.0]).dtype?