chapter 01 / ml-foundations · estimated time 90-120 min

Introduction to Machine Learning
and
Core Concepts

AUDIO // Chapter Audio Guide
Chapter Contents
  1. What Is Machine Learning: A Different Way to Write Programs
  2. The Three Learning Paradigms
  3. The Language of Machine Learning: Data, Model, Loss
  4. Linear Regression: Our First Complete Derivation
  5. The Analytical Solution: The Normal Equation
  6. Gradient Descent: The Engine of All Deep Learning
  7. Interactive Lab: Train a Model with Your Own Hands
  8. Generalization: The Real Challenge of Machine Learning
  9. The Bias-Variance Decomposition
  10. Hands-On Code: A From-Scratch NumPy Implementation
  11. Chapter Quiz

What Is Machine Learning: A Different Way to Write Programs

Traditional programming works like this: a human understands the problem, writes the solution as rules, and the computer executes those rules.

rules + data  ──▶  computer  ──▶  answers

But there is a class of problems where humans themselves can't articulate the rules. You can recognize that a photo contains a cat, but you can't write down the "if-else rules for cat-ness" — Pointy ears? What about Scottish folds? Has fur? What about hairless cats? The rules explode. Machine learning flips the process around:

data + answers  ──▶  computer  ──▶  rules (the model)

We don't tell the computer "how to do it." Instead, we give it lots of "input → correct output" examples and let it find for itself the function that maps inputs to outputs. That is the essence of machine learning: automatically finding a function from data.

A more rigorous definition comes from Tom Mitchell (1997):

A program is said to "learn" if its performance on a task T (measured by metric P) improves with experience E.
Example: a spam filter — T = classifying email, P = classification accuracy, E = the emails users have flagged. The more users flag, the more accurate the filtering → it is learning.
Why is the phrase "finding a function" literally accurate?
Because every ML model is ultimately a mathematical function $f$: image classification is $f: \mathbb{R}^{224\times224\times3} \to \{1..1000\}$ (pixels to class); ChatGPT is $f: \text{sequence of preceding tokens} \to \text{probability distribution over the next token}$. "Training" means searching a vast space of functions, using data as clues, to find the best $f$. Every chapter that follows — from linear regression to GPT — is just a continual upgrade of two things: "what the function looks like" and "how we search for it."

The Three Learning Paradigms

Supervised Learning

The dataset consists of pairs $(x, y)$: each input $x$ comes with a human-annotated correct answer $y$ (the label). The model learns the mapping $f(x) \approx y$. Depending on the type of $y$, there are two cases:

Unsupervised Learning

You only have $x$, with no labels. The model discovers the structure inside the data on its own:

Reinforcement Learning

