Day 1 · Prerequisites: Python & Math Essentials — Part 1: Python Essentials
Suggested pace: 1–1.5 hrs read · 2–3 hrs type and solve · rest to review your mistakes Goal of today Build the minimum Python and math you need for the rest of the course. You do not need advanced calculus. Learn the min
Suggested pace: 1–1.5 hrs read · 2–3 hrs type and solve · rest to review your mistakes
Goal of today Build the minimum Python and math you need for the rest of the course. You do not need advanced calculus. Learn the minimum intuition first, then deepen the mathematics when the course reaches gradient descent and backpropagation.
*How every topic is taught in this book 1. Learn the clear definition. 2. Follow “How it works, step by step”, then read the short explanation and example. 3. Study a worked problem, one step at a time. 4. Try* a problem yourself, then check its step-by-step solution.
You will be able to:
- Use variables, lists, dictionaries, loops and functions in Python
- Read a Python error message and find the failing line
- Calculate averages, percentages, dot products and matrix shapes by hand
- Compute a simple derivative and run gradient descent for a few steps
Skills checklist
| Python essentials | Math essentials |
|---|---|
| Variables, numbers, strings, booleans | Arithmetic, percentages, averages |
| Lists, tuples, dictionaries, sets | Basic algebra and functions |
| if/else and loops | Vectors and matrices |
| Functions and return values | Dot product and matrix multiplication intuition |
| Imports and modules | Mean and variance |
| Python essentials | Math essentials |
|---|---|
| Basic classes and objects | Basic probability |
| Exceptions and reading tracebacks | Derivative and gradient intuition |
| pip and virtual environments |
Quick prerequisite test
- Can you write and call a Python function?
- Can you loop through a list and access a dictionary value?
- Can you explain what a 2×3 matrix means?
- Can you multiply two compatible matrix shapes on paper?
- Can you calculate a simple average?
- Can you read a Python error and locate the failing line?
If you answered “no” to several of these, that is fine. This whole day teaches them. The answers are at the end of the day.
Part 1 — Python Essentials
The checklist only names the skills. Every topic below has a clear definition, a step-by-step explanation, a tiny example, the common trap, and where it shows up later in the course. Run the code in a notebook as you read: typing it yourself is what makes it stick.
1. Variables, numbers, strings, booleans
Definition A variable is a name that points to a value stored in the computer's memory. Every value has a type that decides what you can do with it: int = whole number (25), float = decimal number (9.99), str = text in quotes ("Ada"), bool = True or False.
How it works, step by step
- Pick a name: letters, digits and
_only, it cannot start with a digit, and capital letters matter (Ageandageare different). - Store a value with
=. Python works out the right side first, then attaches the name:age = 25. - Python decides the type from the value. Check it with
type(age). - Use the name wherever you need the value:
age + 1gives 26. - Convert when the type is wrong for the job:
int("5"),float("0.8"),str(25).
Syntax: name = value
A variable is a label stuck on a value. The value has a type that decides what you can do with it.
age = 25 # int (whole number)
price = 9.99 # float (decimal)
name = "Ada" # str (text)
is_ok = True # bool (True / False)
print(7 / 2) # 3.5 normal division
print(7 // 2) # 3 floor division (drops the decimal)
print(7 % 2) # 1 remainder
print(int("5") + 1) # 6 convert text -> number firstTraps: "5" + 1 is an error (text + number). Floats are approximate: 0.1 + 0.2 gives 0.30000000000000004, so never test floats with ==.
Why it matters in ML: features are numbers, labels are often strings that must be converted to numbers, and loss values are floats.
Worked Problem · Totals And Type Conversion
Problem. A shop sells 3 notebooks at 40.0 each. The quantity arrives as the text "3". Add 10% tax and print the total.
- Text cannot be multiplied like a number, so convert it first:
qty = int("3"). - Store the price:
price = 40.0. - Subtotal = quantity × price = 3 × 40.0 = 120.0.
- Tax = subtotal × 0.10 = 12.0.
- Total = subtotal + tax = 132.0. Print it.
qty = int("3")
price = 40.0
subtotal = qty * price
tax = subtotal * 0.10
total = subtotal + tax
print("Total:", total)Total: 132.0Answer. The total is 132.0. Remember: convert text to a number before doing math.
Your Turn · Add To A Score Stored As Text
Problem. A model score arrives as the text "0.8". Add 0.1 to it and print the result as a percentage.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Convert the text to a float:
score = float("0.8"). - Add 0.1:
new_score = score + 0.1, which is 0.9. - Turn 0.9 into a percentage. The format code
:.0%multiplies by 100 and adds%. - Print with an f-string.
score = float("0.8")
new_score = score + 0.1
print(f"{new_score:.0%}")90%Answer. 90%. Using float() first avoids the "0.8" + 0.1 error.
2. Lists, tuples, dictionaries, sets
Definition A collection is one variable that holds many values. List [ ] = ordered and changeable. Tuple ( ) = ordered but fixed. Dictionary {key: value} = look up a value by its key. Set { } = unique values with no duplicates.
How it works, step by step
- Ask what you need: an ordered pile that grows (list), a fixed pair or shape (tuple), a lookup table (dictionary), or “which different values exist?” (set).
- Create it with the matching brackets, for example
scores = [80, 90, 70]. - Read items by position (lists and tuples) starting at 0:
scores[0]is 80, andscores[-1]is the last item. - Read dictionary values by key:
cfg["lr"]. - Change a list with
append, or a dictionary withcfg["epochs"] = 10. Tuples cannot be changed.
nums = [10, 20, 30] # list: ordered, can change
nums.append(40) # [10, 20, 30, 40]
print(nums[0], nums[-1]) # 10 40 (index starts at 0, -1 = last)
print(nums[1:3]) # [20, 30] (slice: start included, end excluded)
shape = (2, 3) # tuple: ordered, cannot change
cfg = {"lr": 0.01, "epochs": 5} # dict: key -> value
print(cfg["lr"]) # 0.01
labels = {"cat", "dog", "cat"} # set -> {'cat', 'dog'} (no duplicates)Rule of thumb: list = a collection of things, tuple = a fixed pair or shape, dict = a lookup table, set = “which unique values exist?”
Why it matters in ML: a dataset is a list of samples, array shapes are tuples, hyperparameters live in dicts, and set(y) tells you the classes.
Worked Problem · Filter A Dictionary
Problem. Given grades = {"Asha": 82, "Ben": 67, "Cara": 91}, list the students who scored 70 or more, and find the top scorer.
- A dictionary stores pairs, so loop with
.items()to get the name and score together. - Start an empty list:
passed = []. - For each pair, if
score >= 70, add the name withpassed.append(name). - For the top scorer,
max(grades, key=grades.get)compares the scores and returns the matching name.
grades = {"Asha": 82, "Ben": 67, "Cara": 91}
passed = []
for name, score in grades.items():
if score >= 70:
passed.append(name)
print(passed)
print(max(grades, key=grades.get))['Asha', 'Cara']
CaraAnswer. Passed: Asha and Cara. Top scorer: Cara (91).
Your Turn · Count The Classes
Problem. Given labels = ["cat", "dog", "cat", "bird", "dog", "cat"], count how many times each label appears and list the unique labels in alphabetical order.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Start an empty dictionary:
counts = {}. - For each label, add 1 to its count.
counts.get(lab, 0)returns 0 the first time we see a label. set(labels)removes duplicates.sorted(...)puts them in alphabetical order.
labels = ["cat", "dog", "cat", "bird", "dog", "cat"]
counts = {}
for lab in labels:
counts[lab] = counts.get(lab, 0) + 1
print(counts)
print(sorted(set(labels))){'cat': 3, 'dog': 2, 'bird': 1}
['bird', 'cat', 'dog']Answer. cat appears 3 times, dog 2, bird 1. Unique labels: bird, cat, dog.
3. if/else and loops
Definition A condition is a question whose answer is True or False. An if statement runs code only when the condition is True. A loop repeats code once for every item in a collection (a for loop) or while a condition stays True. Indentation (4 spaces) tells Python which lines belong inside the if or the loop.
How it works, step by step
- Write the condition after
if, then a colon:if score >= 0.5:. - Indent the lines that should run when it is True. Optionally add
else:for the other case. - For a loop, write
for item in collection:and indent the body. - Python takes the first item, runs the indented body, then takes the next item, until the collection is finished.
range(5)produces 0, 1, 2, 3, 4 (it stops before 5), sofor i in range(5):runs 5 times.
scores = [0.9, 0.4, 0.75]
for s in scores:
if s >= 0.5:
print(s, "pass")
else:
print(s, "fail")
squares = [x**2 for x in range(5)] # list comprehension -> [0, 1, 4, 9, 16]
for i, s in enumerate(scores): # gives index AND value
print(i, s)Indentation (4 spaces) is how Python knows what is inside the loop. range(5) gives 0, 1, 2, 3, 4 (never 5).
Why it matters in ML: the training loop is literally for epoch in range(n):. You will read one on Day 11.
Worked Problem · Average Of Only The Passing Scores
Problem. Scores are [0.9, 0.4, 0.75, 0.3]. A score of 0.5 or more passes. Find the average of the passing scores only.
- Make two counters that start at zero:
total = 0andcount = 0. - Loop through the scores. If a score is 0.5 or more, add it to
totaland add 1 tocount. - Passing scores are 0.9 and 0.75, so total = 1.65 and count = 2.
- Average = total ÷ count = 1.65 ÷ 2.
scores = [0.9, 0.4, 0.75, 0.3]
total = 0
count = 0
for s in scores:
if s >= 0.5:
total += s
count += 1
print(total / count)0.825Answer. The average of the passing scores is 0.825.
Your Turn · Squares Of Even Numbers
Problem. Use a list comprehension to build the squares of the even numbers from 0 to 9.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- The numbers are
range(10), which gives 0 to 9. - Keep only even numbers:
if x % 2 == 0(remainder 0 when divided by 2). - Square each kept number with
x**2. - Put it together in one line:
[expression for x in range(10) if condition].
result = [x**2 for x in range(10) if x % 2 == 0]
print(result)[0, 4, 16, 36, 64]Answer. [0, 4, 16, 36, 64], which are the squares of 0, 2, 4, 6, 8.
4. Functions and return values
Definition A function is a named, reusable block of code. It takes inputs (called parameters inside the function and arguments when you call it), does some work, and sends back a result with return.
How it works, step by step
- Start with
def, the function name and the parameters in brackets, then a colon:defmean(values):. - Indent the body. Inside, use the parameters like ordinary variables.
- Finish with
return result. The function stops there and handsresultback. - Call it by name with arguments:
mean([2, 4, 6]). The returned value can be stored:m = mean([2, 4, 6]). - A parameter can have a default:
def predict(x, w=2.0). If you skip it when calling, the default is used.
Syntax: def name(parameters): followed by an indented body ending in return value.
def mean(values):
return sum(values) / len(values)
def predict(x, w=2.0, b=1.0): # w and b have default values
return w * x + b
print(mean([2, 4, 6])) # 4.0
print(predict(3)) # 7.0
print(predict(3, w=1)) # 4.0 (override one default)A function with no return gives back None. That is a very common bug when a result mysteriously becomes None.
Why it matters in ML: every model is a function: inputs go in, predictions come out.
Worked Problem · Write Your Own Error Function
Problem. Write mse(y_true, y_pred) that returns the mean squared error. Test it with [3, 5] and [2, 7].
- Mean squared error = the average of (true − predicted)².
- Pair up the values with
zip. First pair: 3 and 2, gap 3 − 2 = 1, squared 1. - Second pair: 5 and 7, gap 5 − 7 = −2, squared 4.
- Add the squares: 1 + 4 = 5. Divide by the count, 2: 5 ÷ 2 = 2.5.
- Use
returnso the caller gets the number back.
def mse(y_true, y_pred):
total = 0
for t, p in zip(y_true, y_pred):
total += (t - p) ** 2
return total / len(y_true)
print(mse([3, 5], [2, 7]))2.5Answer. 2.5. Squaring makes every gap positive, so errors in opposite directions do not cancel.
Your Turn · The Relu Function
Problem. Write relu(x) that returns x when x is positive and 0 otherwise. Apply it to [-2, 0, 3].
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Define the function with one input:
def relu(x):. - If
x > 0, returnx. Otherwise return 0. - Apply it to every value with a list comprehension.
def relu(x):
if x > 0:
return x
return 0
print([relu(v) for v in [-2, 0, 3]])[0, 0, 3]Answer. [0, 0, 3]. ReLU turns negatives (and zero) into 0 and keeps positives. You will meet it again in neural networks.
5. Imports and modules
Definition A module is a file of ready-made code. A package is a folder of modules, such as NumPy or pandas. Importing brings that code into your program so you can use it.
How it works, step by step
- If the package is not built in, install it once:
pip install numpy. - Import it at the top of your file:
import numpy as np. Theas nppart is just a short nickname. - Use a tool from it with a dot:
np.mean([1, 2, 3])means “themeantool insidenp”. - To take one tool only, write
from math import sqrtand then callsqrt(16)directly. ModuleNotFoundErrormeans the package is not installed in the environment you are running.
A module is someone else's toolbox. Import it, then use name.tool.
import numpy as np # np is just a nickname
from math import sqrt # import one tool only
print(np.mean([1, 2, 3])) # 2.0
print(sqrt(16)) # 4.0Error ModuleNotFoundError means the package is not installed in this environment: run pip install <name>.
Worked Problem · Use Numpy For Mean And Spread
Problem. Use NumPy to find the mean and the standard deviation of [2, 4, 4, 4, 5, 5, 7, 9].
- Import NumPy with its usual nickname:
import numpy as np. - Put the numbers in a list called
data. np.mean(data)gives the center.np.std(data)gives the spread.- Print both.
import numpy as np
data = [2, 4, 4, 4, 5, 5, 7, 9]
print(np.mean(data))
print(np.std(data))5.0
2.0Answer. Mean 5.0 and standard deviation 2.0. You will check these by hand in the math part below.
Your Turn · Use The Math Module
Problem. Use the math module to find the square root of 81 and the area of a circle with radius 2, rounded to 2 decimals.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Import the module:
import math. math.sqrt(81)gives the square root.- Area = π × r². Use
math.pi * 2**2. - Wrap the area in
round(..., 2).
import math
print(math.sqrt(81))
print(round(math.pi * 2**2, 2))9.0
12.57Answer. Square root: 9.0. Circle area: 12.57.
6. Basic classes and objects
Definition A class is a blueprint. An object is one thing built from that blueprint. Attributes are the data stored on the object (self.w). Methods are functions inside the class. self means “this object”, and it is always the first parameter of a method.
How it works, step by step
- Write
class Neuron:to start the blueprint. - Add
__init__(self, w, b). It runs automatically when you create an object, and it stores the starting data:self.w = w. - Add methods with
selffirst, for exampledef forward(self, x):. - Create an object by calling the class like a function:
n = Neuron(2.0, 1.0). - Use the object's methods with a dot:
n.forward(3).
A class bundles data and functions. You will read classes far more often than write them.
class Counter:
def __init__(self): # runs when you create the object
self.count = 0
def add(self, n): # a method: a function inside a class
self.count += n
c = Counter() # make an object
c.add(5)
print(c.count) # 5Why it matters in ML: in PyTorch a model is a class with __init__ (define layers) and forward (how data flows). Same pattern as above.
Worked Problem · A One-Neuron Model As A Class
Problem. Make a class Neuron that stores a weight w and a bias b, and has a method forward(x) that returns w * x + b. Test it with w = 2.0, b = 1.0, x = 3.
__init__runs when the object is created. Save the inputs on the object withself.w = wandself.b = b.- Write
forward(self, x). It uses the saved values:return self.w * x + self.b. - Create an object:
n = Neuron(2.0, 1.0). - Call the method:
n.forward(3)gives 2.0 × 3 + 1.0 = 7.0.
class Neuron:
def __init__(self, w, b):
self.w = w
self.b = b
def forward(self, x):
return self.w * x + self.b
n = Neuron(2.0, 1.0)
print(n.forward(3))7.0Answer. 7.0. This is the same shape as every PyTorch model you will build later.
Your Turn · A Scaler Class
Problem. Make a class Scaler that stores a mean and a std, and has transform(x) returning (x - mean) / std. Create Scaler(5, 2) and transform 9 and 5.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Save
meanandstdin__init__. - Write
transformusing the saved values. - For 9: (9 − 5) ÷ 2 = 2.0. For 5: (5 − 5) ÷ 2 = 0.0.
class Scaler:
def __init__(self, mean, std):
self.mean = mean
self.std = std
def transform(self, x):
return (x - self.mean) / self.std
s = Scaler(5, 2)
print(s.transform(9))
print(s.transform(5))2.0
0.0Answer. 2.0 and 0.0. A value equal to the mean becomes 0. This is exactly how feature scaling works.
7. Exceptions and reading tracebacks
Definition An exception is an error raised while the program runs. A traceback is the error report that Python prints. try / except lets your program catch an error and continue instead of crashing.
How it works, step by step
- Read the last line first. It names the error type and the reason.
- Find the last
File ..., line Nabove it. That is where Python stopped. - Look at that line of code and ask: what value did I expect, and what did I actually have? Print it.
- Fix the cause, then run again.
- If failure is expected (for example bad user input), wrap the risky line in
try:and handle it inexcept ErrorName:.
| Error name | What it usually means |
|---|---|
| SyntaxError | The code is written wrongly (missing colon, bracket or quote) |
| NameError | You used a name that does not exist (typo, or not defined yet) |
| TypeError | The wrong type for the operation, such as text + number |
| ValueError | Right type but a bad value, such as int("abc") |
| IndexError | You asked for a position that is not in the list |
| KeyError | You asked for a dictionary key that does not exist |
| ModuleNotFoundError | The package is not installed in this environment |
Read the error from the bottom up: last line = what went wrong, lines above = where.
Traceback (most recent call last):
File "train.py", line 12, in <module>
result = predict(x)
File "train.py", line 5, in predict
return w * x + b
TypeError: can't multiply sequence by non-int of type 'float'
# ^ what: x is a list, w is a float ^ where: line 5, inside predicttry / except catches errors so the program continues:
try:
value = int("abc")
except ValueError:
print("not a number")Why it matters in ML: most deep-learning bugs are shape or type mismatches. Reading the last line of the error solves about 80% of them.
Worked Problem · Read An Error And Fix It
Problem. The call predict([1, 2]) crashes. Find out why and fix it.
- Look at the last line of the error: it says what went wrong. Here it is a
TypeErrorabout multiplying a sequence by a float. - Look one line above for where:
w * x + binsidepredict. - Work out the cause:
xis the list[1, 2], butpredictexpects one number. - Fix it: call
predictonce for each number, using a list comprehension.
def predict(x, w=2.0, b=1.0):
return w * x + b
try:
predict([1, 2])
except TypeError as e:
print("TypeError:", e)
print([predict(v) for v in [1, 2]])TypeError: can't multiply sequence by non-int of type 'float'
[3.0, 5.0]Answer. Cause: a list was passed where one number was expected. Fix: [3.0, 5.0], one call per value.
Your Turn · Skip Bad Values Safely
Problem. Convert ["3", "x", "5"] to numbers. Skip anything that cannot be converted and print a message for it.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Start an empty list:
nums = []. - Loop over the items and try
float(item)insidetry:. - If it fails, Python raises
ValueError. Catch it withexcept ValueError:and print a message. - Print the final list.
raw = ["3", "x", "5"]
nums = []
for item in raw:
try:
nums.append(float(item))
except ValueError:
print("skipping", item)
print(nums)skipping x
[3.0, 5.0]Answer. The loop skips x and prints [3.0, 5.0].
8. pip and virtual environments
*Definition pip is Python's package installer. A virtual environment* is an isolated folder with its own copy of Python and its own packages, so one project cannot break another.
How it works, step by step
- Create the environment once per project:
python -m venv env. - Activate it. Your terminal prompt now starts with
(env). - Install what you need with
pip install name. It goes into this environment only. - Run your code while the environment is active.
- Leave it with
deactivate.
python -m venv env # create an isolated environment
source env/bin/activate # Mac/Linux (Windows: env\Scripts\activate)
pip install numpy pandas # install packages into it
pip list # see what is installedWhy: project A may need one version of a library and project B another. A virtual environment keeps them from breaking each other. In Google Colab you skip this, since it is already set up.
Worked Problem · Set Up A Clean Project
Problem. Create a new environment for this course and install NumPy and pandas.
- Open a terminal in your project folder.
- Create the environment:
python -m venv env. This makes a folder calledenv. - Activate it. On Mac/Linux run
source env/bin/activate. On Windows runenv\Scripts\activate. Your prompt now starts with(env). - Install the packages with
pip install numpy pandas. - Check with
pip list. You should see both names.
python -m venv env
source env/bin/activate
pip install numpy pandas
pip listAnswer. When the prompt shows (env), you are inside the environment. Close the terminal or run deactivate to leave it.
Your Turn · Check A Library Version
Problem. Find out which version of NumPy is installed, and leave the environment when you are done.
Try it on paper or in a notebook first. Then check the solution.
Solution — Step By Step
- Run a one-line Python command that imports NumPy and prints its version.
- Or ask pip:
pip show numpyprints the name, version and location. - Leave the environment with
deactivate.
python -c "import numpy; print(numpy.__version__)"
pip show numpy
deactivateAnswer. Either command shows the version number (it depends on your install). Knowing the version helps when a tutorial says “requires NumPy 1.x or newer”.