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 πŸŽ“

03 Β· Tensor Math βž—

4 min read

Neural networks are, under the hood, a long chain of matrix multiplications and element-wise operations. This is where the "math" in "the math" lives.


1. The 'Why'

Strip away the jargon and a neural network is mostly two operations repeated over and over: matrix multiplication (combining inputs with learned weights) and element-wise functions (activations like ReLU applied to each number independently). If you're fuzzy on the difference between these, your models will produce nonsense and you won't know why β€” because both operations often run without error while giving completely different results. That silent failure mode is exactly why tensor math deserves careful attention.

Beyond the two headline operations, you'll constantly reduce tensors β€” summing a loss across a batch, averaging accuracy, finding the maximum-scoring class. And you'll rely on broadcasting, PyTorch's rule for automatically stretching mismatched shapes so you can, say, add a bias vector to every row of a matrix without writing a loop. Together these tools are the arithmetic backbone of everything you'll build, so it's worth building real intuition for them now rather than debugging them later.


2. Core Concepts

Element-wise operations (+, -, *, /) act position-by-position. A * B multiplies each element of A by the element in the same spot of B. Shapes must match or be broadcastable.

Matrix multiplication uses @ or torch.matmul. This is the linear-algebra product where inner dimensions must agree: a (2Γ—3) can multiply a (3Γ—4) to give a (2Γ—4). This is not the same as *.

Reductions collapse dimensions: .sum(), .mean(), .max(), .argmax(). Add a dim= argument to reduce along a specific axis β€” dim=0 collapses rows (operating down columns), dim=1 collapses columns (operating across rows).

Broadcasting lets operations combine tensors of different shapes by virtually expanding size-1 (or missing) dimensions. Adding a shape-(3,) bias to a shape-(2, 3) matrix adds it to both rows.


3. Code in Action

Element-wise vs. matrix multiplication

import torch

A = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0]])       # 2x2 matrix
B = torch.tensor([[5.0, 6.0],
                  [7.0, 8.0]])       # 2x2 matrix

print(A * B)             # ELEMENT-WISE product: multiplies matching positions
print(A @ B)             # MATRIX product: rows-times-columns (linear algebra)
print(torch.matmul(A, B))# identical to A @ B

# Inner dimensions must match for matmul:
X = torch.rand(2, 3)     # shape (2, 3)
Y = torch.rand(3, 4)     # shape (3, 4)
print((X @ Y).shape)     # torch.Size([2, 4]) -> inner 3s cancel

Reductions

import torch

A = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0]])

print(A.sum())           # tensor(10.) -> sum of ALL elements
print(A.mean())          # tensor(2.5) -> mean of all elements
print(A.sum(dim=0))      # tensor([4., 6.]) -> sum down each COLUMN
print(A.sum(dim=1))      # tensor([3., 7.]) -> sum across each ROW
print(A.argmax())        # tensor(3) -> flat index of the largest value (the 4.0)

Broadcasting and transpose

import torch

matrix = torch.ones(2, 3)          # shape (2, 3), all ones
bias   = torch.tensor([1., 2., 3.])# shape (3,)

# bias is broadcast across BOTH rows of matrix β€” no loop needed
print(matrix + bias)     # each row becomes [2., 3., 4.]

# Transpose swaps two dimensions (rows <-> columns)
print(matrix.T.shape)    # torch.Size([3, 2])

4. Common Pitfalls

Pitfall 1 β€” Confusing * with @. A * B is element-wise; A @ B is matrix multiplication. Both may run silently, so a wrong operator gives a plausible-but-wrong result. When your loss looks bizarre, check this first.

Pitfall 2 β€” Matrix-multiply dimension mismatch. matmul needs inner dimensions to agree: (2Γ—3) @ (3Γ—4) works, (2Γ—3) @ (4Γ—3) doesn't. Print .shape on both operands, and remember you can .T one of them to line them up.

Pitfall 3 β€” The wrong reduction dim. dim=0 and dim=1 reduce different axes, and mixing them up quietly corrupts metrics like per-sample accuracy. Say it in words first: "I want one number per row" β†’ reduce across columns β†’ dim=1.


5. Further Reading & Watch List


⬅️ Prev: 02 Β· Indexing & Reshaping Β· ➑️ Next: 04 Β· Device Placement