R0KernelPath
module 217 / 262

Foundations of Machine Learning

Machine learning teaches computers to improve from experience — turning raw data into generalizing functions without being explicitly programmed for every case.

~18h focused study10 concepts

Overview

Machine learning is the study of algorithms that learn patterns from data and use those patterns to make decisions or predictions on new, unseen inputs. Unlike classical programming — where a human writes explicit rules — ML systems infer rules from examples. This inversion is profound: instead of if pixel_intensity > 128 then it's bright, you hand the algorithm ten thousand images with labels and it discovers the relevant structure itself.

The field sits at the intersection of statistics, optimization, and computer science. A statistician might say ML is about estimating distributions and making decisions under uncertainty. An optimization theorist might say it's about minimizing loss functions over function spaces. A computer scientist might say it's about building systems that improve with experience. All three views are correct, and the power of the field comes from their union.

This module covers the foundational scaffolding that all of ML rests on: the three major learning paradigms (supervised, unsupervised, reinforcement), the language of features and labels, the critical distinction between training and generalization, and the standard engineering workflow from raw data to deployed model. These ideas appear again and again across every subfield — whether you're building a neural language model, a recommender system, or an autonomous driving stack.

By the end, you will have the conceptual vocabulary and mathematical grounding to understand why ML works, when it fails, and how to build systems that actually generalize. This is not a survey of algorithms — it is the bedrock on which all algorithms stand.

why this matters for the kernel

Machine learning has become the primary technology by which computers perceive the world, translate language, diagnose disease, and recommend content. Understanding its foundations is no longer optional for any serious software engineer or scientist. Nearly every system that touches real-world unstructured data — images, text, sensor streams, genomic sequences — uses ML at its core. Without understanding generalization, train/test splits, and the bias-variance tradeoff, a practitioner will routinely build models that appear to work but fail in production, sometimes catastrophically.

At a deeper level, ML foundations connect to some of the most beautiful ideas in mathematics and science. The PAC learning framework answers the philosophical question "how much data do we need to learn a concept reliably?" in rigorous terms. The notion of the VC dimension gives a geometric measure of a hypothesis class's expressive power. Bias-variance decomposition explains why making a model more flexible does not always improve it. These are not mere engineering heuristics — they are theorems, and knowing them gives you the kind of deep intuition that separates engineers who cargo-cult tutorials from researchers who can reason from first principles about why something works.

The Three ML Paradigms and the Training-Generalization Loop

rendering diagram…

The full ML workflow: raw data flows through paradigm selection, train/val/test partitioning, the training-validation cycle for model and hyperparameter selection, and finally a single test-set evaluation. The outer loop — triggered by deployment failures or distribution shift — feeds back into data collection, closing the empirical science cycle.

The concepts

01What Is Learning? The Mitchell Definition

Tom Mitchell's 1997 definition remains the cleanest framing: A computer program is said to learn from experience E with respect to some task T and performance measure P, if its performance at T, as measured by P, improves with experience E.

This deceptively simple sentence does a lot of work. It forces you to be precise about three things before building anything:

  1. Task T: What are you trying to do? Classify email as spam or not-spam? Predict tomorrow's stock price? Generate a caption for an image? The task defines the output space.
  2. Experience E: What data does the system learn from? Labeled examples? Rewards from an environment? Unlabeled text?
  3. Performance P: How do you measure success? Accuracy? Mean squared error? Revenue per user? The choice of metric encodes your values and constraints.

Notice what this definition does not say: it does not say the program is "intelligent" or "understands". It is a purely behavioral, operational definition. A spam filter that improves its accuracy from 80% to 95% after seeing more labeled emails is learning by this definition, full stop — no claims about cognition required.

The definition also implicitly encodes the core challenge: the experience E is a finite sample from some distribution, but we want performance P to hold on the full distribution of future inputs. This gap — between what you've seen and what you'll see — is the central tension in all of machine learning.

# Mitchell's three elements made concrete
# Task T: classify whether a tumor is malignant
# Experience E: 1000 labeled biopsy records
# Performance P: classification accuracy on held-out test set

from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = LogisticRegression(max_iter=10000)
model.fit(X_train, y_train)   # experience E shapes the model

print(accuracy_score(y_test, model.predict(X_test)))  # performance P on unseen data

02Supervised Learning: Learning with a Teacher

In supervised learning, every training example comes with a label — the correct answer. The algorithm's job is to find a function f that maps inputs to outputs, such that f generalizes well to new inputs it has never seen.

