Foundations
Python, NumPy, and PyTorch in 20 minutes
Arrays, tensors, shapes, and autograd in practice, ending with the four lines that read Qwen3.8-27B's own configuration and produce the numbers this course reuses everywhere.
Updated
01 · Concept
Concept
Four lessons of this track have leaned on three numbers: hidden size 5120, 64 layers, and an output width of 248,320. You should not take them on trust from a course, and you certainly should not take them from a blog post. Every published model ships a configuration file stating them, and reading it yourself takes about ten seconds. That is where this lesson ends. It starts with just enough Python tooling to make the reading, and everything after it, comprehensible.
Python is the coordination layer of machine-learning work. You write ordinary variables, functions, loops, and imports, while numerical libraries perform the heavy arithmetic in compiled kernels. The habit that matters most is making data flow explicit: name your shapes, avoid hidden global state, check assumptions at boundaries, and keep an experiment reproducible while it is still small enough to reason about.
NumPy’s central object is the multidimensional array. Two operations that look similar are entirely different: x * w multiplies elementwise, while x @ w performs the matrix product from lesson 0.5. Confusing them produces a result of the wrong shape if you are lucky and a plausible result of the right shape if you are not. Inspect .shape and .dtype constantly. Broadcasting lets a smaller array act across a larger one under precise compatibility rules, so adding a vector of shape features to an array of shape batch-by-features adds the same bias to every row. Broadcasting is convenient and is also an excellent way to silently average over an axis you meant to keep.
PyTorch tensors offer the same array operations plus two additions: devices, and automatic differentiation. Tensors in one operation generally need compatible dtypes and the same device, and moving a model to a GPU does not drag every related object along with it. Autograd records operations on tensors that require gradients, so that when you call backward on a scalar loss it can apply the chain rule from lesson 0.6 backwards through the recorded graph. The loop has a fixed rhythm: clear accumulated gradients, run the forward pass, reduce to a scalar loss, call backward, then call the optimizer’s step. Keeping those responsibilities separate is what makes a broken loop debuggable — backward computes gradients and never changes parameters, step changes parameters and never computes gradients.
Larger models subclass torch.nn.Module or compose existing modules; registered parameters then appear in model.parameters(). Note one trap: model.train() and model.eval() switch the behavior of modules such as dropout, but they do not enable or disable gradient tracking. Evaluation without derivatives needs torch.no_grad. Save a state_dict plus enough configuration to rebuild the model, rather than serializing an opaque object graph that a library upgrade will refuse to load.
Which brings us to configuration, and to the whole point of this lesson. A published model is a pair: weights, and a description of the shape those weights are supposed to have. Hugging Face’s transformers library reads the second without downloading the first, which is why this is cheap enough to do as a reflex:
from transformers import AutoConfig
config = AutoConfig.from_pretrained("Qwen/Qwen3.8-27B")
text = getattr(config, "text_config", config)
print(text.hidden_size) # 5120
print(text.num_hidden_layers) # 64
print(text.vocab_size) # 248320
Three lines of output and you hold the spine of this course. The 5120 is the width of one token’s state, from lesson 0.4. The 64 is how many times that state gets rewritten. The 248,320 is how many scores the final layer produces per position, from lesson 0.1. The getattr in the third line is not decoration: Qwen3.8-27B is multimodal, so its configuration carries a nested vision configuration alongside the text one, and different multimodal models place the text fields at different depths. Writing the fallback explicitly means the code states which sub-configuration it read, rather than picking up a vision tower’s hidden size and reporting it as the model’s.
Reproducibility needs more than a random seed. Record library versions, data splits, preprocessing, hyperparameters, device, precision, and evaluation procedure, because any of them can move a result more than the change you are testing. Some accelerator kernels remain nondeterministic by design. Always keep a simple baseline in the comparison, inspect individual examples rather than only aggregates, and assert that shapes are what you expect and that losses are finite. A training loop that runs green to completion can optimize entirely the wrong objective with perfect fidelity.
The portable skill here is not memorizing an API surface that changes every year. It is reading a tensor program as a sequence of shape transformations with explicit state: what does each axis mean, which values require gradients, where does randomness enter, which reduction produces the loss, and when do parameters change? Add to that the reflex of loading a model’s real configuration instead of quoting a number from memory, and the rest of this course is a matter of following shapes.
02 · Analogy
Analogy
Think of Python as the workshop, NumPy as a rack of precise measuring and cutting tools, and PyTorch as the same workshop with a camera recording every differentiable operation you perform. When you finally announce a scalar loss, autograd rewinds the tape and reports how each adjustable knob contributed. The camera is faithful and completely uncritical: it will differentiate a beautifully executed measurement of the wrong plank just as happily as the right one.
03 · Teach it back
Teach it back
Describe a minimal PyTorch training step and the role of shapes, autograd, zero_grad, backward, and step, then explain how you would obtain a model's real architectural constants rather than trusting a blog post.
Compare with a model answer
Inputs and targets are tensors with deliberate shapes. The forward pass computes predictions and reduces them to a scalar loss while autograd records each differentiable operation. optimizer.zero_grad clears gradients left from the previous step, because PyTorch accumulates rather than overwrites them. loss.backward walks the recorded graph and fills each parameter's grad field. optimizer.step consumes those gradients to update parameters; it does not compute them. For architectural constants, load the model's own configuration with AutoConfig.from_pretrained and read hidden_size, num_hidden_layers, and vocab_size directly. For Qwen3.8-27B those are 5120, 64, and 248320, and every capacity and cost calculation in this course starts from numbers obtained that way rather than from memory.
04 · Check your understanding
Check your understanding
Complete the teach-back and answer the quiz correctly to finish this lesson.
◎ · Evidence marker
Sources
- Charles R. Harris et al. (2020). Array programming with NumPy.
- Adam Paszke et al. (2019). PyTorch: An Imperative Style, High-Performance Deep Learning Library.
- Hugging Face (2026). Hugging Face Transformers documentation.
- Qwen Team (2026). Qwen3.8-27B Model Card.