Every module shows you real, copyable code alongside simulated animations that make it click. You can copy any code block and run it yourself — in Google Colab (free, in your browser) or on your own machine.
The animations here are illustrations, not a live Python engine — real PyTorch can't run inside a web page. When you want to actually execute something, copy the code and run it in Colab or locally. That's how you'll learn it for real.
Three things, and that's it:
Plain Python is too slow for the millions of number-crunching operations training needs. PyTorch gives you (1) tensors — fast multi-dimensional arrays, (2) autograd — automatic backpropagation (Module 3 of fundamentals, done for you!), and (3) GPU support — one line to make it all run on a graphics card. It's the toolbox that turns "I understand backprop" into "I can train a model."
You have three options. For starting out, Google Colab is the clear winner — zero install, a free GPU, works on any laptop (even a Chromebook). Here's the honest comparison:
Colab = a free shared workshop you walk into and use (but they close it at night). Local = your own garage — always there, but you buy and maintain the tools. Cloud GPU = renting an industrial factory by the hour. Start in the free shared workshop; build your garage later.
Go to colab.research.google.com → New Notebook. PyTorch is already installed. Just verify it — paste this into a cell and press ▶ (Shift+Enter):
import torch
print("PyTorch version:", torch.__version__)
print("GPU available:", torch.cuda.is_available())To turn on the free GPU in Colab: menu → Runtime → Change runtime type → Hardware accelerator → GPU → Save. Then re-run — GPU available: becomes True.
First make an isolated environment (so PyTorch doesn't clash with other projects), then install:
# 1. create & activate a virtual environment
python -m venv ai-env
# Windows:
ai-env\Scripts\activate
# Mac/Linux:
source ai-env/bin/activate
# 2. install PyTorch (CPU version — works everywhere)
pip install torch
# 3. verify
python -c "import torch; print(torch.__version__)"pip install torch gives the CPU version — perfect for Modules 1–5 here. To use an NVIDIA GPU locally, you need the CUDA build — get the exact command for your system from the official selector at pytorch.org/get-started/locally. Don't guess the CUDA version; use their picker.
This simulates what you'll see when you run the checks. Step through it to know exactly what a healthy setup looks like — then run the real code above yourself.
A tensor is just a container of numbers with a shape. You already met them without the name: in fundamentals, a flattened image was a vector (Module 1), and a word embedding was a vector (Module 4). A tensor generalizes that to any number of dimensions.
The ladder of shapes:
import torch
# 0-D: a scalar (single number)
a = torch.tensor(7.0) # shape: ()
# 1-D: a vector (like an embedding)
b = torch.tensor([1.0, 2.0, 3.0]) # shape: (3,)
# 2-D: a matrix (like a batch of vectors, or a grayscale image)
c = torch.tensor([[1, 2, 3],
[4, 5, 6]]) # shape: (2, 3)
# 3-D: e.g. a color image (height x width x RGB)
d = torch.randn(4, 4, 3) # shape: (4, 4, 3)
print(b.shape) # torch.Size([3])
print(c.shape) # torch.Size([2, 3])A tensor's shape is the list of its dimensions. (2, 3) means 2 rows × 3 columns. 99% of PyTorch bugs are shape mismatches — "expected (32, 128) but got (128, 32)". Get comfortable reading .shape now and you'll save yourself hours later.
Click each dimension to see the tensor and its shape. The real code that builds it is shown too — copy and try it.
import torch
t = torch.tensor(7.0) # scalar, shape ()Knowing what a tensor is isn't enough — you need to manipulate them. These four operations show up in almost every model you'll ever write. They're also where beginners hit their first real wall, so it's worth getting comfortable now.
Math on tensors happens on every element at once (no loops). And @ is matrix multiply — the single most important operation in neural networks (every layer is basically x @ W).
a = torch.tensor([1., 2., 3.])
b = torch.tensor([10., 20., 30.])
a + b # tensor([11., 22., 33.]) — element-wise, no loop
a * 2 # tensor([2., 4., 6.]) — scalar applies to all
M = torch.randn(2, 3)
x = torch.randn(3, 4)
M @ x # shape (2, 4) — matrix multiply (this is a neural net layer!)Grab parts of a tensor just like Python lists — but across multiple dimensions.
t = torch.tensor([[1,2,3],
[4,5,6]]) # shape (2, 3)
t[0] # tensor([1, 2, 3]) — first row
t[:, 1] # tensor([2, 5]) — second column (: means "all rows")
t[0, 2] # tensor(3) — row 0, col 2.view() / .reshape()Rearrange the same numbers into a different shape — used constantly to make tensors fit the next layer. The total number of elements must stay the same.
t = torch.arange(6) # tensor([0,1,2,3,4,5]), shape (6,)
t.view(2, 3) # [[0,1,2],[3,4,5]] — same data, shape (2, 3)
t.view(3, 2) # [[0,1],[2,3],[4,5]] — shape (3, 2)
t.view(-1, 2) # -1 means "figure it out" → shape (3, 2)PyTorch auto-stretches smaller tensors to match bigger ones so shapes line up — no manual copying. It feels like magic until it bites you, so know it exists.
M = torch.ones(3, 3) # shape (3, 3)
row = torch.tensor([1., 2., 3.]) # shape (3,)
M + row # row is auto-"broadcast" to every row of M
# [[2,3,4],[2,3,4],[2,3,4]] — no loop neededMost PyTorch errors are shape mismatches — e.g. trying @ on tensors whose inner dimensions don't match. When something breaks, your first move is always print(x.shape) on every tensor involved. Reshaping and broadcasting exist precisely to make shapes line up. Master these four moves and you can read (and debug) almost any model code.
A CPU does a few things very fast, one after another. A GPU does thousands of things at once — and training is thousands of identical number-crunches, so it's a perfect fit. The magic is that switching is one line:
# pick GPU if available, else CPU — the standard pattern
device = "cuda" if torch.cuda.is_available() else "cpu"
x = torch.randn(1000, 1000)
x = x.to(device) # move the tensor to the GPU (or CPU)
print("running on:", device)A CPU is one brilliant chef making dishes one at a time. A GPU is 1000 line cooks each doing one simple step simultaneously. For a single fancy dish, the chef wins. For "chop 1000 onions" (i.e. training), the 1000 cooks demolish it. Training is millions of tiny identical operations — that's why GPUs win.
Watch the difference on a big matrix multiply (simulated):
Exact speedups depend on the task and hardware — small jobs may even be slower on GPU (moving data over has a cost). GPUs win big on large batches, which is exactly what training does. Don't worry about this yet; Modules 1–5 run fine on CPU.
torch.cuda.is_available() tell you?torch.tensor([[1,2,3],[4,5,6]])?x.to("cuda"). Verify any setup with torch.__version__ and torch.cuda.is_available().Next up → Module 2: Autograd — the engine that makes training possible. You'll build the "turning the knobs" from fundamentals Module 3 as real code: PyTorch computing gradients automatically so a network can learn.