ML Atlas

Machine learning from zero · Lesson 2.1 · 8 of 33 lessons ready · 10 min

Classification: drawing a boundary

In short

A classifier turns measurements into a category. Logistic regression gives a probability and a boundary; the lesson shows both on two penguin species.

Warm-up Two questions from earlier lessons. A minute, and it never blocks the lesson.
From lesson 1.6: Gradient descent: learning by small steps Why is gradient descent used for neural networks instead of an exact formula?

Show the answer

Correct answer: Because there is usually no formula for the best parameters, but a gradient can be computed

Gradient descent needs only the slope of the loss at the current point, which is available even for models with billions of parameters.

From lesson 1.5: Linear regression: fitting a line Why does linear regression square the residuals instead of just adding them up?

Show the answer

Correct answer: So that misses in both directions cannot cancel, and big ones cost more

Squares are always positive and grow quickly, so the score reflects every miss and punishes large ones heavily.

A biologist in Antarctica measures a penguin's bill and flippers. Which species is it? This is a different kind of question from "how much will the disease progress?". The answer is not a number on a scale but a choice between categories. Can the straight lines and downhill steps of the previous lessons handle that? They can, with one clever twist.

Predict, then check The model tells Adelie penguins from Chinstrap penguins, and the default decision threshold is 0.5. Which threshold on the slider gives the highest accuracy: 0.1, 0.3, 0.5 or 0.9?
How sure are you?

Interactive lab

This interactive lab runs in a browser with JavaScript enabled. The text around it describes what the lab shows.

Show the answer

Correct answer: 0.3

At threshold 0.3 the accuracy is 98.6% (3 mistakes), at 0.5 only 96.8% (7 mistakes), at 0.9 92.2% and at 0.1 90.4%. 0.5 is not sacred: the best threshold depends on the class proportions and on which mistake costs more.

Try this

  • Look at the boundary first. Find the penguins that sit on the wrong side of it: these are the cases the model gets wrong even with its best line.
  • Move the probability threshold slider towards one end and watch the boundary shift towards one species. Notice that the line moves but keeps its direction.
  • Set the threshold near one extreme and count how the mistakes change: one species is now almost never missed, at the cost of the other.
  • Find penguins close to the boundary and compare them with penguins far away. Which ones would you double-check by hand?

Now that you've tried it, let's see what was actually going on.

The idea in plain words

Predicting a category is called classification. In this lesson we use the Palmer Penguins dataset: 344 penguins of three species from islands near Palmer Station in Antarctica, each with bill, flipper and body measurements. To keep things flat, we take two species and two measurements, so every penguin is a point on a plane — one measurement across, the other up — with a known species.

A classifier has to split that plane into two regions: "species A here, species B there". The line between the regions is the decision boundary. Logistic regression, despite its name, is a classification model, and its boundary is a straight line.

The twist is that logistic regression does not answer just "A" or "B". It answers with a probability: "this penguin is species B with probability 0.9". Think of a weather forecast. The forecaster says "80% chance of rain", and you decide whether that is enough to take an umbrella. The model plays the forecaster; the threshold is your umbrella rule.

Why it works

First the model computes a score just like linear regression does: a weighted sum of the features plus an intercept, z = w₁x₁ + w₂x₂ + b. That score can be any number, so it is squeezed into the range from 0 to 1 by the sigmoid function:

p = 1 / (1 + e^(−z))

A large positive score gives a probability close to 1, a large negative score gives a probability close to 0, and a score of exactly zero gives 0.5. Penguins far from the boundary get confident probabilities; penguins near it get probabilities close to one half — the model is unsure, and it says so.

The weights are learned with gradient descent, as in lesson 4, but with a different loss. Instead of squared error the model uses log loss (also called cross-entropy). It barely punishes a confident correct answer and punishes a confident wrong answer very hard. This pushes the model to be sure only when the data justify it.

Finally, the threshold turns the probability into a decision: probability above the threshold → species B, below → species A. The usual default is 0.5, but nothing forces it. Moving the threshold moves the boundary. Because the probability depends on the weighted sum, every threshold corresponds to a line with the same direction, just shifted: the model's opinion about which way the species differ stays the same, only your demand for certainty changes.

Common mistake

Two errors are common. The first is treating 0.5 as a law. The threshold is a decision about costs, not part of the model; in medicine you may accept many false alarms to miss fewer cancers. The second is reading the probability as certainty. A probability of 0.97 means the model expects to be wrong in about three such cases out of a hundred — and only if the new data resemble the training data. A penguin from a population the model never saw can get a confident and wrong answer.

