Skip to main content
Fanout
flatten, reshape, view, squeeze, unsqueeze
Curriculum overview

PyTorch Fundamentals · lesson 04/9

flatten, reshape, view, squeeze, unsqueeze

flatten, reshape, view, squeeze, and unsqueeze all change a tensor's shape without changing its values or their order. They differ in memory behavior, and that difference bites when you combine them with transpose.

The idea

  • flatten(start, end) collapses a range of dimensions. (2, 3, 4).flatten()(24,); .flatten(1)(2, 12).
  • reshape(*shape) returns a view when possible and copies when not. It always works. A single -1 infers that dimension.
  • view(*shape) is view-only and requires contiguous input.
  • squeeze(dim) removes a length-1 dimension; squeeze() removes all of them. (1, 3, 1, 5).squeeze()(3, 5).
  • unsqueeze(dim) inserts a length-1 dimension. (3,).unsqueeze(1)(3, 1).

view and reshape never reorder elements; they reinterpret the same flat buffer with new strides. That is why x.transpose(0, 1).view(-1) fails: the transpose scrambled the stride order, so no valid shape describes the logical order. .reshape() copies instead. .flatten() is the safe default when you want all leading dimensions merged.

unsqueeze is how you add a batch axis or align shapes for broadcasting: logits (B, V) and labels (B,) become comparable through labels.unsqueeze(1)(B, 1).

Worked example

A model emits images of shape (32, 3, 224, 224) and a classifier expects (32, 784):

  • .flatten(1)(32, 150528)
  • .mean(dim=(2, 3))(32, 3), one average per channel
  • .reshape(32, -1)(32, 150528), the same as flatten here

A single image of shape (1, 3, 224, 224) loses its batch axis with .squeeze(0)(3, 224, 224), and gets it back with .unsqueeze(0).

In code

import torch

x = torch.arange(24).reshape(2, 3, 4)
print(x.flatten().shape)        # torch.Size([24])
print(x.flatten(1).shape)       # torch.Size([2, 12])
print(x.reshape(2, -1).shape)   # torch.Size([2, 12])

y = torch.arange(5)
print(y.unsqueeze(0).shape)     # torch.Size([1, 5])
print(y.unsqueeze(1).shape)     # torch.Size([5, 1])
print(y.unsqueeze(1).squeeze(1).shape)   # torch.Size([5])

z = torch.arange(6).reshape(2, 3).t()
print(z.is_contiguous())        # False
print(z.reshape(-1).shape)      # torch.Size([6])
print(z.contiguous().view(-1).shape)     # torch.Size([6])

Check yourself

  1. When does view fail but reshape succeed?
  2. What is the difference between squeeze(0) and squeeze() on a (1, 3, 1) tensor?
  3. How do you make (B, V) logits and (B,) labels broadcast together into a (B, V) selection?

Key takeaways

  • These ops reinterpret shape, never element order; reshape copies, view does not.
  • flatten merges a range, squeeze drops ones, unsqueeze adds one.
  • Non-contiguous tensors need .reshape() or .contiguous().view().