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.

Minimum: 80 characters and 15 words. Your writing stays only in this browser.

Waiting for your explanation.

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

01Reading Qwen3.8-27B's configuration gives vocab_size 248320. What does that number describe exactly?
Answer and explanation

The number of rows in the embedding matrix, which may be padded above the tokenizer's live vocabulary — vocab_size sizes the embedding and output matrices. Vocabularies are commonly padded to a convenient multiple, so the count of tokens the tokenizer can actually emit can be smaller; lesson 1.2 works through the consequence.

02Why is optimizer.zero_grad normally called before backward?
Answer and explanation

PyTorch accumulates gradients into the grad fields rather than replacing them — Accumulation is deliberate, since it enables techniques like gradient accumulation across micro-batches, but an ordinary independent step must clear the previous step's values first.

03Lesson 0.6 showed that a learning rate of 1.1 on L(w) = w squared makes the parameter diverge. If you saw that behavior in a real training loop, which of these would autograd have told you?
Answer and explanation

Nothing; autograd computes correct gradients for a diverging run exactly as it does for a converging one — Automatic differentiation reports sensitivity, not judgement. A loop that computes perfect gradients and takes ruinous steps is fully valid Python and fully valid autograd.

Complete the teach-back and answer the quiz correctly to finish this lesson.

◎ · Evidence marker

Sources

  1. Charles R. Harris et al. (2020). Array programming with NumPy.
  2. Adam Paszke et al. (2019). PyTorch: An Imperative Style, High-Performance Deep Learning Library.
  3. Hugging Face (2026). Hugging Face Transformers documentation.
  4. Qwen Team (2026). Qwen3.8-27B Model Card.