Classic machine learning, step by step
Classic machine learning on 2-D data you can edit — a decision tree growing split by split with its best-split search, a random forest's bootstrap trees and vote map, gradient boosting round by round, k-NN, logistic regression, a soft-margin linear SVM and Gaussian naive Bayes side by side — and an optimiser lab racing SGD, momentum, Nesterov, AdaGrad, RMSProp, Adam and AdamW over Rosenbrock, Beale, a saddle and an ill-conditioned bowl with learning-rate schedules.
Added
How to use it
Two classes of points on a plane, and the classic models drawing their decision boundaries over them. Every model is computed exactly, in this page, and checked against scikit-learn. Every setting can be put in a link — see Linking to an example.
The data
Each classifier tab has the same Data panel: a dataset (two moons, circles, blobs, XOR, a spiral or a linear split), the number of points, the noise, and New sample for another random draw (the seed). 30 % of the points are held out as a test set: filled points train the model, hollow points only test it, so every tab shows a train and a test accuracy and overfitting is something you can see. The plot’s mode:
-
Probe — hover (or tap) to read the model’s probability of class 1 at that spot.
-
+ ● blue / + ▲ red — tap to add a training point of that class (outlined).
-
Erase — tap a point to remove it. Clear edits brings the original sample back.
Shading shows the predicted class and how sure the model is; the line is the decision boundary, where p = ½.
Decision tree
CART as scikit-learn grows it: at each node, try every threshold halfway between neighbouring values of each feature, score each by the drop in impurity (Gini, or entropy in bits), keep the best, and stop at the maximum depth, below min samples to split, when a child would get fewer than min samples per leaf, or when the node is pure. Next ▶ makes one split at a time (breadth first), ◀ undoes one, Play animates the growth and ⏭ Grow finishes it. The dashed box on the plot is the node about to be split, with its chosen line; the chart under it plots every candidate threshold for x₁ and x₂ against its impurity decrease, with the winner ringed, and the diagram draws the tree so far: the split, impurity, size and class counts of each node (up to six levels). The XOR example shows the greedy search’s blind spot: no single split lowers the impurity much, but two levels solve it.
Random forest
Many deep trees, each grown on a bootstrap sample (n points drawn with replacement, so about 63 % distinct) and choosing each split from a random subset of the features — with two features, scikit-learn’s default √p means one at random per split. The forest averages the trees' class probabilities: the main plot is that vote share. Small multiples show the first eight trees with their own samples (big dots were drawn several times, faint ones are out of bag); tap one, or use Show, to see it large. The out-of-bag error judges every training point only by the trees that never saw it — a free estimate of the test error.
Gradient boosting
Friedman’s gradient boosting for log loss, as scikit-learn’s GradientBoostingClassifier: the score starts at the log-odds of the class share, F₀ = log(p/(1 − p)); each round fits a small regression tree to the pseudo-residuals y − p and gives each leaf the Newton step Σr / Σp(1 − p), added after shrinking by the learning rate. Step through the rounds: point size shows how wrong the model still is at each training point, the small plot shows the tree added that round (red pushes towards class 1, blue towards 0), and the chart shows train and test log loss by round, with the best test round ringed — more rounds keep lowering the training loss long after the test loss has turned.
k-NN, logistic regression, linear SVM, naive Bayes
-
k-NN: k, the distance (Euclidean, Manhattan, Chebyshev) and equal or 1/distance votes. Probe a point to see its k neighbours, the ball that holds them (a circle, a diamond or a square, depending on the distance) and a table of their distances. k = 1 is perfect on the training set and ragged on the test set.
-
Logistic regression: on every monomial up to the degree (degree 2 can draw a circle), with L2 regularisation. Fitted exactly by Newton’s method on scikit-learn’s objective ½‖w‖² + C Σ log loss (intercept unpenalised). Shows the coefficients. For the one-feature version and its cross-entropy bowl, see the maths of machine learning.
-
Linear SVM: the soft margin. Minimises ½‖w‖² + C Σ max(0, 1 − y(w·x + b)), solved in the dual by SMO with LIBSVM’s working-set selection; draws the boundary, the margin lines ±1, the support vectors (thick ring: αᵢ = C, inside or beyond its margin) and reports the margin width 2/‖w‖. Small C: wide margin, many violations; large C: narrow margin.
-
Naive Bayes: one Gaussian per feature per class (scikit-learn’s GaussianNB, var_smoothing 10⁻⁹), drawn as axis-aligned ellipses at 1σ and 2σ, with the fitted means, variances and priors. Untick the class-share prior to move the boundary with the prior slider.
Compare draws all seven models, each with its own tab’s settings, on the current data, with their train and test accuracies. Tap one to open its tab.
Optimiser lab
Seven optimisers race down a loss surface from the same start. Tap the plot to move the start.
-
Surfaces: Rosenbrock’s banana valley, (1 − x)² + 100(y − x²)², minimum at (1, 1); Beale’s function, minimum at (3, ½); a saddle, x² − y², with no minimum (start just off the ridge and watch who escapes); an ill-conditioned quadratic ½(u² + κv²) turned 30°, where plain gradient descent diverges once the learning rate passes 2/κ; and Himmelblau’s function with four minima.
-
Optimisers, exactly as PyTorch’s
torch.optimdefines them: SGD; momentum (b ← μb + g, step lr·b); Nesterov (step lr·(g + μb)); AdaGrad; RMSProp (α 0.99); Adam (bias-corrected moments, β₁, β₂); AdamW (Adam with decoupled weight decay, p ← p(1 − lr·λ) first). Tick the ones to run; each has its own learning rate (Suggested rates restores the defaults for the surface). -
Schedules: constant; step (halve every quarter of the run); cosine annealing; linear warm-up over the first 10 % then cosine; and one-cycle (PyTorch’s OneCycleLR: from lr/25 up to lr at 30 % of the run, then down to lr/250 000, cosine-shaped). The small chart plots the multiplier.
-
Hyperparameters: momentum μ, β₁, β₂, AdamW’s decay λ and gradient noise σ, Gaussian noise added to every gradient (seeded) to mimic mini-batch SGD.
The paths animate together (Play, Restart, speed, or drag the Step slider); the chart plots f − f_min on a log scale for each, and the table gives each optimiser’s loss and distance to the nearest minimum at the current step.
Full screen
⛶ Full screen (or the F key) fills the screen with the tool: the plot grows to fill the space and the side panel can be hidden with Hide panel so the plot goes edge to edge. Escape (or the button) leaves. Where the browser has no full-screen mode (iPhone) the tool covers the window instead.
Limits
-
Two features and two classes only, so everything can be drawn. Up to 400 generated points plus your own.
-
The models are exact for small data but not fast: random forests of 100 deep trees or k-NN with many points take a moment to redraw.
-
The SVM is linear only (no kernels) and stops at LIBSVM’s tolerance (10⁻³), so its w and b agree with scikit-learn’s to about three decimals. The SVM shading is a squashed margin, not a probability.
-
The random forest uses its own seeded random numbers, so its trees are not scikit-learn’s for the same data (the method is the same). Ties between equally good splits go to the first found.
-
The optimisers run on exact gradients of two-variable functions; real training has millions of parameters and noisy gradients, so treat the race as intuition, not a benchmark.
Tested against scikit-learn 1.9.1 on a fixed 40-point dataset: the decision tree’s root split (feature, threshold, impurity), all node impurities, probabilities and feature importances for Gini and entropy; KNeighborsClassifier probabilities for four settings; GaussianNB means, variances, ε and probabilities; LogisticRegression coefficients (raw and degree-2 features); SVC(kernel="linear") w, b, support-vector counts and accuracy for C = 0.1, 1, 10; and GradientBoostingClassifier probabilities and training deviance. The optimiser steps are checked against the update formulas in the PyTorch documentation and the schedules against their definitions.
Sources: L. Breiman, J. Friedman, R. Olshen, C. Stone, Classification and Regression Trees (1984); L. Breiman, Random forests (2001); J. H. Friedman, Greedy function approximation: a gradient boosting machine (2001); J. Platt, Sequential minimal optimization (1998) and R.-E. Fan, P.-H. Chen, C.-J. Lin, Working set selection using second order information (2005); S. Shalev-Shwartz et al., Pegasos (2007), for the primal view of the same hinge loss; Y. Nesterov (1983) and I. Sutskever et al. (2013) for momentum; J. Duchi, E. Hazan, Y. Singer, AdaGrad (2011); G. Hinton, Lecture 6e of Neural Networks for Machine Learning (RMSProp, 2012); D. Kingma, J. Ba, Adam (2015); I. Loshchilov, F. Hutter, Decoupled weight decay regularization (AdamW, 2019) and SGDR (cosine annealing, 2017); L. Smith, Super-convergence (one-cycle, 2018); scikit-learn’s user guide and the torch.optim documentation.
Related: Neural network designer (the next step: train a network on the same kinds of data), The maths of machine learning (least squares, gradient descent, logistic regression in one dimension, backpropagation), Model evaluation (confusion matrices, ROC and PR curves, calibration), Embedding explorer, PCA and SVD and Statistics.
Linking to an example
A link opens the tool with the given settings: /tools/ml-visualiser/?<parameters>#<tab>, where
the tab is tree, forest, boost, knn, logreg, svm, bayes, compare or optim.
Share link (top right) builds one from whatever is on screen; points you added or erased are
packed into the link too.
| Parameter | Meaning | Example |
|---|---|---|
|
a preset (list below); other parameters in the same link override it |
|
|
dataset: |
|
|
number of generated points (20–400) |
|
|
Gaussian noise added to each coordinate |
|
|
random sample (also seeds the forest and the gradient noise) |
|
|
tree impurity: |
|
|
tree maximum depth |
|
|
tree minimum samples to split |
|
|
tree minimum samples per leaf |
|
|
splits made so far (999 = the whole tree) |
|
|
forest size |
|
|
forest trees' maximum depth |
|
|
bootstrap samples ( |
|
|
features tried per split: |
|
|
forest tree shown (0 = the vote) |
|
|
boosting rounds |
|
|
boosting learning rate |
|
|
boosting tree depth |
|
|
boosting round shown |
|
|
k-NN neighbours |
|
|
k-NN distance: |
|
|
k-NN votes: |
|
|
logistic regression polynomial degree (1–6) |
|
|
logistic regression C |
|
|
SVM C |
|
|
naive Bayes P(class 1); −1 = the class share |
|
|
optimiser surface: |
|
|
quadratic’s condition number κ |
|
|
optimiser steps |
|
|
schedule: |
|
|
optimisers run: any of |
|
|
learning rates that differ from the suggested ones |
|
|
momentum μ, β₁, β₂, AdamW decay λ, gradient noise σ |
|
|
start point |
|
Presets for example=:
| Name | Tab | Shows |
|---|---|---|
|
tree |
A depth-4 tree on two moons, three splits in |
|
tree |
XOR: no single split helps, yet two levels solve it |
|
tree |
A full-depth entropy tree memorising noisy moons |
|
forest |
25 bootstrapped trees voting on noisy moons |
|
boost |
Boosting stumps into a circle, round by round |
|
knn |
k = 1 on noisy data: islands around every point |
|
logreg |
Circles: a straight line fails, degree 2 succeeds |
|
svm |
Soft margin with C = 0.1: a wide margin with violations |
|
bayes |
Gaussian naive Bayes on two blobs |
|
optim |
Rosenbrock’s valley: SGD, momentum, RMSProp and Adam |
|
optim |
A saddle point: plain SGD lingers, adaptive methods escape |
|
optim |
κ = 50: SGD zig-zags, momentum and Nesterov cut across |
|
optim |
The one-cycle schedule on Beale’s function |
For example, https://geekychris.github.io/tools/ml-visualiser/?example=tree-xor#tree opens the XOR tree, and https://geekychris.github.io/tools/ml-visualiser/?surf=quadratic&kappa=80&on=sgd,momentum&steps=300#optim an even worse-conditioned bowl.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.