Formally: given a dataset D = {(x₁,y₁), (x₂,y₂), ..., (xₙ,yₙ)} where each xᵢ ∈ X is a feature vector and each yᵢ ∈ Y is a label drawn from some unknown joint distribution P(X, Y), find a hypothesis h: X → Y that minimizes expected loss E[L(h(x), y)] over P.

The two main flavors:

  • Classification: Y is discrete. Predict which of k categories an input belongs to. Examples: spam detection (k=2), digit recognition (k=10), ImageNet (k=1000).
  • Regression: Y is continuous. Predict a real number. Examples: predicting house prices, forecasting temperature, estimating remaining battery life.

The key insight: the "teacher" (the labels) provides a training signal. At each step the model makes a prediction, compares it to the label via a loss function, and adjusts its parameters to reduce that loss. The choice of loss function is not arbitrary — it encodes assumptions about the noise model. Mean squared error implicitly assumes Gaussian noise. Cross-entropy implicitly assumes Bernoulli/categorical outputs. Understanding this connection (loss ↔ probabilistic model) lets you reason about why a particular loss is appropriate.

Why it works: the No Free Lunch theorem (Wolpert 1996) shows that no single algorithm outperforms all others across all problems. Supervised learning works in practice because real-world data has structure — images have spatial correlations, text has grammatical regularity, user preferences cluster. The inductive biases built into our algorithms (locality in CNNs, compositionality in deep nets) match this structure.

import numpy as np

# Minimal linear regression from scratch — supervised learning laid bare
# Task: predict y from x; Experience: (x,y) pairs; Performance: MSE

np.random.seed(0)
x = np.linspace(0, 10, 50)          # feature
y = 2.5 * x + 1.0 + np.random.randn(50) * 1.5  # true signal + noise

# Closed-form OLS solution: w* = (X^T X)^{-1} X^T y
X = np.column_stack([x, np.ones(len(x))])  # design matrix [x, 1]
w = np.linalg.lstsq(X, y, rcond=None)[0]   # [slope, intercept]
print(f"Learned: slope={w[0]:.3f}, intercept={w[1]:.3f}")
# True values: slope=2.5, intercept=1.0  — we recover them from noisy data!

03Unsupervised Learning: Finding Structure in the Unlabeled Wild

Unsupervised learning has no labels. The dataset is just D = {x₁, x₂, ..., xₙ} — raw inputs, no correct answers attached. The goal is to discover hidden structure in the data itself.

This might sound less well-defined than supervised learning, and in a sense it is — "structure" must be defined by the algorithm's inductive bias. The main tasks:

  • Clustering: partition data into groups where members within a group are more similar to each other than to members of other groups. K-means, DBSCAN, Gaussian mixture models. Application: customer segmentation, topic modeling, genomic subtype discovery.
  • Dimensionality reduction: find a low-dimensional representation that preserves the important geometry of high-dimensional data. PCA finds the directions of maximum variance. t-SNE and UMAP preserve local neighborhood structure for visualization. Autoencoders learn nonlinear manifolds.
  • Density estimation: model the probability distribution P(X) that generated the data. Kernel density estimation, variational autoencoders (VAEs), normalizing flows, and diffusion models all do this. Once you have a density model, you can generate new samples, detect anomalies (low-density inputs), and do principled imputation.
  • Representation learning: learn features that are useful for downstream tasks. Self-supervised learning (e.g., BERT's masked language modeling, SimCLR's contrastive learning) is technically unsupervised but trains representations that transfer well to supervised tasks with little labeled data.

The philosophical question unsupervised learning confronts is: what is a good representation of data? There is no single answer — it depends on what you want to do with the representation. This is why unsupervised learning is harder to evaluate than supervised learning. Cluster quality metrics (silhouette score, Davies-Bouldin) are proxies, not ground truth.

The scale advantage of unsupervised learning is enormous: labeled data is expensive (human annotation), but unlabeled data is cheap (the internet). This is why pretraining large models on unlabeled text (GPT, BERT) followed by fine-tuning on small labeled datasets has become the dominant paradigm in NLP.

from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import numpy as np

# Generate 3 clusters in 10D, then reduce to 2D and cluster
np.random.seed(42)
blobs = np.vstack([
    np.random.randn(100, 10) + np.array([5]*10),
    np.random.randn(100, 10) + np.array([0]*10),
    np.random.randn(100, 10) + np.array([-5]*10),
])

# Step 1: reduce dimensionality (unsupervised)
pca = PCA(n_components=2)
X_2d = pca.fit_transform(blobs)
print(f"Variance explained: {pca.explained_variance_ratio_.sum():.2%}")

