DEV Community

Cover image for Reshape vs View in PyTorch: What's the Difference (and When `view()` Breaks)

Reshape vs View in PyTorch: What's the Difference (and When `view()` Breaks)

You called .view() on a tensor, got a RuntimeError: view size is not compatible with input tensor's size and stride, switched to .reshape(), and it just... worked. So what's actually going on — and which one should you use?

The short answer

reshape and view both give you the same numbers under a new shape. The difference is one word: contiguity. view() returns a zero-copy view but requires the tensor to be contiguous — if it isn't, it errors. reshape() returns a view when it can and silently makes a copy when it can't, so it never errors on contiguity. Use reshape() by default; reach for view() only when you specifically need to guarantee shared memory.

Why reshape vs view exists at all

A tensor's data and its shape are two separate things. The data is a flat row of numbers sitting in memory; the shape is just the lens you look at it through. Reshaping changes the lens without touching the numbers — twelve elements can be seen as a 3×4 matrix, a 4×3, a 2×6, or a flat row of 12.

import torch

t = torch.arange(1, 13)     # 12 elements: 1..12
print(t.reshape(3, 4))
print(t.reshape(2, 6))
print(t.reshape(12))
Enter fullscreen mode Exit fullscreen mode

The only constraint is that the product of the new shape must equal the total number of elements (numel()). Twelve elements can become (3, 4) because 3 × 4 = 12, but not (3, 5) because 3 × 5 = 15.

When the tensor is contiguous — which it almost always is right after you create it — view and reshape do exactly the same thing: return a zero-cost view of the same memory.

t = torch.arange(12)
a = t.view(3, 4)      # view — always free
b = t.reshape(3, 4)   # reshape — also free here
print(a.data_ptr() == t.data_ptr())   # True
print(b.data_ptr() == t.data_ptr())   # True
Enter fullscreen mode Exit fullscreen mode

Same data pointer means no copy happened. So far, no difference.

What does "contiguous" mean in PyTorch?

A tensor is contiguous when its elements — read left-to-right along the last dimension, then the next dimension out, and so on — sit in that same order in memory. Most tensors are contiguous from birth. The operations that break contiguity are transpose, permute, and expand: they change the strides without moving the data.

t = torch.arange(6).reshape(2, 3)
print(t.is_contiguous())   # True
print(t.stride())          # (3, 1)

tt = t.T                   # transpose
print(tt.is_contiguous())  # False
print(tt.stride())         # (1, 3)
Enter fullscreen mode Exit fullscreen mode

After the transpose the strides are reversed. The numbers are still in the same flat row 0 1 2 3 4 5, but reading them in the new dimension order no longer walks through memory in order. That is non-contiguous — and it's exactly the situation where view() breaks.

When view() breaks (the error you googled)

tt = t.T                       # non-contiguous
tt.view(6)                     # RuntimeError!
Enter fullscreen mode Exit fullscreen mode

You'll see something like:

RuntimeError: view size is not compatible with input tensor's size and stride
(at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
Enter fullscreen mode Exit fullscreen mode

PyTorch is telling you the operation isn't safe as a zero-copy view. You have two fixes:

print(tt.reshape(6))               # works — quietly copies
print(tt.contiguous().view(6))     # also works — rearranges memory first, then views
Enter fullscreen mode Exit fullscreen mode

reshape is almost always the right default. Call .contiguous().view(...) only when you have a reason to force the copy explicitly.

Watch out: reshape doesn't guarantee a view

Because reshape returns a view sometimes and a copy other times, you cannot rely on it to share memory. If you need in-place edits to propagate back to the original, use view — and if it errors, that's PyTorch telling you the operation genuinely can't be done without copying.

The -1 shortcut

You can pass -1 for exactly one dimension and PyTorch infers its size from the total element count:

t = torch.arange(24)
print(t.reshape(4, -1).shape)     # (4, 6)
print(t.reshape(-1, 3).shape)     # (8, 3)
print(t.reshape(2, 3, -1).shape)  # (2, 3, 4)
Enter fullscreen mode Exit fullscreen mode

At most one -1 per call. This is everywhere in real code — you know some dimensions and let PyTorch solve for the last.

A worked example: flatten before a linear layer

The single most common reshape in practice is collapsing spatial dimensions before a fully-connected layer, while keeping the batch dimension intact:

batch = torch.randn(32, 3, 28, 28)   # N, C, H, W
flat = batch.flatten(1)              # flatten from dim 1 onward
print(flat.shape)                    # torch.Size([32, 2352])
Enter fullscreen mode Exit fullscreen mode

flatten(1) keeps dimension 0 (the batch) and merges the rest into one. Note the trap: flatten() with no argument collapses everything, batch included — inside a model's forward pass, always use flatten(1).

Common mistakes and gotchas

  • Calling view() after transpose/permute. That's the classic RuntimeError. Use reshape() or .contiguous().view().
  • Assuming reshape shares memory. Sometimes it copies. If you need a guaranteed view, use view().
  • Using bare flatten() in a forward pass. It eats the batch dimension. Use flatten(1).
  • Wrong element count. reshape(3, 5) on 12 elements raises RuntimeError: shape '[3, 5]' is invalid for input of size 12. The product must equal numel().

Recap

view and reshape both reinterpret the same flat memory under a new shape. view requires contiguity and always returns a true view; reshape returns a view when it can and copies when it must, so it never errors on contiguity. Default to reshape; use view when you specifically need shared memory.


This is one idea from my book PyTorch From Ground Up, which builds everything from tensors upward so nothing stays vague. If it helped, you can grab the free PyTorch tensor cheat-sheet here, run every example from the book in the companion notebooks on GitHub, or find the full paperback on Amazon here or find a digital copy of the book on leanpup here

Top comments (0)