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-1infers 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
- When does
viewfail butreshapesucceed? - What is the difference between
squeeze(0)andsqueeze()on a(1, 3, 1)tensor? - 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;
reshapecopies,viewdoes not. flattenmerges a range,squeezedrops ones,unsqueezeadds one.- Non-contiguous tensors need
.reshape()or.contiguous().view().