Мост с numpy
Связываем PyTorch с numpy: туда и обратно, с важным нюансом об общей памяти.
Мост с numpy — это переход между
torch.Tensorиnumpy.ndarray; на CPU они могут делить одну и ту же память.
Зачем это нужно
Половина инструментов для данных (загрузка CSV через pandas, картинки через OpenCV, графики через matplotlib) говорит на языке numpy-массивов. PyTorch создан так, чтобы переход был дешёвым: API тензоров намеренно повторяет numpy. Поэтому в реальном проекте вы постоянно переводите данные туда-сюда: получили numpy-массив из файла, превратили в тензор, обучили модель, результат вернули в numpy для визуализации.
Из numpy в тензор
Есть два пути: torch.from_numpy(arr) и torch.tensor(arr). Разница принципиальна.
import numpy as np
import torch
arr = np.array([1.0, 2.0, 3.0])
t1 = torch.from_numpy(arr) # ОБЩАЯ память с arr
t2 = torch.tensor(arr) # КОПИЯ данных
arr[0] = 99.0
print(t1) # tensor([99., 2., 3.]) — изменился вслед за arr!
print(t2) # tensor([1., 2., 3.]) — копия, не тронут
from_numpy не копирует данные — тензор и массив указывают на одну и ту же область памяти. Изменили массив — изменился тензор, и наоборот. Это быстро и экономно, но опасно, если вы не ждёте такой связи. torch.tensor всегда делает независимую копию.
Из тензора в numpy
Обратно — метод .numpy(). Он тоже разделяет память с тензором (для CPU-тензоров):
t = torch.ones(3)
arr = t.numpy() # общая память
t[0] = 5.0
print(arr) # [5. 1. 1.] — массив увидел изменение
Подводные камни
Два случая, когда .numpy() не сработает напрямую и нужно подготовить тензор:
| Проблема | Решение |
| Тензор на GPU — у numpy нет GPU | сначала .cpu(): t.cpu().numpy() |
Тензор с requires_grad=True (в графе) | сначала .detach(): t.detach().numpy() |
На практике безопасная универсальная формула, которую вы будете писать постоянно при выводе результатов модели:
# превратить любой тензор (GPU, в графе) в numpy
result = some_tensor.detach().cpu().numpy()
Порядок важен: сперва detach() (выйти из графа), потом cpu() (перенести на процессор), потом numpy(). Что такое detach и граф — подробно в следующем разделе про autograd.
Чистый Python для интуиции
Идея «общей памяти» знакома и в обычном Python: список, на который ссылаются две переменные, меняется через любую из них. Этот пример запускается:
# аналогия общей памяти из чистого Python
a = [1, 2, 3]
b = a # b ссылается на тот же список (как from_numpy)
c = a.copy() # c — независимая копия (как torch.tensor)
a[0] = 99
print("b:", b) # b увидел изменение
print("c:", c) # c не тронут
Вывод:
b: [99, 2, 3] c: [1, 2, 3]
Та же логика: from_numpy/.numpy() ведут себя как b = a (общая память), а torch.tensor(arr) — как a.copy().
Итог
torch.from_numpy(arr)— общая память;torch.tensor(arr)— независимая копия..numpy()переводит тензор в массив, тоже разделяя память (на CPU).- Перед
.numpy()часто нужны.detach()(выйти из графа) и.cpu()(с GPU). - Универсальная формула вывода результата:
tensor.detach().cpu().numpy().