# Step 2: cluster in 2D (unsupervised — no labels used at any point)
km = KMeans(n_clusters=3, random_state=0, n_init='auto')
labels = km.fit_predict(X_2d)
print(f"Cluster sizes: {np.bincount(labels)}")  # should be ~[100, 100, 100]

04Reinforcement Learning: Learning from Consequences

Reinforcement learning (RL) is the third paradigm, and it's structurally different from both supervised and unsupervised learning. An agent interacts with an environment in a loop: it observes the current state s, takes an action a, receives a scalar reward r, and transitions to a new state s'. The agent's goal is to learn a policy π(a|s) — a mapping from states to actions — that maximizes cumulative future reward.

Formally, RL optimizes: E[Σₜ γᵗ rₜ] where γ ∈ [0,1) is a discount factor that makes immediate rewards worth more than distant ones.

What makes RL distinct:

  1. Delayed credit assignment: rewards often come long after the actions that caused them. A chess program may make a brilliant sacrifice on move 20 that only pays off on move 40. Figuring out which past actions deserve credit for a reward is the core challenge.
  2. Exploration vs. exploitation: the agent must balance exploiting known good actions (taking moves it believes are good) with exploring new actions (to discover potentially better ones). Purely exploiting leads to local optima; purely exploring is useless.
  3. Non-i.i.d. data: unlike supervised learning where training examples are independent, in RL the agent's actions change the distribution of future states. The data is generated by the policy itself — a circular dependency that makes convergence analysis hard.

RL has produced stunning results: AlphaGo (2016), AlphaFold2's template-free structure prediction pipeline, OpenAI Five's Dota 2 play, and Anthropic/OpenAI's use of RLHF (Reinforcement Learning from Human Feedback) to align language models. The key insight of RLHF is treating human preference comparisons as rewards, replacing the hand-crafted reward function with learned human preferences.

The formal framework is a Markov Decision Process (MDP): (S, A, P, R, γ) where S is the state space, A is the action space, P(s'|s,a) is the transition probability, R(s,a) is the reward function, and γ is the discount. The Markov property — P(s'|s,a) depends only on the current state and action, not history — is both a mathematical convenience and a practical necessity.

# Minimal Q-learning on a grid world — RL from scratch
import numpy as np

# 1D grid: states 0..4, goal=4, actions={left=-1, right=+1}
Q = np.zeros((5, 2))  # Q[state, action]
alpha, gamma, epsilon = 0.1, 0.9, 0.1

for episode in range(5000):
    s = 0  # always start at left
    for _ in range(100):
        # epsilon-greedy action selection (exploration vs exploitation)
        a = np.random.randint(2) if np.random.rand() < epsilon else np.argmax(Q[s])
        s_next = max(0, min(4, s + (1 if a == 1 else -1)))
        r = 1.0 if s_next == 4 else 0.0  # reward only at goal
        # Bellman update
        Q[s, a] += alpha * (r + gamma * np.max(Q[s_next]) - Q[s, a])
        s = s_next
        if s == 4: break

print("Learned Q-values (right action should dominate):")
print(np.round(Q, 2))

05Features, Labels, and the Representation Problem

Every ML algorithm operates on a feature vector x ∈ Rᵈ — a numerical representation of a raw input. Choosing the right features is one of the most consequential decisions in an ML project, and historically it was the primary bottleneck.

Features are the measurable properties of your data that you hand to the model. For a house price prediction problem, features might include: square footage, number of bedrooms, zip code (one-hot encoded), year built, distance to nearest school. For text, you might use bag-of-words (word counts), TF-IDF weights, or (in modern systems) the output of a pretrained transformer encoder.

Labels (in supervised learning) are the target outputs y. They can be:

  • Binary (0/1): spam or not
  • Multiclass (0,1,...,k-1): which digit (0-9)
  • Multilabel: a document can belong to multiple categories simultaneously
  • Real-valued: price, temperature
  • Structured: a parse tree, a translation sentence, a bounding box

Feature engineering is the craft of transforming raw inputs into features that make the learning problem easier. Examples:

  • Log-transforming income (right-skewed) to make it closer to Gaussian
  • Creating an interaction feature bedrooms × bathrooms
  • Extracting day-of-week from a timestamp
  • Using domain knowledge to compute physical features (e.g., body-mass index from height and weight)

Deep learning partially automates feature engineering by learning hierarchical representations: a CNN's early layers learn edge detectors, middle layers learn textures and parts, and late layers learn object-level features — all from raw pixels. This is representation learning: letting the model discover its own features.