There are no ready-made labels, but there is a reward signal. An agent acts within an environment, the environment feeds back rewards, and the goal is to learn a policy that maximizes long-term cumulative reward. AlphaGo, robot control, and — pay attention here — the core technique behind RLHF for training ChatGPT and behind training reasoning models (OpenAI's o-series, DeepSeek-R1), covered in detail in Chapter 8.

ParadigmData formWhat is learnedTypical examples
Supervised learningpairs $(x, y)$mapping $f(x)\to y$image classification, house-price prediction
Unsupervised learningonly $x$structure/distribution of the dataclustering, PCA, generative models
Reinforcement learningstates, actions, rewardsoptimal policy $\pi(a|s)$AlphaGo, RLHF
Large language models use all three: pretraining is self-supervised learning (it manufactures its own supervision signal from unlabeled text via "predict the next word," sitting between supervised and unsupervised); SFT fine-tuning is supervised learning; RLHF/RLVR alignment is reinforcement learning. By the end of this course you will have walked this entire chain end to end.

The Language of Machine Learning: Data, Model, Loss

Let's settle the terminology first; all eleven chapters that follow rely on it:

Burn this sentence into your mind; it is the skeleton of all modern AI: machine learning = model (parameterized function) + loss (how wrong) + optimization (how to adjust parameters to make the loss smaller). The only difference between GPT-4 and linear regression is the complexity of each of these three pieces.

Before we start deriving, watch a video that explains the "intuition of learning" better than anything else. It is nominally about neural networks, but the imagery of "what the network is learning, what the parameters are" will give you an intuitive anchor for all the math to come:

VIDEO 01
But what is a neural network?
3Blue1Brown · Deep Learning series, Episode 1 18:40
Viewing guide · watch with these 3 questions in mind
  • 02:42 A neuron is just "a container holding a number" — corresponding to what we just said: a model is only a function.
  • 05:30 Weights and biases — this is the true identity of the parameters $\theta$. Note when it says this network has 13002 parameters: "learning" means finding the right setting for these 13002 knobs.
  • 12:30 Why layers? The layer-by-layer abstraction of features. Chapters 3 and 4 will return to this image again and again.
  • We recommend turning on subtitles (Settings → Subtitles → Auto-translate → your language).

Linear Regression: Our First Complete Derivation

Now let's walk through the "three pieces" completely using the simplest possible model. Don't skip it just because it's simple — concepts like gradient descent, the loss surface, and the learning rate are seen most clearly here, and by the time you reach the Transformer in Chapter 6, the mathematical skeleton is still this same one.

Model

Assume the output is a linear function of the input. For a single feature:

$$\hat{y} = f_\theta(x) = wx + b$$

$w$ (weight/slope) and $b$ (bias/intercept) are the parameters, $\theta = (w, b)$. The hat $\hat{y}$ denotes the "predicted value," distinct from the true value $y$. With multiple features, we write it as a vector inner product $\hat{y} = \mathbf{w}^\top \mathbf{x} + b$.

Loss: Mean Squared Error (MSE)

For each sample, the error is $\hat{y}^{(i)} - y^{(i)}$. Square all the sample errors and take the average:

$$L(w, b) = \frac{1}{n} \sum_{i=1}^{n} \left( wx^{(i)} + b - y^{(i)} \right)^2$$
Why squared, rather than absolute value or fourth power?
Three reasons, each deeper than the last:
Differentiability: $|e|$ is not differentiable at 0, whereas the square is smooth and differentiable everywhere, which makes computing gradients convenient.
Penalty structure: the square's penalty for large errors grows quadratically — twice the error, four times the penalty. A fourth power penalizes too aggressively and gets completely hijacked by outliers (in the Playground below you can add two outliers and see with your own eyes just how scared squared loss already is of outliers).
Probabilistic interpretation (the deepest): if you assume the noise follows a Gaussian distribution $y = wx + b + \varepsilon,\ \varepsilon \sim \mathcal{N}(0, \sigma^2)$ and do maximum likelihood estimation on the parameters, write out the log-likelihood and negate it, what remains is exactly the MSE. In other words, "minimizing MSE = maximum likelihood under an assumption of Gaussian noise." The same recipe (assume a distribution → maximum likelihood → loss function) will derive cross-entropy in Chapter 2 and the pretraining objective of LLMs in Chapter 7. Loss functions are never picked at random.

The Analytical Solution: The Normal Equation

$L(w,b)$ is a quadratic function of the parameters — a bowl-shaped surface with a unique lowest point. Calculus tells us: at the lowest point, the partial derivatives are zero. Solve directly:

$$\frac{\partial L}{\partial w} = \frac{2}{n}\sum_{i=1}^n \left(wx^{(i)} + b - y^{(i)}\right)x^{(i)} = 0$$ $$\frac{\partial L}{\partial b} = \frac{2}{n}\sum_{i=1}^n \left(wx^{(i)} + b - y^{(i)}\right) = 0$$

Two equations, two unknowns; solving (derivation: the second equation gives $b = \bar{y} - w\bar{x}$, substitute into the first and simplify):

$$w^* = \frac{\sum_i (x^{(i)} - \bar{x})(y^{(i)} - \bar{y})}{\sum_i (x^{(i)} - \bar{x})^2} = \frac{\text{Cov}(x, y)}{\text{Var}(x)}, \qquad b^* = \bar{y} - w^*\bar{x}$$

The multi-feature case is more elegant in matrix form. Stack all samples into a matrix $X \in \mathbb{R}^{n \times d}$ (one sample per row, with a column of all ones appended to absorb $b$), and stack the labels into $\mathbf{y} \in \mathbb{R}^n$, so $L(\mathbf{w}) = \frac{1}{n}\|X\mathbf{w} - \mathbf{y}\|^2$. Take the gradient with respect to $\mathbf{w}$ and set it to zero:

$$\nabla_\mathbf{w} L = \frac{2}{n} X^\top (X\mathbf{w} - \mathbf{y}) = 0 \;\;\Longrightarrow\;\; \boxed{\mathbf{w}^* = (X^\top X)^{-1} X^\top \mathbf{y}}$$

This is the famous Normal Equation.

If there's a formula to compute it directly, why do we still need gradient descent later? Two fatal problems: ① the complexity of the inversion $(X^\top X)^{-1}$ is $O(d^3)$ — at GPT scale $d$ is in the trillions, and it wouldn't finish before the heat death of the universe; ② the analytical solution exists only for the special case of a linear model + MSE. A neural network's loss surface is full of bumps and pits, with no closed-form solution at all. So the real engine of modern AI is the iterative method in the next section.

Gradient Descent: The Engine of All Deep Learning

Take a different approach: instead of solving the equations directly, start from a random position and walk downhill one small step at a time until you reach the bottom of the valley.

The gradient $\nabla_\theta L$ is a vector pointing in the direction in which the loss increases fastest. So walk in the opposite direction:

$$\theta_{t+1} = \theta_t - \eta \, \nabla_\theta L(\theta_t)$$

$\eta$ is the learning rate — how big a step to take each time. For our linear regression, just reuse the partial derivatives we computed in the last section, and each update step becomes:

$$w \leftarrow w - \eta \cdot \frac{2}{n}\sum_i (\hat{y}^{(i)} - y^{(i)})\, x^{(i)}, \qquad b \leftarrow b - \eta \cdot \frac{2}{n}\sum_i (\hat{y}^{(i)} - y^{(i)})$$
Notice the shape of the gradient formula: error × input. Intuition: the larger the error and the larger that feature's value, the more blame this parameter has to carry, and the harder it gets adjusted. This "error × input" shape will reappear in the backpropagation derivation in Chapter 3 — backpropagation is essentially using the chain rule to pass "how the error should be apportioned as blame" back layer by layer.

Three Ways to Feed the Data

VariantData used per stepCharacteristics
Batch gradient descent (BGD)all $n$ samplesaccurate gradient, stable direction, but expensive per step; infeasible when data is large
Stochastic gradient descent (SGD)1 sampleextremely fast per step but the direction jitters a lot; the noise actually helps escape local pits
Mini-batch$B$ samples (e.g. 32~512)the practical standard. GPU-parallel friendly, with moderate noise. LLM training batches can reach millions of tokens

Learning Rate: The Most Important Hyperparameter

Don't just read the words — go to the lab below, drag the learning-rate slider all the way to the right, and watch divergence happen with your own eyes. That image is more unforgettable than any formula.

First watch two videos to cement this. The first uses "going downhill" to explain gradient descent thoroughly; the second is StatQuest, breaking down every arithmetic step, perfect if you want to follow along and compute by hand:

VIDEO 02
Gradient descent, how neural networks learn
3Blue1Brown · Deep Learning series, Episode 2 21:01
Viewing guide · watch with these 3 questions in mind
  • 04:45 Visualizing the cost surface — the high-dimensional version of the loss curve in our Playground.
  • 06:10 The geometric intuition that "the negative gradient direction = the direction of steepest descent," corresponding to the formula $\theta \leftarrow \theta - \eta\nabla L$.
  • 09:30 Going downhill in 13002-dimensional space — understand that "training GPT" and "fitting a line" are mathematically the same thing.
VIDEO 03
Gradient Descent, Step-by-Step
StatQuest with Josh Starmer 23:54
Viewing guide · optional, suited for those who want to compute by hand once
  • 05:00 Computing the gradient of the intercept with real numbers — corresponds term-by-term to our formula in §6.
  • 12:20 Updating both parameters simultaneously (slope + intercept) — note you must update them "simultaneously," not update one first and then use the new value to compute the other.
  • 18:00 Step size (learning rate) and convergence criteria.

Interactive Lab: Train a Model with Your Own Hands

linear-regression.train

Click the canvas to add data points (or pick a preset dataset) → click "Start Training" and watch the blue line get pulled into place by gradient descent step by step. The red vertical lines = each point's residual (error), the yellow curve = the trajectory of the loss decreasing over iterations. Must-do experiments: ① crank the learning rate to its maximum and watch divergence; ② add two outliers and watch MSE get hijacked; ③ switch to SGD and watch the jitter.

w = 0 b = 0 step = 0 MSE = 0
In SGD mode, why does the loss curve keep jittering at a low level and never decrease further?
SGD looks at only one random sample per step, so the gradient is an unbiased but noisy estimate of the true gradient. Near the optimum, the true gradient approaches 0, but the noise of the single-sample gradient is still there, so the parameters perform a random walk around the optimum and the loss oscillates on a "noise floor." The fix is learning-rate decay: the noise magnitude is proportional to $\eta$, so gradually shrinking $\eta$ lets it converge deeper. This is precisely one of the reasons LLM training uses cosine-decay schedules.

Generalization: The Real Challenge of Machine Learning

So far we've only made the loss small on the training data. But the real purpose of training is for the model to perform well on data it has never seen — this is called generalization. This is the essential distinction between ML and plain optimization.

The essence of overfitting is not that "the model is big," but that "the model has memorized non-repeatable accidental patterns from limited data." The criterion is always: the gap between training error and validation error. A large gap = overfitting. Modern LLMs have enormous parameter counts yet don't overfit severely, because the data is even more enormous (trillions of tokens) and they're trained for roughly one epoch only — Chapter 7 covers this delicate balance in detail (along with counterintuitive phenomena like "grokking").

The Discipline of Splitting Data

SetPurposeTypical proportion
Training setupdate parameters $\theta$~80%
Validation settune hyperparameters (learning rate, model size), early stopping~10%
Test setuse only once, at the very end, to report final performance~10%

Why separate validation and test? Because you'll repeatedly tune hyperparameters based on validation performance — tune too much, and the hyperparameters "secretly fit" the validation set, inflating the validation score. The test set is a "judge" that never participated in any decision. When data is scarce, use K-fold cross-validation: split into K folds, take turns holding one out for validation, and average. In the LLM era this same discipline persists, becoming the problem of "benchmark contamination" — once benchmark questions leak into the pretraining data, the scores become untrustworthy.

The Bias-Variance Decomposition

Underfitting/overfitting can be written as precise mathematics. Suppose the true relationship is $y = g(x) + \varepsilon$, with noise $\varepsilon$ of mean 0 and variance $\sigma^2$. At a fixed point $x$, taking the expectation over "models $\hat{f}$ trained on different training sets," the expected squared error decomposes exactly into three terms (derivation: add and subtract $\mathbb{E}[\hat{f}(x)]$ then expand the square; the cross term has expectation zero):

$$\mathbb{E}\big[(y - \hat{f}(x))^2\big] = \underbrace{\big(g(x) - \mathbb{E}[\hat{f}(x)]\big)^2}_{\text{bias}^2} + \underbrace{\mathbb{E}\big[(\hat{f}(x) - \mathbb{E}[\hat{f}(x)])^2\big]}_{\text{variance}} + \underbrace{\sigma^2}_{\text{irreducible error}}$$
Classical theory says bias and variance trade off against each other (the U-shaped test-error curve). But the 2019 "double descent" research found that once a model is large enough to perfectly interpolate the training data, making it even larger causes the test error to fall again. This is one of the theoretical backdrops for "why bigger LLMs are better," and it is the most famous crack between classical statistical learning theory and deep learning practice. Remember this foreshadowing.

Hands-On Code: A From-Scratch NumPy Implementation

Translate all the math of this chapter into 30 lines of code. Read it line by line; each line corresponds to a formula from earlier:

python · linear_regression_from_scratch.py
# Linear regression from scratch: normal equation vs gradient descent
import numpy as np

rng = np.random.default_rng(42)

# ---- Make data: y = 2.5x + 1.0 + Gaussian noise ----
n = 200
x = rng.uniform(0, 10, size=n)
y = 2.5 * x + 1.0 + rng.normal(0, 1.5, size=n)

# ---- Method 1: normal equation  w* = (XᵀX)⁻¹Xᵀy ----
X = np.column_stack([x, np.ones(n)])        # append a column of 1s to absorb the bias b
w_closed = np.linalg.solve(X.T @ X, X.T @ y) # more stable than explicit inversion
print(f"Analytical   w={w_closed[0]:.4f}, b={w_closed[1]:.4f}")

# ---- Method 2: batch gradient descent ----
w, b = 0.0, 0.0          # random starting point (here we use 0)
eta = 0.01               # learning rate
for step in range(2000):
    y_hat = w * x + b                 # forward: prediction
    err = y_hat - y                   # error vector
    grad_w = 2 * np.mean(err * x)     # ∂L/∂w = (2/n)Σ err·x  "error × input"
    grad_b = 2 * np.mean(err)         # ∂L/∂b = (2/n)Σ err
    w -= eta * grad_w                 # take a step along the negative gradient
    b -= eta * grad_b
    if step % 500 == 0:
        print(f"step {step:4d}  MSE={np.mean(err**2):.4f}  w={w:.3f} b={b:.3f}")

print(f"Grad descent w={w:.4f}, b={b:.4f}   # matches the analytical solution ✓")

In production it's a one-liner (but now you know what happens behind that one line):

python · sklearn version
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(x.reshape(-1, 1), y)
print(model.coef_[0], model.intercept_)   # ≈ 2.5, 1.0
Why does the code use np.linalg.solve instead of np.linalg.inv followed by a multiplication?
Numerical stability. inv explicitly inverts and then multiplies, and the error gets amplified by the condition number twice; solve uses LU decomposition to solve the linear system directly, which is faster and more stable. When features are highly correlated (multicollinearity), $X^\top X$ is nearly singular, and this difference becomes a chasm. Engineering details are part of technical skill too.

Finally, take a glimpse of where all this leads. This talk by Karpathy (a former founding member of OpenAI and former Director of AI at Tesla) is the best bridge from "this chapter to LLMs" — you'll find that the "training is compression" and "next-word prediction" he describes have exactly the skeleton you just finished learning: model + loss + optimization:

VIDEO 04 · LOOKING AHEAD
Intro to Large Language Models
Andrej Karpathy 59:48
Viewing guide · for now just watch the first 18 minutes
  • 00:00 An LLM = two files: a parameter file + the code that runs the parameters. The parameters are this chapter's $\theta$, just 70 billion of them.
  • 04:17 Training = lossy compression of the internet. Another way of phrasing the loss-function perspective.
  • 07:50 Why next-word prediction can squeeze out "understanding."
  • After 18 minutes it covers fine-tuning and agents; come back once you've reached Chapters 8 and 11, and the experience will be completely different.

Chapter Quiz