```{eval-rst} .. currentmodule:: torch ``` (tensor-view-doc)= # Tensor Views PyTorch allows a tensor to be a ``View`` of an existing tensor. View tensor shares the same underlying data with its base tensor. Supporting ``View`` avoids explicit data copy, thus allows us to do fast and memory efficient reshaping, slicing and element-wise operations. For example, to get a view of an existing tensor ``t``, you can call ``t.view(...)``. ```python >>> t = torch.rand(4, 4) >>> b = t.view(2, 8) >>> t.storage().data_ptr() == b.storage().data_ptr() # `t` and `b` share the same underlying data. True # Modifying view tensor changes base tensor as well. >>> b[0][0] = 3.14 >>> t[0][0] tensor(3.14) ``` Since views share underlying data with its base tensor, if you edit the data in the view, it will be reflected in the base tensor as well. Typically a PyTorch op returns a new tensor as output, e.g. {meth}`~torch.Tensor.add`. But in case of view ops, outputs are views of input tensors to avoid unnecessary data copy. No data movement occurs when creating a view, view tensor just changes the way it interprets the same data. Taking a view of contiguous tensor could potentially produce a non-contiguous tensor. Users should pay additional attention as contiguity might have implicit performance impact. {meth}`~torch.Tensor.transpose` is a common example. ```python >>> base = torch.tensor([[0, 1],[2, 3]]) >>> base.is_contiguous() True >>> t = base.transpose(0, 1) # `t` is a view of `base`. No data movement happened here. # View tensors might be non-contiguous. >>> t.is_contiguous() False # To get a contiguous tensor, call `.contiguous()` to enforce # copying data when `t` is not contiguous. >>> c = t.contiguous() ``` For reference, here’s a full list of view ops in PyTorch: - Basic slicing and indexing op, e.g. ``tensor[0, 2:, 1:7:2]`` returns a view of base ``tensor``, see note below. - {meth}`~torch.Tensor.adjoint` - {meth}`~torch.Tensor.as_strided` - {meth}`~torch.Tensor.detach` - {meth}`~torch.Tensor.diagonal` - {meth}`~torch.Tensor.expand` - {meth}`~torch.Tensor.expand_as` - {meth}`~torch.Tensor.movedim` - {meth}`~torch.Tensor.narrow` - {meth}`~torch.Tensor.permute` - {meth}`~torch.Tensor.select` - {meth}`~torch.Tensor.squeeze` - {meth}`~torch.Tensor.transpose` - {meth}`~torch.Tensor.t` - {attr}`~torch.Tensor.T` - {attr}`~torch.Tensor.H` - {attr}`~torch.Tensor.mT` - {attr}`~torch.Tensor.mH` - {attr}`~torch.Tensor.real` - {attr}`~torch.Tensor.imag` - {meth}`~torch.Tensor.view_as_real` - {meth}`~torch.Tensor.unflatten` - {meth}`~torch.Tensor.unfold` - {meth}`~torch.Tensor.unsqueeze` - {meth}`~torch.Tensor.view` - {meth}`~torch.Tensor.view_as` - {meth}`~torch.Tensor.unbind` - {meth}`~torch.Tensor.split` - {meth}`~torch.Tensor.hsplit` - {meth}`~torch.Tensor.vsplit` - {meth}`~torch.Tensor.tensor_split` - {meth}`~torch.Tensor.split_with_sizes` - {meth}`~torch.Tensor.swapaxes` - {meth}`~torch.Tensor.swapdims` - {meth}`~torch.Tensor.chunk` - {meth}`~torch.Tensor.indices` (sparse tensor only) - {meth}`~torch.Tensor.values` (sparse tensor only) ```{note} When accessing the contents of a tensor via indexing, PyTorch follows Numpy behaviors that basic indexing returns views, while advanced indexing returns a copy. Assignment via either basic or advanced indexing is in-place. See more examples in [Numpy indexing documentation](https://numpy.org/doc/stable/user/basics.indexing.html). ``` It's also worth mentioning a few ops with special behaviors: - {meth}`~torch.Tensor.reshape`, {meth}`~torch.Tensor.reshape_as` and {meth}`~torch.Tensor.flatten` can return either a view or new tensor, user code shouldn't rely on whether it's view or not. - {meth}`~torch.Tensor.contiguous` returns **itself** if input tensor is already contiguous, otherwise it returns a new contiguous tensor by copying data. For a more detailed walk-through of PyTorch internal implementation, please refer to [ezyang's blogpost about PyTorch Internals](http://blog.ezyang.com/2019/05/pytorch-internals/).