The curse of dimensionality (Bellman 1961) is the central challenge of high-dimensional feature spaces. In d dimensions, the volume of the space grows as Rᵈ, so any fixed number of training points becomes exponentially sparse as d grows. Nearest-neighbor methods degrade catastrophically; distances become meaningless (all points are roughly equidistant). This motivates dimensionality reduction and the use of models with appropriate inductive biases (sparsity, locality, compositionality).

import numpy as np
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression

# Feature engineering pipeline: numeric + categorical features together
# Suppose X has columns: [age (numeric), income (numeric), city (categorical)]
X_raw = np.array([
    [25, 50000, 'NYC'],
    [35, 80000, 'LA'],
    [28, 60000, 'NYC'],
    [45, 120000, 'Chicago'],
])
y = np.array([200, 350, 240, 500])  # house price (thousands)

preprocessor = ColumnTransformer(transformers=[
    ('num', StandardScaler(), [0, 1]),         # z-score numeric features
    ('cat', OneHotEncoder(), [2]),             # one-hot encode city
])

pipeline = Pipeline([('prep', preprocessor), ('model', LinearRegression())])
# In real usage: pipeline.fit(X_train, y_train); pipeline.predict(X_test)

06Generalization: The Real Goal of Machine Learning

The most important concept in all of ML is generalization — the ability of a learned model to perform well on data it has never seen. It is emphatically not performance on training data.

This is where the statistical foundation of ML lives. We assume our training data D = {(x₁,y₁),...,(xₙ,yₙ)} is drawn i.i.d. (independently and identically distributed) from some unknown distribution P(X,Y). We want to minimize the true risk (expected loss over P), but we can only observe and optimize the empirical risk (average loss over the training set).

True risk: R(h) = E_{(x,y)~P}[L(h(x), y)] Empirical risk: R̂(h) = (1/n) Σᵢ L(h(xᵢ), yᵢ)

Overfitting occurs when a model achieves very low empirical risk but high true risk — it has memorized the training set's idiosyncrasies (including noise) rather than learning the underlying pattern. A degree-100 polynomial through 10 noisy points passes through every point exactly (training loss = 0) but oscillates wildly between them.

Underfitting occurs when empirical risk is already high — the model is too simple to capture the signal. A linear model fit to data with a clear quadratic relationship will underfit.

The bias-variance decomposition (for MSE) makes this precise. For any point x:

E[(h(x) - y)²] = Bias²[h(x)] + Var[h(x)] + σ²

  • Bias: error due to wrong assumptions in the model family. High-bias models consistently miss the true pattern (underfitting).
  • Variance: error due to sensitivity to the specific training set. High-variance models change dramatically with small changes in training data (overfitting).
  • Irreducible noise σ²: noise in the data-generating process that no model can remove.

Increasing model complexity typically decreases bias but increases variance. The art of ML is finding the sweet spot — and techniques like regularization, dropout, early stopping, and data augmentation are all mechanisms for navigating this tradeoff.

The PAC (Probably Approximately Correct) learning framework makes generalization rigorous: given a hypothesis class H of VC dimension d, n training examples, and desired error tolerance ε and confidence 1-δ, the generalization bound is:

R(h) ≤ R̂(h) + O(√(d·log(n/d) + log(1/δ)) / n)

This tells you: more complex models (larger d) need more data to generalize. The gap between training and test performance is bounded, and the bound tightens with more data.

import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

np.random.seed(7)
x = np.linspace(0, 1, 30)
y = np.sin(2 * np.pi * x) + np.random.randn(30) * 0.3
X = x.reshape(-1, 1)

for deg in [1, 4, 15]:
    model = make_pipeline(PolynomialFeatures(deg), LinearRegression())
    model.fit(X, y)
    train_mse = mean_squared_error(y, model.predict(X))
    # Evaluate on fine grid (proxy for true risk)
    x_test = np.linspace(0, 1, 300).reshape(-1, 1)
    y_test_true = np.sin(2 * np.pi * x_test.ravel())
    test_mse = mean_squared_error(y_test_true, model.predict(x_test))
    print(f"degree={deg:2d}: train_MSE={train_mse:.4f}, approx_true_MSE={test_mse:.4f}")
# degree= 1: high train AND test MSE   -> underfitting (high bias)
# degree= 4: low train, low test MSE  -> good generalization
# degree=15: near-zero train MSE, high test MSE -> overfitting (high variance)

07Training, Validation, and Test Sets

One of the most operationally critical ideas in ML is the proper partitioning of data. The fundamental rule: never evaluate generalization on data used to make any modeling decision.

