PyTorch
Flatten
View
Tensor Operations
Deep Learning

What is the difference between .flatten and .view-1 in PyTorch?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

When building neural networks in PyTorch, you frequently need to reshape tensors into one dimension, for example before passing a feature map into a fully connected layer. The two most common ways to do this are .flatten() and .view(-1). They look similar and often produce the same result, but they behave differently under the hood, especially when tensors are not contiguous in memory. This article explains exactly when each method works, when it fails, and which one you should prefer.

What Contiguous Means

Before comparing the two methods, you need to understand memory layout. A tensor is "contiguous" when its elements are stored in a single, unbroken block of memory in row-major (C) order. Most tensors you create are contiguous by default:

python
1import torch
2
3x = torch.randn(2, 3)
4print(x.is_contiguous())  # True

However, certain operations like .transpose(), .permute(), or .narrow() return views that share the same underlying data but with a different stride pattern. These views are often non-contiguous:

python
x = torch.randn(2, 3)
y = x.transpose(0, 1)
print(y.is_contiguous())  # False

How .view(-1) Works

.view() reshapes a tensor by reinterpreting its underlying data buffer. The -1 argument tells PyTorch to infer the size of that dimension automatically. For a full flatten, you use .view(-1) to collapse all dimensions into one:

python
x = torch.randn(2, 3, 4)
flat = x.view(-1)
print(flat.shape)  # torch.Size([24])

The critical requirement is that .view() only works on contiguous tensors. If the tensor is not contiguous, it raises a RuntimeError:

python
x = torch.randn(2, 3, 4)
y = x.transpose(0, 1)  # now non-contiguous
flat = y.view(-1)       # RuntimeError!

To fix this, you must call .contiguous() first:

python
flat = y.contiguous().view(-1)  # works

How .flatten() Works

.flatten() collapses one or more dimensions into a single dimension. By default, it flattens all dimensions:

python
x = torch.randn(2, 3, 4)
flat = x.flatten()
print(flat.shape)  # torch.Size([24])

The key difference is that .flatten() handles non-contiguous tensors automatically. If the tensor is already contiguous, it returns a view (no data copy). If it is not contiguous, it creates a contiguous copy and then returns the flattened result:

python
1x = torch.randn(2, 3, 4)
2y = x.transpose(0, 1)  # non-contiguous
3flat = y.flatten()      # works without error
4print(flat.shape)       # torch.Size([24])

Partial Flattening

.flatten() supports flattening a specific range of dimensions, which .view(-1) cannot do as cleanly:

python
1x = torch.randn(2, 3, 4, 5)
2
3# Flatten dimensions 1 and 2, keep 0 and 3
4result = x.flatten(start_dim=1, end_dim=2)
5print(result.shape)  # torch.Size([2, 12, 5])

This is common in convolutional neural networks where you want to flatten spatial dimensions while keeping the batch dimension intact:

python
# In a CNN forward method
batch = torch.randn(16, 64, 7, 7)  # (batch, channels, height, width)
flat = batch.flatten(1)              # (16, 3136) -- flatten everything except batch

To achieve the same with .view(), you need to calculate the size manually:

python
flat = batch.view(batch.size(0), -1)  # equivalent but more verbose

Performance Considerations

When the tensor is contiguous, both .flatten() and .view(-1) return a view of the same data without copying. Performance is identical in this case.

When the tensor is non-contiguous, .flatten() creates a copy to make the data contiguous. This involves a memory allocation and a data copy, which adds overhead. The .view(-1) approach forces you to call .contiguous() explicitly, making the copy visible in your code:

python
# Both do the same thing for non-contiguous tensors
flat_a = y.flatten()              # implicit copy
flat_b = y.contiguous().view(-1)  # explicit copy

Neither is faster than the other for non-contiguous tensors, since both need to copy data. The difference is only in explicitness.

Using .reshape() as an Alternative

PyTorch also provides .reshape(), which behaves like .view() when possible (returns a view) and falls back to copying when the tensor is non-contiguous. It is effectively the same behavior as .flatten() when used with -1:

python
flat = x.reshape(-1)  # works whether contiguous or not

The trade-off is that .reshape() does not guarantee a view, so if you need to ensure that changes to the reshaped tensor are reflected in the original, use .view().

Common Pitfalls

  • Silent copies with .flatten() and .reshape(): Because these methods handle non-contiguous tensors by copying, you might not realize you are working with a copy. Modifications to the flattened tensor will not affect the original.
  • RuntimeError with .view(): If you call .view() on a non-contiguous tensor without .contiguous(), your code crashes at runtime. This is a frequent source of bugs after transpose or permute operations.
  • Forgetting start_dim in CNNs: Calling .flatten() without arguments flattens the batch dimension too, which almost always causes a shape mismatch in the next layer. Use .flatten(1) to preserve the batch dimension.
  • Gradient flow: All three methods (.view(), .flatten(), .reshape()) support autograd. Gradients flow through them correctly, so they are safe to use in training.

Summary

Use .flatten() when you want a safe, readable way to collapse dimensions that works regardless of memory layout. Use .view(-1) when you know the tensor is contiguous and you want to make that assumption explicit. For partial flattening in CNNs, .flatten(start_dim=1) is the cleanest option. When in doubt, prefer .flatten() because it handles edge cases automatically.


Course illustration
Course illustration

All Rights Reserved.