Background
Sections
IntroductionModule 01 β€” Tensors 🧊01 Β· Creating Tensors 🧊02 Β· Indexing & Reshaping πŸ”ͺ03 Β· Tensor Math βž—04 Β· Device Placement πŸ–₯️⚑Module 02 β€” Autograd βš™οΈ01 Β· The Computational Graph πŸ•ΈοΈ02 Β· Backward Pass & Gradients ⬅️03 Β· Turning Autograd Off πŸ›‘04 Β· Gradient Gotchas πŸͺ€Module 03 β€” Neural Networks01 Β· The `nn.Module` Basics 🧱02 Β· Common Layers 🧩03 Β· Building a Network πŸ—οΈ04 Β· Inspecting Models πŸ”Module 04 β€” Data Handling πŸ—‚οΈ01 Β· Dataset Basics πŸ“‡02 Β· The DataLoader 🚚03 Β· Transforms 🎨04 Β· Splits & Built-in Datasets βœ‚οΈModule 05 β€” The Training Loop πŸ”01 Β· Loss Functions 🎯02 Β· Optimizers βš™οΈ03 Β· The Training Loop πŸ”04 Β· Evaluation & Metrics πŸ“ŠModule 06 β€” Saving & Loading πŸ’Ύ01 Β· `state_dict` Basics πŸ’Ύ02 Β· Checkpoints & Resuming ⏸️03 Β· Loading for Inference πŸš€04 Β· Best Model & Early Stopping πŸ…Module 07 β€” Computer Vision πŸ‘οΈ01 Β· Convolutions πŸ”²02 Β· CNN Architecture πŸ›οΈ03 Β· Transfer Learning πŸ”04 Β· Image Classification Project πŸ§ͺModule 08 β€” NLP & Transformers πŸ’¬01 Β· Text Data & Tokenization πŸ”€02 Β· Embeddings 🧭03 Β· Recurrent Layers & LSTMs πŸ”„04 Β· Intro to Transformers ⚑Module 09 β€” Ecosystem: PyTorch Lightning ⚑01 Β· Why Lightning? πŸ€”02 Β· The LightningModule 🧩03 Β· The Trainer πŸŽ›οΈ04 Β· DataModules & Callbacks 🧰Module 10 β€” Deployment & Optimization πŸš€01 Β· Exporting Models πŸ“¦02 Β· `torch.compile` ⚑03 Β· Inference Optimization πŸͺΆ04 Β· Serving & Next Steps πŸŽ“

02 Β· Indexing & Reshaping πŸ”ͺ

4 min read

Rarely do you use a whole tensor at once. You grab rows, columns, sub-blocks β€” and constantly change a tensor's shape to fit the next operation.


1. The 'Why'

Real data almost never arrives in the exact shape your next operation needs. A batch of images might come in as [batch, height, width, channels] but a convolution layer wants [batch, channels, height, width]. A flat vector of 784 numbers needs to become a 28Γ—28 image to visualize it. You'll spend a surprising amount of your PyTorch life rearranging tensors β€” pulling out slices, flattening, and reshaping β€” so fluency here pays off constantly.

The subtle part, and the reason this deserves its own sub-module, is memory. Many reshaping and slicing operations don't copy your data β€” they hand you a view that shares the same underlying memory as the original tensor. That's wonderfully efficient, but it means mutating the view mutates the original, which produces some of the most baffling "the value changed on its own" bugs beginners hit. Understanding when you have a view versus a copy is what separates confident PyTorch users from frustrated ones.


2. Core Concepts

Indexing and slicing work just like NumPy: t[row, col], with : selecting an entire axis and start:stop selecting a range. Negative indices count from the end.

Reshaping changes how the same elements are laid out into dimensions. reshape(rows, cols) requires the total element count to stay the same (a 12-element tensor can become 3Γ—4 but not 3Γ—5). Passing -1 for one dimension asks PyTorch to infer it.

Views vs. copies. .view() always returns a view sharing memory (and requires the tensor to be contiguous). .reshape() returns a view when it can and silently copies when it can't. Slicing typically returns a view too. When you need a guaranteed-independent tensor, call .clone().


3. Code in Action

Indexing and slicing

import torch

grid = torch.arange(12).reshape(3, 4)   # 3x4 matrix of values 0..11
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])

print(grid[0, 0])        # tensor(0)  -> top-left element [row 0, col 0]
print(grid[1])           # tensor([4, 5, 6, 7]) -> the entire second row
print(grid[:, 2])        # tensor([ 2,  6, 10]) -> the third column (all rows)
print(grid[0:2, 1:3])    # a 2x2 sub-block: rows 0-1, columns 1-2
print(grid[-1])          # tensor([ 8,  9, 10, 11]) -> the last row

Reshaping and flattening

import torch

x = torch.arange(12)     # 1-D tensor: [0, 1, ..., 11]

grid = x.reshape(3, 4)   # give the same 12 elements a 3x4 shape
flat = grid.reshape(-1)  # flatten back to 1-D; -1 lets PyTorch compute the 12

# unsqueeze/squeeze add or remove size-1 dimensions (common for batching)
row = torch.tensor([1, 2, 3])   # shape (3,)
batched = row.unsqueeze(0)      # shape (1, 3) -> add a batch dimension at front
print(batched.shape)            # torch.Size([1, 3])
print(batched.squeeze().shape)  # torch.Size([3]) -> remove size-1 dims

Views vs. copies (the gotcha)

import torch

x = torch.arange(6)      # [0, 1, 2, 3, 4, 5]

view = x.reshape(2, 3)   # usually a VIEW sharing memory with x
view[0, 0] = 99          # mutate the view...
print(x[0])              # tensor(99) -> ...and x changed too!

safe = x.reshape(2, 3).clone()  # clone() forces an independent copy
safe[0, 0] = -1                 # mutate the copy...
print(x[0])                     # tensor(99) -> x is untouched this time

4. Common Pitfalls

Pitfall 1 β€” Accidentally sharing memory via views. Because reshape, .view(), and slicing often return a view, mutating one tensor silently changes another. If you need independence, .clone(). Conversely, don't clone reflexively β€” needless copies waste memory.

Pitfall 2 β€” .view() on a non-contiguous tensor. After operations like .T (transpose), a tensor may be non-contiguous, and .view() will raise an error. Use .reshape() (which handles it) or call .contiguous() first.

Pitfall 3 β€” Reshape element-count mismatch. reshape(3, 5) on a 12-element tensor fails because 3Γ—5 β‰  12. When juggling shapes, print .shape liberally, and lean on -1 to let PyTorch fill in one dimension for you.


5. Further Reading & Watch List


⬅️ Prev: 01 Β· Creating Tensors Β· ➑️ Next: 03 Β· Tensor Math