The standard split:

  • Training set (~60-70%): used to fit model parameters (e.g., weights in a neural network, split points in a decision tree).
  • Validation set (~15-20%): used to make modeling decisions — which hyperparameters to use, when to stop training, which architecture to pick. The key insight: if you optimize any decision using a dataset, you've used it as training data in a broader sense.
  • Test set (~15-20%): locked away until final evaluation. Used once, to report the model's expected performance on unseen data. Peeking at the test set — even to decide "which model is best" — is a form of data leakage that makes your test estimate optimistically biased.

K-fold cross-validation is the standard alternative when data is limited. Split data into k equal folds; train on k-1 folds and validate on the held-out fold; rotate k times; average the validation scores. This gives a low-variance estimate of generalization performance using all data for training.

Data leakage is the subtle pathology where information from the test set (or future data) contaminates training. Examples:

  • Fitting a StandardScaler on the full dataset before splitting (the scaler "knows" test-set statistics)
  • Including the target variable as a feature (trivially perfect predictions)
  • Splitting time-series data randomly instead of chronologically (the model sees future data)

Leakage is the number-one cause of models that look incredible in development but fail in production. The defense: always build preprocessing pipelines that are fit only on training data and applied to validation/test data.

The test set is a one-shot measurement instrument. Once used, it's contaminated. In competitions (Kaggle), public leaderboards use a portion of the test set, and final rankings use a held-out private portion — precisely because participants adapt to the public leaderboard.

from sklearn.model_selection import KFold, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import Ridge
from sklearn.datasets import load_diabetes
import numpy as np

X, y = load_diabetes(return_X_y=True)

# WRONG: fit scaler on all data before splitting — leakage!
# scaler = StandardScaler().fit(X)   # <-- sees test data!
# X_scaled = scaler.transform(X)

# CORRECT: put scaler inside a pipeline so it only fits on train folds
pipeline = Pipeline([
    ('scaler', StandardScaler()),   # fit only on training fold each time
    ('ridge', Ridge(alpha=1.0)),
])

kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(pipeline, X, y, cv=kf, scoring='r2')
print(f"5-fold CV R² scores: {np.round(scores, 3)}")
print(f"Mean ± Std: {scores.mean():.3f} ± {scores.std():.3f}")

08The ML Workflow: From Raw Data to Deployed Model

Real ML projects follow a cycle, not a linear path. Understanding the full workflow prevents the common mistake of jumping straight to "pick a model and train it."

Step 1 — Problem definition: What is the task T? What is the performance metric P? What is the business constraint (latency, interpretability, fairness)? Misdefining the task is the most expensive mistake.

Step 2 — Data collection and exploration: Gather data, understand its provenance, check for bias in collection. Exploratory Data Analysis (EDA): distributions, missing values, outliers, correlations, class imbalances. You cannot debug what you don't understand.

Step 3 — Data preprocessing: Handle missing values (imputation, removal), encode categoricals, scale numerics, remove duplicates. Build a reproducible pipeline.

Step 4 — Feature engineering: Transform raw inputs into informative features. Domain knowledge is invaluable here. For deep learning, this step merges with model design.

Step 5 — Model selection: Choose a hypothesis class appropriate to your data size, dimensionality, and task. Start simple: a logistic regression or gradient-boosted trees baseline is almost always worth building first. It establishes a performance floor and often surprises you.

Step 6 — Training and hyperparameter tuning: Fit model parameters on training data. Tune hyperparameters (learning rate, regularization strength, tree depth) on the validation set. Grid search, random search, or Bayesian optimization.

Step 7 — Evaluation: Report final performance on the held-out test set. Error analysis: where does the model fail? Why? This drives the next iteration.

Step 8 — Deployment and monitoring: Serving a model in production introduces a new failure mode: distribution shift. The real world changes; the distribution P(X,Y) at deployment time may differ from the training distribution. Monitor input statistics, output distributions, and performance metrics continuously.

The cycle repeats: insights from deployment (and failures) feed back into data collection and problem redefinition. ML is empirical science — the scientific method applied to building intelligent systems.

# Skeleton of a rigorous ML workflow
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report
from sklearn.datasets import make_classification
import numpy as np

# 1. Data (simulated here)
X, y = make_classification(n_samples=2000, n_features=20, n_informative=10, random_state=0)

# 2. Hold out test set FIRST — lock it away
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

# 3. Validation split from remaining
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.2, random_state=42, stratify=y_temp)

# 4. Build pipeline (scaler + model)
pipe = Pipeline([('sc', StandardScaler()), ('gb', GradientBoostingClassifier(random_state=0))])