A boundary that gets most penguins right sounds good, but "most" hides a lot: which mistakes, how many, and how bad? In the next lesson we open the box of mistakes and learn to count them properly.

Quiz

Three questions to check yourself. Pick an answer, or open the answer under a question. Keyboard: arrow keys choose, Enter or Space confirms.

1. What does logistic regression output before any threshold is applied?
Show the answer

Correct answer: A number from 0 to 1 — the probability of one of the classes

The sigmoid turns the weighted sum into a probability, and the threshold turns that probability into a decision.

2. You raise the threshold from 0.5 to 0.8. What happens to the boundary?
Show the answer

Correct answer: It shifts parallel to itself, so fewer penguins are classed as species B

Each threshold corresponds to a line with the same direction; a stricter threshold shifts it so that only confident cases pass.

3. Why does logistic regression use log loss instead of squared error?
Show the answer

Correct answer: Because it punishes confident wrong answers heavily, which suits probabilities

Log loss grows steeply as the probability given to the true class approaches zero, discouraging unjustified confidence.

Key takeaways

  • Classification predicts a category; the decision boundary separates the regions of each class.
  • Logistic regression squeezes a weighted sum through the sigmoid to get a probability.
  • The threshold turns a probability into a decision and is a choice about costs, not a fixed 0.5.
  • Changing the threshold shifts a logistic regression boundary without rotating it.

Going further: formula, code, practice

The math

The model computes a score z = w·x + b and turns it into a probability with the sigmoid, p = 1 / (1 + e^(−z)). It is trained by minimising the log loss L = −mean( y·ln p + (1 − y)·ln(1 − p) ), whose gradient is simply mean((p − y)·x) for each weight.

  • The prediction is class 1 when p ≥ t. Because the sigmoid is increasing, that is the same as z ≥ ln(t / (1 − t)).
  • For t = 0.5 the right side is 0, so the boundary is the line w·x + b = 0. For t = 0.3 it is −0.85: the same direction, shifted towards class 0.
  • w sets the direction of the boundary. t only slides it.
  • A confident wrong answer costs a lot under log loss, since −ln p grows without limit as p goes to 0.
In code (Python)

The lab's model: Adelie against Chinstrap by bill length and bill depth, standardised, scored on the same penguins at four thresholds:

import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

df = pd.read_csv("data/raw/penguins.csv")
df = df[df["species"].isin(["Adelie", "Chinstrap"])].dropna(subset=["bill_length_mm", "bill_depth_mm"])
X, y = df[["bill_length_mm", "bill_depth_mm"]], df["species"] == "Chinstrap"
model = make_pipeline(StandardScaler(), LogisticRegression()).fit(X, y)
p = model.predict_proba(X)[:, 1]  # probability of Chinstrap
for t in [0.1, 0.3, 0.5, 0.9]:
    wrong = int(((p >= t) != y).sum())  # call Chinstrap when p >= t
    print(f"threshold {t}: {wrong} mistakes, accuracy {1 - wrong / len(y):.1%}")

Output

threshold 0.1: 21 mistakes, accuracy 90.4%
threshold 0.3: 3 mistakes, accuracy 98.6%
threshold 0.5: 7 mistakes, accuracy 96.8%
threshold 0.9: 17 mistakes, accuracy 92.2%

The notebook linked under the output fetches the data itself. The best threshold here is 0.3, not the default 0.5.

For practitioners
  • predict() always uses 0.5. To use another threshold, take predict_proba and compare it yourself, as above, or wrap the model in FixedThresholdClassifier (a fixed threshold) or TunedThresholdClassifierCV (a threshold chosen by cross-validation), both in scikit-learn 1.5 or newer.
  • Choose the threshold on validation data, not on the rows you report. The 98.6% here is measured on the same penguins the model was fitted on, so it is optimistic.
  • Standardise the features. LogisticRegression applies L2 regularisation by default (C=1.0), and that penalty depends on the scale of each feature.
  • Compare accuracy with the majority-class baseline, and when the classes are unbalanced or the costs differ, look at the two kinds of mistake separately.
  • A probability is only as good as the data it was fitted on. For an animal from a different population, a confident answer can still be wrong.

Go deeper in the Atlas

Encyclopedia entries on the ideas in this lesson: