Three operations that change what data looks like
None of these operations move bytes around (unless they have to). They change the strides — the recipe PyTorch uses to walk through memory. Understanding stride is the difference between knowing tensors and using tensors.
The four operations
reshape(*shape)— most flexible. Returns a view if memory permits, otherwise copies. Always works.view(*shape)— view-only. Errors if the tensor isn't contiguous.transpose(d0, d1)/.T— swap two dims. Always returns a view, but the result is non-contiguous.permute(*dims)— reorder all dims. View, non-contiguous.
The contiguous trap is the most common subtle PyTorch bug. After a transpose or permute, the tensor is laid out non-contiguously in memory. Some operations (notably .view() and many older custom CUDA kernels) require contiguous memory. The fix is either .contiguous() (which copies into a fresh contiguous block) or just using .reshape() instead of .view().
Adding and removing size-1 dims
unsqueeze(dim) adds a size-1 dim at dim. squeeze(dim=None) removes size-1 dims. Both are pure metadata changes — no copy. They're constantly needed when matching shapes for broadcasting (a row vector vs. column vector situation).