# 5. Hyperparameter search on train+val via cross-validation
param_dist = {'gb__n_estimators': [100, 200], 'gb__max_depth': [3, 5], 'gb__learning_rate': [0.05, 0.1]}
search = RandomizedSearchCV(pipe, param_dist, n_iter=6, cv=3, scoring='roc_auc', random_state=0)
search.fit(X_train, y_train)

# 6. Final evaluation — test set used ONCE here
print(classification_report(y_test, search.best_estimator_.predict(X_test)))

09Loss Functions: The Language of Optimization

A loss function (or cost function) L(ŷ, y) quantifies how wrong a prediction ŷ is relative to the true label y. It is the scalar signal that drives all of parameter learning.

The choice of loss function is not arbitrary — it encodes your assumptions about the data-generating process and your tolerance for different kinds of errors.

Mean Squared Error (MSE): L = (1/n) Σ(yᵢ - ŷᵢ)² Used for regression. Minimizing MSE is equivalent to maximum likelihood estimation under a Gaussian noise model. It penalizes large errors quadratically, making it sensitive to outliers.

Mean Absolute Error (MAE): L = (1/n) Σ|yᵢ - ŷᵢ| Also regression. Corresponds to a Laplacian noise model. More robust to outliers than MSE (linear vs. quadratic penalty), but the gradient is discontinuous at zero.

Binary Cross-Entropy: L = -(1/n) Σ[yᵢ log(ŷᵢ) + (1-yᵢ) log(1-ŷᵢ)] Used for binary classification where ŷᵢ = σ(wᵀxᵢ) ∈ (0,1) is a probability. Minimizing this is equivalent to maximum likelihood under a Bernoulli model. Produces very strong gradients when the model is confidently wrong — good for fast learning.

Categorical Cross-Entropy (Softmax Loss): L = -(1/n) Σ Σₖ yᵢₖ log(ŷᵢₖ) Generalization to k classes. Used with softmax output layer in neural networks. The log ensures that being very confident and wrong incurs a very large loss.

Hinge Loss (SVM): L = (1/n) Σ max(0, 1 - yᵢ·ŷᵢ) for y ∈ {-1, +1} Encourages a margin between classes. Examples correctly classified with margin ≥ 1 contribute zero loss — only the "support vectors" near the margin matter.

The key insight: loss function = probabilistic model + link function. If you assume outputs are Gaussian → use MSE. Bernoulli → cross-entropy. Poisson → Poisson deviance. This connection (generalized linear models, maximum likelihood) gives you a principled way to choose loss functions for new problems.

import numpy as np

def mse(y_true, y_pred):
    return np.mean((y_true - y_pred) ** 2)

def binary_cross_entropy(y_true, y_pred):
    """y_pred: probabilities in (0,1); y_true: {0,1}"""
    eps = 1e-15  # numerical stability
    y_pred = np.clip(y_pred, eps, 1 - eps)
    return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))

# Demonstrate: BCE heavily penalizes confident wrong predictions
y = np.array([1, 1, 0, 0])
print("Uncertain predictions (p=0.5):",
      binary_cross_entropy(y, np.array([0.5, 0.5, 0.5, 0.5])))   # ~0.693
print("Confident, mostly right (p=0.9/0.1):",
      binary_cross_entropy(y, np.array([0.9, 0.9, 0.1, 0.1])))   # ~0.105
print("Confident, all wrong (p=0.1/0.9):",
      binary_cross_entropy(y, np.array([0.1, 0.1, 0.9, 0.9])))   # ~2.303

10The Inductive Bias and No Free Lunch

Every ML algorithm embeds assumptions about the world. These assumptions — which are not derived from data but are built into the algorithm's design — are called its inductive bias. Without inductive bias, no learning is possible.

Consider the simplest possible algorithm: memorize the training set (nearest neighbor with k=1). It makes no inductive bias beyond "similar inputs have similar outputs" under some distance metric. But the choice of distance metric is itself an inductive bias.

Linear regression assumes the target is a linear function of the features. Decision trees assume the decision boundary can be expressed as axis-aligned hyperplane cuts. Gaussian Naive Bayes assumes features are conditionally independent given the class and Gaussian-distributed. Convolutional neural networks assume that useful patterns in images are translation-invariant and locally structured.

None of these assumptions are universally true. They are useful fictions that enable generalization when the true data distribution approximately matches the assumption.

The No Free Lunch theorem (Wolpert & Macready, 1997) formalizes this: averaged over all possible data-generating distributions, every learning algorithm performs identically. No algorithm is universally superior. What makes an algorithm "good" is not an absolute property but a match between its inductive bias and the true structure of the problem.

