Модель памяти PyTorch: "torch.from_numpy()" vs "torch.Tensor()"

Я пытаюсь иметь глубокое понимание того, как работает модель памяти PyTorch Tensor.

# input numpy array
In [91]: arr = np.arange(10, dtype=float32).reshape(5, 2)

# input tensors in two different ways
In [92]: t1, t2 = torch.Tensor(arr), torch.from_numpy(arr)

# their types
In [93]: type(arr), type(t1), type(t2)
Out[93]: (numpy.ndarray, torch.FloatTensor, torch.FloatTensor)

# ndarray 
In [94]: arr
Out[94]: 
array([[ 0.,  1.],
       [ 2.,  3.],
       [ 4.,  5.],
       [ 6.,  7.],
       [ 8.,  9.]], dtype=float32)

Я знаю, что тензоры PyTorch разделяют буфер памяти NumPy ndarrays. Таким образом, изменение одного будет отражено в другом. Итак, здесь я нарезаю и обновляю некоторые значения в Tensor t2

In [98]: t2[:, 1] = 23.0

И, как и ожидалось, он обновляется в t2 и arr поскольку они имеют один и тот же буфер памяти.

In [99]: t2
Out[99]: 

  0  23
  2  23
  4  23
  6  23
  8  23
[torch.FloatTensor of size 5x2]


In [101]: arr
Out[101]: 
array([[  0.,  23.],
       [  2.,  23.],
       [  4.,  23.],
       [  6.,  23.],
       [  8.,  23.]], dtype=float32)

Но t1 также обновляется. Помните, что t1 был построен с использованием torch.Tensor() тогда как t2 был построен с использованием torch.from_numpy()

In [100]: t1
Out[100]: 

  0  23
  2  23
  4  23
  6  23
  8  23
[torch.FloatTensor of size 5x2]

Таким образом, независимо от того, используем ли мы torch.from_numpy() или torch.Tensor() для построения тензора из ndarray, все такие тензоры и ndarrays используют один и тот же буфер памяти.

Основываясь на этом понимании, мой вопрос заключается в том, почему функция torch.from_numpy() существует, когда просто torch.Tensor() может выполнять задание?

Я посмотрел документацию PyTorch, но в этом ничего не говорится? Любые идеи/предложения?

Ответ 1

from_numpy() автоматически наследует входной массив dtype. С другой стороны, torch.Tensor является псевдонимом для torch.FloatTensor.

Поэтому, если вы передаете массив int64 для torch.Tensor, тензор вывода будет тензором с плавающей точкой, и они не будут делить хранилище. torch.from_numpy дает вам torch.LongTensor как и ожидалось.

a = np.arange(10)
ft = torch.Tensor(a)  # same as torch.FloatTensor
it = torch.from_numpy(a)

a.dtype  # == dtype('int64')
ft.dtype  # == torch.float32
it.dtype  # == torch.int64

Ответ 2

Это происходит от _torch_docs.py; есть также возможная дискуссия о "почему" здесь.

def from_numpy(ndarray): # real signature unknown; restored from __doc__
    """
    from_numpy(ndarray) -> Tensor

    Creates a :class:'Tensor' from a :class:'numpy.ndarray'.

    The returned tensor and 'ndarray' share the same memory. 
    Modifications to the tensor will be reflected in the 'ndarray' 
    and vice versa. The returned tensor is not resizable.

    Example::

        >>> a = numpy.array([1, 2, 3])
        >>> t = torch.from_numpy(a)
        >>> t
        torch.LongTensor([1, 2, 3])
        >>> t[0] = -1
        >>> a
        array([-1,  2,  3])
    """
    pass

Взято из numpy docs:

Различные ndarrays могут совместно использовать одни и те же данные, так что изменения, сделанные в одном ndarray, могут быть видны в другом. То есть, ndarray может быть "представлением" для другого ndarray, и данные, на которые он ссылается, заботятся о "базовом" ndarray.

docs Pytorch:

Если numpy.ndarray, torch.Tensor или torch.Tensor, torch.Storage новый тензор, который использует одни и те же данные. Если задана последовательность Python, то создается новый тензор из копии последовательности.

Ответ 3

Рекомендуемый способ построения тензоров в Pytorch - использовать следующие две фабричные функции: torch.tensor и torch.as_tensor.

torch.tensor всегда копирует данные. Например, torch.tensor(x) эквивалентен x.clone().detach().

torch.as_tensor всегда старается избегать копий данных. Один из случаев, когда as_tensor избегает копирования данных, - это если исходные данные являются массивом.