Intuition The One Core Idea
A Python program is just named boxes that hold values , plus rules that decide which lines run and how often . Once you can read a name, know what type of thing lives in its box, and follow the indentation that groups lines together, every ML script in the parent note becomes plain reading — no magic left.
Before you can read a single training loop, you must be able to read the marks on the page . This note walks every symbol, keyword, and idea the parent note leans on, starting from a blank line and adding exactly one new thing at a time.
Definition A note on the arrow
→ used below
When these notes write int(3.7) → 3, the arrow → is not Python code . It is our shorthand for "evaluates to" or "gives" — read it as "int(3.7) gives 3" . You will never type → in a program; it only appears here to point from an expression to its result.
Definition Comments — text Python ignores
Anything after a # on a line is a comment : a human note that Python skips entirely when running. lr = 0.01 # learning rate runs exactly as lr = 0.01 — the # learning rate is there only for the reader. You will see comments everywhere in real code; treat them as sticky notes in the margin, never as instructions the computer obeys.
Imagine a shelf of labelled boxes. Each box holds one value. A variable is nothing more than a label stuck to a box .
Figure 1 — A single value 5 sits in a box; the amber tag x is a label tied to that box. The white arrow shows x = 5 attaching the label. This is the picture behind every variable.
Definition Variable and the
= sign
x = 5 reads "stick the label x onto the box that holds 5" . The = here is NOT the "equals" of maths — it does not ask is x equal to 5? . It commands : "make the label x point at this value". We read it right-to-left: first the value 5 exists, then the label x is attached.
Why does the topic need this? Every hyperparameter (learning_rate = 0.01), every dataset, every model lives in a labelled box. If you misread = as a question, nothing else makes sense.
= versus ==
One equals assigns (moves a label). Two equals asks ("are these the same value?", answers True or False). We define the comparison symbols like ==, >, and < fully in Section 4 — for now just remember = alone means assign .
A box does not just hold "a value" — it holds a value of some kind . The kind is the type . Python looks at the type to decide what operations are legal.
Figure 2 — Eight labelled boxes, one per type the parent uses: int holds 5, float holds 0.01, str holds "Epoch", bool holds True, list holds [0.5, 0.3], tuple holds the frozen (28, 28, 1), dict holds the key→value pair {"lr": 0.01}, and None marks an explicitly empty box. Use it as a visual glossary of the kinds of value.
Definition The types the parent uses
int — a whole number, no fractional part: 5, 100, -3. Picture: a box with a counted number of dots.
float — a number with a decimal point : 0.01, 31.25. Picture: a point sliding along a ruler.
str — a string of characters (text), written inside quotes: "Epoch". Picture: beads on a wire, one bead per character.
bool — a truth value , only two possible: True or False. Picture: a light switch.
list — an ordered, changeable row of boxes: [0.5, 0.3, 0.8]. Picture: a train of carriages you can rearrange.
tuple — an ordered row that is frozen (unchangeable): (28, 28, 1). Picture: a sealed train.
dict — a set of label → value pairs: {"lr": 0.01}. Picture: a real dictionary; look up a key , get its value .
None — the "nothing here yet" value, a single special object. Picture: an empty box explicitly marked "EMPTY".
Now that we have numbers, we need the symbols that combine them.
Definition Arithmetic operators on
int and float
These take two numbers and produce a new number:
+ add : 2 + 3 gives 5.
- subtract : 5 - 2 gives 3.
* multiply : 4 * 3 gives 12.
/ true division : 10 / 4 gives 2.5. Note: / always produces a float , even when it divides evenly — 10 / 2 gives 5.0, not 5.
// floor division : 10 // 4 gives 2. It divides then rounds down to the nearest whole number , keeping the whole part. On two ints it gives an int.
% modulo / remainder: 10 % 3 is 1 — what is left over after dividing 10 by 3 as far as whole numbers allow. It is an arithmetic operator (it produces a number), and the parent's x % 2 == 0 uses it to ask "is x even?" — an even number leaves remainder 0.
** power : x ** 2 means x 2 , i.e. x * x (one self-multiplication). Likewise x ** 3 is x * x * x.
The key distinction: / keeps the fraction (and turns the answer into a float); // discards the fraction.
// and int(...) disagree on negative numbers
Both remove the fraction, but in different directions :
// floors — rounds down toward negative infinity : -3 // 2 gives -2 (because − 1.5 rounds down to − 2 ).
int(...) truncates — chops toward zero: int(-1.5) gives -1, and int(-3.7) gives -3.
For positive numbers they agree (7 // 2 and int(3.5) both give 3), so the surprise only appears with negatives. Remember: // heads downhill , int(...) heads toward zero .
int and float promotes to float
Every arithmetic operator behaves the same way: if either side is a float, the result is a float.
5 + 2.0 → 7.0
5 - 2.0 → 3.0
5 * 2.0 → 10.0
5 ** 2.0 → 25.0
/ is even stronger: it produces a float even when both sides are int (10 / 2 → 5.0).
Picture int and float as two rulers; the moment a fractional ruler enters, Python measures the whole answer on the finer float ruler.
Intuition Why the difference between
/ and // matters
The parent writes dataset_size / batch_size which gives 31.25, but a training step must be a whole thing — you cannot run 0.25 of a batch. That is why // (floor division) is used to get 31. The operator you pick decides the type of the result , and the type is the whole point.
Common mistake Text and numbers do not mix by themselves
"Epoch " + 5 fails . The + between two strings means glue them , but between a string and a number Python refuses to guess your intent. You must first turn the number into text with str(5), giving "Epoch 5". Python is strongly typed : it never silently converts across incompatible kinds.
int(...), float(...), str(...) are converter machines : feed a value in, get the same value re-expressed as a new type. (Recall from the top of this note that → below just means "gives" .)
Why the topic needs this: in ML you constantly move between counts (int) and measured quantities (float), and between numbers and log messages (str). Reading conversions correctly prevents silent precision loss — accidentally turning float weights into ints would flatten a model.
Control flow needs a question to answer. Comparison operators take two values and return a bool.
Definition Comparison symbols
> greater than : 5 > 3 gives True. < less than : 3 < 5 gives True.
>= / <= "greater/less than or equal to ".
== equal? (asks if two values match), != not equal?
Each of these hands back a True/False for an if to act on — that is exactly what the parent's if accuracy > 0.95 and if epoch < 30 rely on. (The % symbol is not here: it is arithmetic, defined back in Section 2. We only combine it with a comparison, as in x % 2 == 0.)
Definition Truthiness — when a non-
bool acts like yes/no
Python lets any value stand in for a switch. Falsy things (act like False): 0, 0.0, "" (empty text), [] (empty list), {} (empty dict), None. Truthy : everything else. That is why if predictions: means "if the list has anything in it" .
Most languages wrap a block of lines in braces { }. Python instead uses the amount of space at the start of each line . Lines pushed in by the same amount belong to the same group.
Figure 3 — The two cyan lines are indented under if accuracy > 0.95:, so the amber bracket marks them as inside the if-block; the final white line returns to the left margin and is therefore outside. Read code by its left edge.
Definition Indentation as a block marker
A line ending in : (a colon) opens a block. Every line indented under it (usually 4 spaces) is inside that block. The first line that returns to the earlier margin is outside again.
Common mistake Indentation is enforced, not decoration
Mixing indentation levels raises an IndentationError. In the parent's example, an extra-indented save_checkpoint() breaks because Python cannot tell which block it belongs to. Read the left edge of the code like the outline of a document.
Now the pieces combine. if takes a question (a bool), and runs its block only when the answer is True .
Worked example Reading the step-decay schedule
With epoch = 50: epoch < 30? No. epoch < 60? Yes → run lr = initial_lr * 0.1 → lr becomes 0.01. Everything after is skipped. This is exactly why the answer is 0.01 and not 0.001.
for over an iterable
An iterable is anything you can walk through one item at a time — a list, a tuple, a string, or a range. for item in iterable: grabs each item in turn and runs the indented block with it.
range and its short forms
range(start, stop, step) is a machine that produces numbers on demand : range(0, 100, 32) yields 0, 32, 64, 96 — it starts at 0, jumps by 32, and stops before 100.
range also has shorter forms where Python fills in defaults:
range(10) — one argument is read as stop ; start defaults to 0 and step to 1, so it yields 0, 1, 2, …, 9 (stops before 10).
range(2, 10) — two arguments are start, stop ; step defaults to 1, so it yields 2, 3, …, 9.
The full three-argument form gives start, stop, step as above. This is exactly how the parent's batch-start indices are built.
while condition:
Repeats its block as long as the question stays True . Use it when you do not know the number of repetitions in advance (e.g. "train until converged").
Definition List comprehension
[x ** 2 for x in range(10)] is a compact for loop that builds a list . Because range(10) uses the one-argument form, x walks through 0, 1, …, 9. Read it left-to-right as: "the value x**2, for each x coming from range(10)" . Add a filter: [x**2 for x in range(10) if x % 2 == 0] keeps only even x.
The subtlest idea the parent relies on: assignment copies the label , not the contents .
weights_backup = weights_original shares the box
Both names become labels on the same list. Change the list through one label and the other "sees" the change too, because there is only one box. To get a truly separate copy you must ask for one: copy.deepcopy(...). Picture two name-tags tied to a single suitcase.
Intuition How to read the diagram below
The block below is a Mermaid diagram : each box is one idea from this note, and an arrow A → B means "understand A before B" . If your reader renders Mermaid you will see boxes and arrows; if it does not , you will see the raw text lines instead — read each A --> B line as the same "A comes before B" sentence. Either way the message is identical: everything flows into the parent topic at the bottom.
data types int float str bool
type conversion int float str
comparison and truthiness
arithmetic operators plus minus star slash
reference semantics one label
Python syntax data types control flow
Everything above flows into the parent topic and, from there, into NumPy arrays and vectorization , Pandas DataFrames , Python functions and lambda , and Python iterators and generators .
Read these to yourself as questions and check you can answer before moving on.
What does x = 5 command Python to do, read right-to-left? Create the value 5, then stick the label x onto that box; = assigns, it does not ask a question.
Difference between = and ==? = assigns a value to a name; == compares two values and returns True or False.
What does the computer do with everything after a #? Nothing — it is a comment, ignored when running; it is only a note for the human reader.
Difference between / and // for 10 / 4 and 10 // 4? / gives 2.5 (a float, keeps the fraction); // gives 2 (rounds the answer down to a whole number).
What is the result and type of int(3.7) and int(-3.7)? 3 and -3, both int — it truncates toward zero , it does not round or floor.
Why do -3 // 2 and int(-1.5) disagree? // floors (down to -2); int(...) truncates toward zero (up to -1). They only differ for negatives.
Does 5 * 2.0 give an int or a float, and why? A float, 10.0 — mixing any int with a float promotes the whole result to float.
What does x ** 3 mean in plain multiplication? x * x * x — the exponent counts how many copies of x are multiplied together.
Why does "Epoch " + 5 fail, and what fixes it? Python won't glue text and a number; wrap the number: "Epoch " + str(5).
What does 10 % 3 give and what kind of operator is %? 1; % is an arithmetic operator (remainder after division), so x % 2 == 0 tests "is x even?".
Which values are falsy? 0, 0.0, "", [], {}, and None — empty/zero things.
How does Python know which lines form a block? By indentation — lines pushed in by the same amount after a : belong together.
In if / elif / else, which block runs? The first one whose condition is True; the rest are skipped. else runs only if none were true.
What does range(10) produce, and why? 0, 1, …, 9 — one argument is read as stop, with start=0 and step=1 filled in by default.
After b = a for a list a, why does changing b change a? Both names label the same list (one box); use copy.deepcopy for a separate copy.