The practical implication: choosing a model requires domain knowledge. For images, use architectures that exploit spatial structure (CNNs). For sequences, exploit temporal structure (RNNs, transformers). For tabular data with complex nonlinear interactions and mixed feature types, gradient-boosted trees often win. For small data with prior knowledge, Bayesian models. Trying everything and cross-validating is a legitimate strategy — but it works best when you start with models whose biases match your domain intuitions.

This is why feature engineering is valuable even in the deep learning era: encoding your domain knowledge as features tells the model "this structure matters", which is another form of inductive bias.

# Demonstrating inductive bias: same data, different models, different generalizations
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor

np.random.seed(1)
x = np.sort(np.random.rand(30))
y = np.sin(4 * np.pi * x) + np.random.randn(30) * 0.2
X = x.reshape(-1, 1)

models = [
    ("Linear (high bias)",     LinearRegression()),
    ("KNN k=1 (high variance)", KNeighborsRegressor(n_neighbors=1)),
    ("KNN k=5 (balanced)",     KNeighborsRegressor(n_neighbors=5)),
    ("Deep tree (high var)",   DecisionTreeRegressor(max_depth=None)),
]

x_test = np.linspace(0, 1, 200).reshape(-1, 1)
y_true = np.sin(4 * np.pi * x_test.ravel())

for name, m in models:
    m.fit(X, y)
    mse = np.mean((m.predict(x_test) - y_true)**2)
    print(f"{name:30s}: approx true MSE = {mse:.4f}")

Key insights

  • ✦Generalization — not training accuracy — is the only thing that matters. A model with 100% training accuracy and 60% test accuracy has learned nothing useful.
  • ✦The bias-variance tradeoff is not a dilemma to solve but a tension to navigate: regularization, more data, and better priors all shift the tradeoff favorably.
  • ✦Every loss function encodes a probabilistic model. Choosing MSE assumes Gaussian noise; choosing cross-entropy assumes Bernoulli outputs. Match your loss to your data-generating process.
  • ✦No Free Lunch: no algorithm dominates all others across all problems. Domain knowledge, expressed as inductive bias, is what makes algorithms work in practice.
  • ✦Data leakage is the silent killer of ML projects. Any information from the test set — including preprocessing statistics — that enters the training pipeline invalidates your evaluation.
  • ✦The three learning paradigms differ in their supervision signal: supervised (explicit labels), unsupervised (structure in raw data), reinforcement (scalar reward from environment interaction).
  • ✦The train/validation/test split is not bureaucracy — it is the only valid way to estimate generalization. The test set is a one-shot instrument; using it multiple times makes it a second validation set.
  • ✦Deep learning partially automates feature engineering through representation learning, but does not eliminate the need to understand what features are and why they matter.

Common pitfalls

  • ⚠Optimizing training accuracy instead of validation/test accuracy — the training set is just a tool, not the goal. Always report held-out performance.
  • ⚠Data leakage through preprocessing: fitting scalers, encoders, or imputers on the full dataset before splitting. Always fit preprocessing inside a pipeline on training data only.
  • ⚠Evaluating a classifier only by accuracy on imbalanced datasets (99% class-0 data: predict all 0s = 99% accuracy, but completely useless). Use precision/recall/F1/AUC.
  • ⚠Treating the test set as a validation set by iterating on model choice after seeing test results. This is p-hacking for ML: your reported test performance is now optimistically biased.
  • ⚠Randomly splitting time-series data, allowing the model to use future information to predict the past. Always split time-series chronologically.
  • ⚠Conflating correlation with causation in features: a model can learn a spurious correlation (hospital patients are sicker, so hospitalizations 'cause' death) that works on historical data but fails under distribution shift.
  • ⚠Choosing model complexity without a validation set, then blaming 'the algorithm' when it overfits. Regularization and early stopping are not optional hacks — they are fundamental tools.
  • ⚠Ignoring class imbalance: training a fraud detector on 0.1% positive data without resampling, reweighting, or using appropriate metrics leads to a model that never predicts fraud.

