Torch Visualized: The Operations

Shape errors are the tax you pay for working with tensors, and most of them come from a stubborn gap: you can recite what permute does and still not see it. So I drew them. Fifty PyTorch operations, one figure each, all in the same coordinate frame.

Every figure shows the same thing in the same way. The faint block is the input, the solid one the output, and both sit at the same origin — so the change happens in place rather than being something you reconstruct from two side-by-side pictures. Three black axes meet at that origin and are ticked by index. The code panel on each figure is the real, runnable call that produced it, with the shape change as a trailing comment.

A caveat worth stating up front: every number in every figure is read back out of the tensor after the actual torch call runs, and each page asserts the identity it claims. The pictures cannot quietly disagree with the semantics. Where an operation changes values but not shape — softmax, cumsum, clamp — the cells are shaded by value, because two identically-shaped blocks would otherwise show nothing at all.

Three families are missing on purpose. matmul needs A on the left, B above and C at their intersection; two operands superimposed at one origin are indistinguishable, and I deleted the page after looking at it. conv2d and max_pool2d produce a different grid from their input, so overlaying them at one origin would misstate the geometry. And to(device), detach, no_grad and item have no geometry to draw at all.

Reshaping

Same numbers, different grouping. None of these move data.

1. Tensor.view

Tensor.view — One tape of memory, cut differently — which is why permute(...).view(...) raises.

One tape of memory, cut differently — which is why permute(...).view(...) raises. · Tensor.view docs

2. torch.flatten

torch.flatten — The block unrolled into the tape it always was.

The block unrolled into the tape it always was. · torch.flatten docs

3. torch.flatten

torch.flatten — Merges a range of axes. This is the line that feeds a conv map into a linear layer.

Merges a range of axes. This is the line that feeds a conv map into a linear layer. · torch.flatten docs

4. torch.unflatten

torch.unflatten — One axis splits into two whose product matches — how a projection is cut into heads.

One axis splits into two whose product matches — how a projection is cut into heads. · torch.unflatten docs

5. torch.squeeze

torch.squeeze — The two drawings are identical by necessity: a size-1 axis occupies no space.

The two drawings are identical by necessity: a size-1 axis occupies no space. · torch.squeeze docs

6. torch.unsqueeze

torch.unsqueeze — The picture cannot change. Only where the axis is inserted differs — which decides how it later broadcasts.

The picture cannot change. Only where the axis is inserted differs — which decides how it later broadcasts. · torch.unsqueeze docs

7. Tensor.contiguous

Tensor.contiguous — The one page whose picture is deliberately unchanged: it re-lays the bytes so the fast path works.

The one page whose picture is deliberately unchanged: it re-lays the bytes so the fast path works. · Tensor.contiguous docs

Reordering axes

The elements stay; which index reaches them changes.

8. torch.permute

torch.permute — Axes relabelled, strides reordered. Nothing is copied.

Axes relabelled, strides reordered. Nothing is copied. · torch.permute docs

9. torch.transpose

torch.transpose — Rows become columns; the two axes swap strides, so the result is no longer contiguous.

Rows become columns; the two axes swap strides, so the result is no longer contiguous. · torch.transpose docs

10. torch.movedim

torch.movedim — One axis relocated, the others closing the gap. Easier to reason about than a full permutation.

One axis relocated, the others closing the gap. Easier to reason about than a full permutation. · torch.movedim docs

11. torch.flip

torch.flip — Row order reverses. Unlike a slice, this one copies.

Row order reverses. Unlike a slice, this one copies. · torch.flip docs

12. torch.roll

torch.roll — Rows shift and wrap. Colour is keyed to the original row, so the wrap is visible.

Rows shift and wrap. Colour is keyed to the original row, so the wrap is visible. · torch.roll docs

13. torch.sort

torch.sort — Each row ordered independently. Compare the dim-0 sort, which is a trap.

Each row ordered independently. Compare the dim-0 sort, which is a trap. · torch.sort docs

Growing

More cells out than in — by repetition, padding, or broadcast.

14. torch broadcasting

