The PyTorch mental model¶
Level: Builder · Time: 35 minutes · Prerequisite: basic Python
PyTorch gives you multidimensional arrays, differentiable operations, parameter containers, and execution backends. A model is ordinary Python organizing tensor operations.
Five objects to recognize¶
import torch
from torch import nn
x = torch.randn(2, 4, 8) # Tensor: data and shape
projection = nn.Linear(8, 16) # Module: operation plus Parameters
y = projection(x) # Computation tracked by autograd
loss = y.square().mean() # Scalar objective
loss.backward() # Populate parameter gradients
Tensorholds values, dtype, shape, device, and optional gradient history.Parameteris a tensor registered as trainable state on a module.Modulecontains parameters, buffers, child modules, and aforwardcomputation.- Autograd records differentiable operations and applies reverse-mode differentiation.
- An optimizer reads
.gradand updates parameter values.
The PyTorch documentation is the source of truth for a particular release.
Parameters versus buffers versus activations¶
| Kind | Saved in state_dict? | Updated by optimizer? | Example |
|---|---|---|---|
| Parameter | Yes | Usually | projection weight |
| Persistent buffer | Yes | No | running statistic |
| Non-persistent buffer | No | No | regenerable mask |
| Activation | No | No | output of an attention layer |
Model checkpoints may store more than state_dict: optimizer tensors, schedule, scaler, and data progress are needed for faithful training resumption.
Training and evaluation modes¶
model.train() and model.eval() change the behavior of modules such as dropout and batch normalization. They do not enable or disable gradients. Use torch.no_grad() or torch.inference_mode() when gradients are unnecessary.
Dtypes and devices¶
The same computation can run with different numeric formats and backends. Float32 offers wide support; float16 and bfloat16 reduce memory and can accelerate supported hardware; lower-bit formats generally require specialized kernels and scaling strategies. Changing dtype can change stability and exact output.
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
token_ids = token_ids.to(device)
Never hard-code CUDA in an educational example that should run on a laptop.
A module with an explicit shape contract¶
class SwiGLU(nn.Module):
def __init__(self, d_model: int, d_hidden: int) -> None:
super().__init__()
self.gate_up = nn.Linear(d_model, 2 * d_hidden, bias=False)
self.down = nn.Linear(d_hidden, d_model, bias=False)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# x: [batch, time, d_model]
gate, value = self.gate_up(x).chunk(2, dim=-1)
hidden = torch.nn.functional.silu(gate) * value
return self.down(hidden) # [batch, time, d_model]
This is a gated feed-forward sublayer. A sparse MoE layer replaces one such shared transformation with several expert transformations plus a router; it does not replace attention.
Reproducibility is more than a seed¶
Setting a seed controls some random streams, but exact reproducibility may also depend on backend algorithms, device count, data-worker order, distributed reductions, library versions, and nondeterministic kernels.
Record environment and configuration alongside results. Do not promise bitwise reproduction when the runtime cannot provide it.
Read production code without drowning¶
- Find the configuration object and write down dimensions.
- Find the top-level model
forwardsignature. - Trace one block, ignoring optimizations at first.
- Mark reshapes, transposes, normalization axes, and residual additions.
- Separate mathematical equivalence from kernel fusion.
- Return to cache, quantization, parallelism, and compilation only after the eager path is clear.
The compact nanoGPT model implementation is a useful first production-adjacent trail. The companion code in this repository is even smaller and covered by shape tests.
Exercises¶
- Why does
model.eval()not reduce memory by itself? - What breaks if a residual branch returns
[B,T,4C]instead of[B,T,C]? - Why might a fused attention kernel look unlike the equation but compute the same function?