Practice

  1. 01
    medium

    Implement k-fold cross-validation from scratch (no sklearn) for a simple regression model. Given a dataset, an integer k, and a model with fit/predict methods, return the mean and standard deviation of MSE across folds. Verify your implementation matches sklearn's cross_val_score on the same data.

  2. 02
    hard

    Bias-variance decomposition by simulation: generate 100 different training sets of size 20 from the function f(x) = sin(2πx) + noise. For each training set, fit polynomial models of degree 1, 3, and 10. At x=0.5, compute the bias² (squared difference between average prediction and true value) and variance (variance of predictions across the 100 models). Verify that bias²+variance ≈ total expected MSE.

  3. 03
    hard

    Implement a minimal Q-learning agent that solves a 5x5 grid world with a single goal state and several wall obstacles. The agent must learn a policy that reaches the goal in the minimum expected number of steps from any start state. Plot the learned value function V(s) = max_a Q(s,a) as a heatmap.

  4. 04
    easy

    Demonstrate data leakage concretely: take any dataset (e.g., sklearn's breast cancer), fit a StandardScaler on the FULL dataset (train+test), then split and evaluate. Compare to the correct pipeline where the scaler is fit only on training data. Show that the leaky version reports artificially optimistic test accuracy.

  5. 05
    hard

    Prove the following: if we have n training points drawn i.i.d. from P(X,Y) and we pick the hypothesis h* that minimizes training error from a finite hypothesis class H of size |H|, then with probability at least 1-δ, the true error of h* is at most ε_train(h*) + √(ln|H| + ln(1/δ)) / (2n). This is the finite hypothesis class generalization bound. Work through the union bound + Hoeffding's inequality derivation.

  6. 06
    medium

    Build a complete supervised ML pipeline for a binary classification problem of your choice: data loading, EDA (at minimum: check class balance, plot feature distributions, check for missing values), preprocessing pipeline, model selection via cross-validation among at least 3 model families, final test-set evaluation with a full classification report and ROC curve.

Resources

📕Pattern Recognition and Machine Learning
bookpaidChristopher M. Bishop, Springer (2006). The gold standard for probabilistic ML theory. Chapters 1-3 are essential for this module.

Available as free PDF from the author's Microsoft Research page. Dense but rigorous.

📕The Elements of Statistical Learning
bookfree

Hastie, Tibshirani, Friedman. Free PDF from Stanford. Chapter 2 (Overview of Supervised Learning) and Chapter 7 (Model Assessment and Selection) are directly relevant.

🎓CS229: Machine Learning — Stanford
coursefree

Andrew Ng's graduate ML course. Lecture notes are exceptionally clear on supervised learning, generalization theory, and the ML workflow.

🎓6.036: Introduction to Machine Learning — MIT OCW
coursefree

MIT's undergraduate ML course. Excellent on features, loss functions, and the training-generalization framework.

📕Reinforcement Learning: An Introduction (2nd ed.)
bookfree

Sutton & Barto. The definitive RL textbook. Chapters 1-3 cover the MDP framework and Q-learning introduced here.

📕Understanding Machine Learning: From Theory to Algorithms
bookfree

Shalev-Shwartz & Ben-David. Free PDF. Best rigorous treatment of PAC learning, VC dimension, and generalization bounds at the undergraduate level.

✎A Few Useful Things to Know About Machine Learning
articlefree

Pedro Domingos, CACM 2012. A dense, wise survey of ML folk wisdom — inductive bias, generalization, the curse of dimensionality, feature engineering. Essential reading.

✎Machine Learning Mastery — Cross-Validation Tutorial
articlefreemachinelearningmastery.com by Jason Brownlee. Search 'k-fold cross-validation python'.

Practical, code-first tutorials that complement the theoretical treatment here.

📄Scikit-learn User Guide
docsfree

The canonical reference for sklearn APIs used throughout this module. The section on model selection and evaluation is especially relevant.

✎Bias-Variance Tradeoff — Wikipedia
articlefree

Good mathematical summary with the formal decomposition. Read after working through the concept here for reinforcement.

Checkpoints — you can now…

  • ✓You can precisely state Mitchell's definition of learning and identify the task T, experience E, and performance measure P for any given ML problem.
  • ✓You can explain the difference between supervised, unsupervised, and reinforcement learning — including what the training signal is in each case and give two real-world examples of each.
  • ✓You can define overfitting and underfitting, explain the bias-variance decomposition, and describe at least three techniques that address each problem.
  • ✓You can explain why data leakage invalidates evaluation, identify three common forms of leakage, and build a sklearn Pipeline that prevents leakage by design.
  • ✓You can derive why binary cross-entropy loss corresponds to maximum likelihood estimation under a Bernoulli model, and state which probabilistic model MSE loss corresponds to.
  • ✓You can state the No Free Lunch theorem and explain its practical implication: that the right model choice depends on domain-specific inductive bias, not universal superiority.
  • ✓You can design a proper train/validation/test split strategy for both i.i.d. data and time-series data, explain why each is necessary, and implement k-fold cross-validation from scratch.
  • ✓You can walk through the full ML workflow — from problem definition through deployment monitoring — and identify the key decision point and failure mode at each stage.