torch broadcasting — Each size-1 axis is re-read with stride 0 until it matches. Both operands are ghosts inside the result.

Each size-1 axis is re-read with stride 0 until it matches. Both operands are ghosts inside the result. · torch broadcasting docs

15. Tensor.expand

Tensor.expand — Stride 0: all five slabs are the same memory seen five times.

Stride 0: all five slabs are the same memory seen five times. · Tensor.expand docs

16. Tensor.repeat

Tensor.repeat — The picture expand gives, different memory: repeat allocates and copies.

The picture expand gives, different memory: repeat allocates and copies. · Tensor.repeat docs

17. torch.repeat_interleave

torch.repeat_interleave — 0,0,1,1,2,2 — where repeat would give 0,1,2,0,1,2.

0,0,1,1,2,2 — where repeat would give 0,1,2,0,1,2. · torch.repeat_interleave docs

18. F.pad

F.pad — The pair (1, 1) pads the last dim only — the argument order that trips people up.

The pair (1, 1) pads the last dim only — the argument order that trips people up. · F.pad docs

19. F.one_hot

F.one_hot — Each label becomes a row with a single 1; rank goes up by one.

Each label becomes a row with a single 1; rank goes up by one. · F.one_hot docs

Joining and splitting

Where cat, stack, chunk and split actually differ.

20. torch.cat

torch.cat — Joined along dim 0: that axis grows from 2 to 4.

Joined along dim 0: that axis grows from 2 to 4. · torch.cat docs

21. torch.cat

torch.cat — The same two tensors along dim 1 instead. cat lengthens an axis, never adds one.

The same two tensors along dim 1 instead. cat lengthens an axis, never adds one. · torch.cat docs

22. torch.stack

torch.stack — The same picture as cat, deliberately — but a new axis, so rank goes 2 → 3.

The same picture as cat, deliberately — but a new axis, so rank goes 2 → 3. · torch.stack docs

23. torch.chunk

torch.chunk — One tensor becomes a tuple of views, each starting at a different offset.

One tensor becomes a tuple of views, each starting at a different offset. · torch.chunk docs

24. torch.split

torch.split — chunk takes a count; split takes the sizes, so the pieces can differ.

chunk takes a count; split takes the sizes, so the pieces can differ. · torch.split docs

Indexing

Choosing a subset. Some are views, some copy.

25. torch tensor views

torch tensor views — A contiguous slice is a view — it shifts the offset and shortens one axis.

A contiguous slice is a view — it shifts the offset and shortens one axis. · torch tensor views docs

26. Tensor.stride

Tensor.stride — Every other cell survives. Still a view — the stride on that axis doubles.

Every other cell survives. Still a view — the stride on that axis doubles. · Tensor.stride docs

27. torch.index_select

torch.index_select — Whole slices by position. Rows may repeat or reorder — what a slice cannot do.

Whole slices by position. Rows may repeat or reorder — what a slice cannot do. · torch.index_select docs

28. torch.gather

torch.gather — Positions chosen per row. Unlike a slice, the choice can differ for every row.

Positions chosen per row. Unlike a slice, the choice can differ for every row. · torch.gather docs

29. torch.topk

torch.topk — The k largest per row. The indices are what you feed back into gather.

The k largest per row. The indices are what you feed back into gather. · torch.topk docs

30. Tensor.masked_fill

Tensor.masked_fill — Shape survives, selected cells are overwritten. This is the attention mask, with -inf instead of 0.

Shape survives, selected cells are overwritten. This is the attention mask, with -inf instead of 0. · Tensor.masked_fill docs

31. torch.where

torch.where — An elementwise if/else — per cell, not per row.

An elementwise if/else — per cell, not per row. · torch.where docs

32. torch.tril

torch.tril — The causal mask in every decoder is this staircase.

The causal mask in every decoder is this staircase. · torch.tril docs

33. torch.triu

torch.triu — The complement of tril; diagonal=1 is the form that masks self-attention.

The complement of tril; diagonal=1 is the form that masks self-attention. · torch.triu docs

