PyTorch view(): Reshape Tensors Without Copying

PyTorch view() returns a tensor with a new shape that shares the original’s data, so it copies nothing. The new shape must hold the same number of elements. Pass -1 for one dimension and PyTorch works it out:

x = torch.arange(12)
x.view(4, 3)      # 4 rows x 3 columns
x.view(2, -1)     # PyTorch fills in 6

I ran these examples with PyTorch 2.14.0+cpu on Python 3.12.5; a CPU build is enough for everything here.

You’ll reshape with view and -1, see why changing a view changes the original, fix the “not contiguous” error, compare view with reshape, and use view inside a model.

Reshape a tensor with PyTorch view()

Here a 12-month sales tensor becomes 4 quarters of 3 months. The only rule is that the element count can’t change:

import torch

sales = torch.arange(12)                  # 12 monthly sales figures
print(sales.shape)

by_quarter = sales.view(4, 3)             # 4 quarters x 3 months
print(by_quarter)
print(sales.view(2, -1).shape, sales.view(-1, 6).shape)   # -1 is worked out for you

try:
    sales.view(5, 3)                      # 15 elements don't fit 12
except RuntimeError as err:
    print("RuntimeError:", err)

Output:

torch.Size([12])
tensor([[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]])
torch.Size([2, 6]) torch.Size([2, 6])
RuntimeError: shape '[5, 3]' is invalid for input of size 12
Command Prompt output of PyTorch view reshaping a 12-element tensor into 4 by 3, using -1, and a RuntimeError for an invalid shape
view(4, 3) groups the months by quarter; -1 is inferred, and an impossible shape raises RuntimeError.

Only one dimension can be -1. For adding a dimension of size 1 rather than regrouping, unsqueeze() is clearer; see adding a dimension to a PyTorch tensor.

Does PyTorch view() copy the data?

No. A view is a new window onto the same memory, so writing through the view changes the original tensor. data_ptr() proves they point to the same storage:

import torch

prices = torch.tensor([19.99, 5.25, 102.00, 7.50])
grid = prices.view(2, 2)

grid[0, 0] = 0.0                          # change the view...
print(prices)                             # ...and the original changes too
print("same memory:", grid.data_ptr() == prices.data_ptr())

copy = prices.view(2, 2).clone()          # clone() when you need an independent copy
copy[1, 1] = 99.0
print(prices)

Output:

tensor([  0.0000,   5.2500, 102.0000,   7.5000])
same memory: True
tensor([  0.0000,   5.2500, 102.0000,   7.5000])
Command Prompt output of PyTorch showing a change through a view altering the original tensor and clone creating an independent copy
Setting the first price through the view changed prices; the cloned copy left it alone.

Call .clone() when you need a separate tensor. The same sharing applies to slicing, so keep it in mind before editing in place, as in creating empty tensors in PyTorch.

Fix: view size is not compatible with input tensor’s size and stride

view() needs the elements laid out in row order in memory (contiguous). Operations like t(), transpose() and permute() only change strides, so a following view() fails:

import torch

x = torch.arange(6).view(2, 3)
t = x.t()                                 # transpose: same data, different strides
print("contiguous?", x.is_contiguous(), t.is_contiguous())

try:
    t.view(6)
except RuntimeError as err:
    print("RuntimeError:", str(err).split(".")[0])

print(t.contiguous().view(6))             # copy into row order, then view
r = t.reshape(6)                          # reshape copies only when it must
print(r, "| reshape shares memory:", r.data_ptr() == t.data_ptr())
print("reshape of contiguous shares memory:", x.reshape(6).data_ptr() == x.data_ptr())

Output:

contiguous? True False
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces)
tensor([0, 3, 1, 4, 2, 5])
tensor([0, 3, 1, 4, 2, 5]) | reshape shares memory: False
reshape of contiguous shares memory: True
Command Prompt output of PyTorch view failing on a transposed tensor, then contiguous().view and reshape working
The transposed tensor isn’t contiguous, so view() raises; contiguous() or reshape() fixes it.

Notice that reshape() copied the transposed data but shared memory with the contiguous one. It’s a view when possible and a copy when it has to be.

PyTorch view vs reshape

view()reshape()
Copies dataNeverOnly if needed
Works on non-contiguous tensorsNo (RuntimeError)Yes
Shares memory with the inputAlwaysWhen it can
Best forGuaranteed no-copy reshapesCode that should just work

I use view() when I want an error if a copy would happen, and reshape() everywhere else. More shape options are in reshaping tensors in PyTorch.

Use view() to flatten a batch before a Linear layer

The most common real use: image tensors come in as (batch, channels, height, width), and a fully connected layer wants (batch, features). x.view(x.size(0), -1) keeps the batch and flattens the rest:

import torch
from torch import nn

torch.manual_seed(0)
images = torch.rand(8, 3, 32, 32)         # a batch of 8 RGB images, 32x32

class TinyClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Linear(3 * 32 * 32, 10)

    def forward(self, x):
        x = x.view(x.size(0), -1)         # keep the batch size, flatten the rest
        return self.fc(x)

out = TinyClassifier()(images)
print("input:", tuple(images.shape), "-> flattened:", tuple(images.view(8, -1).shape), "-> output:", tuple(out.shape))

bits = torch.tensor([1.0, -2.0]).view(torch.int32)   # view(dtype): same bytes, new type
print(bits)

Output:

input: (8, 3, 32, 32) -> flattened: (8, 3072) -> output: (8, 10)
tensor([ 1065353216, -1073741824], dtype=torch.int32)
Command Prompt output of a PyTorch model using view to flatten a batch of 8 images from 3x32x32 to 3072 features before nn.Linear
Eight 3x32x32 images become eight rows of 3,072 features, and the Linear layer outputs 10 scores per image.

torch.flatten(x, 1) and nn.Flatten() do the same job and also handle non-contiguous input; see PyTorch flatten. For the layer itself, see PyTorch nn.Linear.

The last line shows a rarer form, view(dtype), which reinterprets the same bytes as another type. It’s useful for low-level bit tricks, not for normal type conversion; use .to(dtype) for that.

PyTorch view rules to remember

  • The total number of elements must stay the same.
  • Use -1 for at most one dimension.
  • The result shares memory with the original; use clone() for a copy.
  • It needs a contiguous tensor; after transpose() or permute(), use reshape() or contiguous() first.

Here are more PyTorch tensor guides:

Frequently asked questions

What does view() do in PyTorch?

It returns a tensor with a different shape that shares the original’s data, without copying it.

What does -1 mean in view()?

PyTorch calculates that dimension from the total element count. Only one dimension can be -1.

Why do I get “view size is not compatible with input tensor’s size and stride”?

The tensor isn’t contiguous, usually after transpose or permute. Call .contiguous() first or use reshape().

What’s the difference between view and reshape?

view never copies and fails on non-contiguous tensors; reshape returns a view when possible and copies when needed.

Does changing a view change the original tensor?

Yes, they share memory. Use .clone() to get an independent copy.

Where is Tensor.view documented?

See torch.Tensor.view in the PyTorch docs.