34. torch.diagonal

torch.diagonal — Cells where the two indices agree. A view with a clever stride, not a copy.

Cells where the two indices agree. A view with a clever stride, not a copy. · torch.diagonal docs

Reducing

An axis disappears.

35. torch.sum

torch.sum — A fibre along the reduced axis folds to one element; rank drops by one.

A fibre along the reduced axis folds to one element; rank drops by one. · torch.sum docs

36. torch.sum

torch.sum — dim takes a tuple, so two axes collapse at once and rank falls 3 → 1.

dim takes a tuple, so two axes collapse at once and rank falls 3 → 1. · torch.sum docs

37. torch.mean

torch.mean — The same collapse as sum, divided by the extent.

The same collapse as sum, divided by the extent. · torch.mean docs

38. torch.prod

torch.prod — Multiplies along the axis. Overflows far sooner than sum, hence log space.

Multiplies along the axis. Overflows far sooner than sum, hence log space. · torch.prod docs

39. torch.min

torch.min — The mirror of max. amin gives values alone when the positions are not needed.

The mirror of max. amin gives values alone when the positions are not needed. · torch.min docs

40. torch.max

torch.max — Returns two tensors — values and argmax positions — both with the axis removed.

Returns two tensors — values and argmax positions — both with the axis removed. · torch.max docs

41. torch.argmax

torch.argmax — The output holds positions, so it is integer and the axis is gone.

The output holds positions, so it is integer and the axis is gone. · torch.argmax docs

42. Tensor.norm

Tensor.norm — Each fibre collapses to its magnitude rather than its total.

Each fibre collapses to its magnitude rather than its total. · Tensor.norm docs

43. torch.std

torch.std — Spread of each fibre. Flat shading is the point — an arange has the same spread everywhere.

Spread of each fibre. Flat shading is the point — an arange has the same spread everywhere. · torch.std docs

44. torch.any

torch.any — A boolean reduction. all is the same collapse with the other rule.

A boolean reduction. all is the same collapse with the other rule. · torch.any docs

45. torch.logsumexp

torch.logsumexp — Sums in log space without leaving it — the safe core of softmax.

Sums in log space without leaving it — the safe core of softmax. · torch.logsumexp docs

Scans

A reduction that keeps every intermediate.

46. torch.cumsum

torch.cumsum — Shape untouched, values accumulate. The shade ramp is the running total.

Shape untouched, values accumulate. The shade ramp is the running total. · torch.cumsum docs

47. torch.cummax

torch.cummax — Running maximum: never decreases, so the shading only ever darkens.

Running maximum: never decreases, so the shading only ever darkens. · torch.cummax docs

Value-only

Shape survives untouched; only the numbers change, so the cells are shaded by value.

48. F.softmax

F.softmax — Shape untouched; only the numbers move. Shade is the value, so the exponential tilt is visible.

Shape untouched; only the numbers move. Shade is the value, so the exponential tilt is visible. · F.softmax docs

49. torch.clamp

torch.clamp — Values squeezed into a band. The flat shading at both ends is the clipping.

Values squeezed into a band. The flat shading at both ends is the clipping. · torch.clamp docs

50. F.normalize

F.normalize — Every row divided by its own length, so each ends on the unit sphere.

Every row divided by its own length, so each ends on the unit sphere. · F.normalize docs

On reading these

Two habits make the set more useful than any single figure.

Compare the pairs. cat and stack are drawn identically on purpose — the only difference is the shape line. So are expand and repeat, squeeze and unsqueeze, tril and triu. When two operations produce the same picture, the distinction lives entirely in the shape or the memory, and that is exactly the distinction people get wrong.

Watch for what does not move. contiguous draws an unchanged picture because it changes only the byte layout. squeeze cannot change the picture, because a size-1 axis occupies no space. In both cases the absence of a visible change is the lesson.

The figures were generated with matplotlib’s mplot3d, which has no z-buffer — every cube face on a page goes into a single Poly3DCollection so that occlusion comes out right, and interior faces are culled so the files stay small.


Posted

in

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

🧭