← Part of Lysen · lysen.org
Lysen ML
← All algorithms Supervised · classification

Logistic regression

The problem it solves: linear regression predicts a number. Logistic regression predicts which of two groups something belongs to. It does this by taking the exact same straight line you already built, and squashing its output into a probability between 0 and 1. Same line, same loop, same way of learning. The only new part is the squash.

What to read first: this chapter assumes you have been through linear regression. Everything about fitting a line, walking downhill and choosing a learning rate is reused here rather than repeated, and where that happens there is a link back to the exact section.

Used for pass or failspam or not spamwill this customer churncredit decisionsmedical triage
Learning typeSupervised
Primary taskClassification
Also doesNothing else
MechanismLinear · gradient descent

Running example for the whole chapter: hours studied against whether a student passed the exam. Every point is one student, where x = hours studied and y = 1 for a pass, 0 for a fail. There is nothing in between, which is already different from house prices.

01

Intuition: keep the line, fix its output

1a Why the old line breaks

We have students. For each one we know how many hours they studied, and whether they passed. Passing is 1, failing is 0. There is no in between, which is already different from house prices.

Try fitting a straight line to it like we did before.

passed (1) failed (0) your line impossible answers
…predicts at 18 hours …predicts at 1 hour …students given an impossible answer
Move the sliders, or press the button to fit the best line by RMSE, exactly as in the previous chapter.

A prediction of 1.4 means nothing. There is no outcome more passing than passing. And a negative probability is not a thing at all. So the straight line is producing answers that cannot exist.

We do not want to throw the line away. We want to keep it and fix its output.

1b The squash

This is the sigmoid. It takes any number, no matter how large or how negative, and squeezes it into the range between 0 and 1.

p = 1 / (1 + e−z) // any number in, a probability out

Feed it a big positive number and you get something close to 1. Feed it a big negative number and you get something close to 0. Feed it exactly 0 and you get exactly 0.5, right in the middle.

p = 0.500

Push the slider all the way to either end and watch the readout. At z = 8 the answer is 0.9997, at z = −8 it is 0.0003. Notice what it never does: it never quite reaches 1, and never quite reaches 0. The model is never fully certain. Hold onto that, it comes back in module 2.

1c And z is just the old line

Here is the part almost nothing explains properly, so we are going to be blunt about it.

z = w·x + b // the linear regression line, completely unchanged
p = 1 / (1 + e−z) // and then the squash

That is it. That is the same line from linear regression, unchanged. The sigmoid does not replace the line. It sits after the line and cleans up its output.

Left: z = w·x + b, the linear regression line

Right: p, the same line after the squash

Both graphs are driven by the same two numbers. Drag either slider and watch them move together. Hover a student, or use the follow slider, to trace one person across both.
passed failed z = 0 on the left, p = 0.5 on the right

Those two marks always sit at the same x, no matter how you drag. That x is the decision boundary. The place where the old line crosses zero is the place where the model becomes undecided.

Play with the two sliders and you can feel what each one does now:

  • w controls how sharp the flip is. Large w makes the curve almost a cliff, so the model goes from confident fail to confident pass over a very short stretch of hours. Small w makes it a gentle slope, so the model stays unsure across a wide range.
  • b slides the whole curve left and right. It moves where the flip happens without changing how sharp it is.
CuriousWhat does w being negative mean?

Everything above assumed more hours means more likely to pass. If w is negative the curve flips, and more of the thing means less likely. That is not an error, it is the model telling you the feature works in the opposite direction.

Set w below zero in the interactive above and watch the curve mirror itself. On this data it is obviously wrong, because studying does help. On a feature like "number of nights out before the exam" a negative weight would be exactly right, and reading that sign off the model is one of the most useful things logistic regression gives you.

02

The algorithm: every step of the loop

1initialize w and b // §2a
2repeat:
3 turn each student into z, then squash z into a probability p
4 measure how wrong those probabilities were // §2b
5 update w and b to be less wrong // §2c
6until the improvement gets small enough

If that looks familiar it is because it is the same loop as linear regression, with one extra line in the middle. That is the whole design.

2a Initialization

Same as before. Any starting values work, closer guesses just get there faster.

Already covered: Linear regression §2a, where you can watch three different starting lines walk to the same answer. Go there →

The one difference worth a sentence: starting both w and b at zero is genuinely fine here, and is what most libraries do. That gives every student a probability of exactly 0.5, which is the model honestly saying it has no idea yet.

2b Measuring how wrong we are

We cannot use RMSE anymore, and the reason is interesting enough that it gets its own tangent below. What we use instead is called log loss.

The idea is to score the model on how surprised it should have been.

  • The student passed, and the model said 0.9. Barely surprised. Tiny loss.
  • The student passed, and the model said 0.5. Fairly surprised. Medium loss.
  • The student passed, and the model said 0.02. The model was confident and it was wrong. Enormous loss.

So the rule is: take the probability the model gave to the outcome that actually happened, and punish it for being low. Written out, for a single student:

if the student passed: loss = −log(p)
if the student failed: loss = −log(1 − p)

Then average that over every student, exactly like we averaged the squared errors before.

loss = 0.105
…times worse than saying 0.9

Drag towards 0.01 with passed selected and watch the number explode. The curve is not symmetric around 0.5: being a bit wrong is cheap, being very confidently wrong is astronomically expensive. That asymmetry is the entire design of log loss, and it is what makes the model care about its worst answers rather than its average one.

This is why the sigmoid never touching 0 or 1 matters. If the model were allowed to say 1.0 and turned out to be wrong, the loss would be infinite. The squash protects it from ever making a claim it cannot survive being wrong about.

CuriousWhy not just use RMSE like before?

You could technically square the difference between p and the true label. It would even sort of work. But remember the mountain from the linear regression chapter, where we walked downhill to find the lowest point.

With RMSE on top of a sigmoid, the mountain stops being a nice bowl. It grows flat plateaus at the edges, and it is exactly the confidently wrong models that sit on those plateaus. On a flat plateau you cannot tell which way is down, so the model barely learns from its worst mistakes. That is the opposite of what you want.

Log loss is shaped so that the more wrong you are, the steeper the ground under you. The worse the mistake, the harder it pushes.

Squared error on top of the sigmoid

…distance travelled …log loss of the model it reached

Log loss

…distance travelled …log loss of the model it reached
Both marbles start at the same spot, on a model that is confidently wrong. Same data, same learning rate, same number of steps. The only difference is the shape of the ground under them.

2c Updating w and b

Identical to linear regression. Walk downhill, take steps sized by the learning rate, stop when steps stop helping.

Already covered: Linear regression §2c, the error landscape and the mountain you walk down. Go there →

But there is one thing worth showing rather than hiding, because it is a genuinely satisfying payoff. In linear regression, the update was driven by (prediction − actual) times the feature. In logistic regression, after all the calculus on the sigmoid and the log loss, the update is driven by (probability − actual) times the feature.

// linear regression
∂J/∂w = (1/n) Σ (ŷᵢ − yᵢ) · xᵢ

// logistic regression
∂J/∂w = (1/n) Σ (pᵢ − yᵢ) · xᵢ

It is the same formula. Not similar, the same. The mess of the sigmoid and the mess of the logarithm cancel each other out perfectly and leave you with the update rule you already know.

CuriousWhere does that cancellation come from?

For readers who know derivatives. The sigmoid has an unusually tidy derivative, and the derivative of the log loss contains its exact reciprocal. They meet in the chain rule and cancel:

∂p/∂z = p(1 − p) // the sigmoid's derivative
∂L/∂p = −y/p + (1−y)/(1−p) = (p − y) / [ p(1 − p) ] // the log loss's
∂L/∂z = ∂L/∂p · ∂p/∂z = (p − y) / [ p(1 − p) ] · p(1 − p) = p − y
∂L/∂w = (p − y) · x // and z = wx + b finishes it

That is not a coincidence and it is not luck. Log loss is the loss you get by asking which w and b make the data you actually observed most likely, and the sigmoid is the function that turns a line into those probabilities. They are two halves of the same construction, so their messes are built to cancel.

2d The full process

z = … · x + …
…log loss …accuracy at 0.5

Press New random start a few times. It lands in the same place regardless of where it starts, and the curve gets sharper as it grows more confident. That sharpening is the model deciding it is allowed to commit: early on it hedges near 0.5 everywhere, and by the end it is willing to say 0.95 for a student who studied fifteen hours.

2e Two features, and where the separating line comes from

Everything so far used one feature, so the boundary was a single point on the x axis. Real problems have more, so let us add hours slept.

z = w₁ · hours studied + w₂ · hours slept + b // same equation, one more term
passed failed the line where z = 0, so p = 0.5
…log loss …accuracy

Notice the shading has a soft edge, not a hard one. The model does not flip from fail to pass at the line, it fades through it. The line you see is exactly the place where the model says fifty percent, and nothing more special than that.

This is the picture most people are shown first, a line separating two clouds of points. It is correct, but it hides where the line comes from. The line is not drawn between the groups directly. It is the set of places where w·x + b happens to equal zero, which is the same as where the sigmoid happens to equal 0.5.

03

Evaluation: the cut is yours to choose

The model does not output pass or fail. It outputs a probability. Turning that probability into a decision means picking a cut, and that cut is yours to choose, not the model's.

0.5 is the obvious cut, and it is only the obvious one because the sigmoid is symmetric around it. It is not automatically the right one.

predicted correctly predicted wrongly your threshold
…wrongly told they will pass …wrongly told they will fail …accuracy
Drag the threshold up and down. You cannot get both counters to zero, because the two groups overlap.

Every step in one direction buys you fewer of one mistake and more of the other. That trade is not a bug in the model, it is the shape of the data: some students who studied a lot failed anyway, and no threshold can unpick that.

Which of those two mistakes is worse depends entirely on what the prediction is for, and no formula can tell you that. If it is used to offer extra tutoring, wrongly flagging a passing student costs an hour of someone's time. If it were used to decide who gets to sit the exam at all, the same mistake costs a person their exam. Same number, completely different weight.

The rest of the scoring lives on the shared evaluation and comparison page: the confusion matrix, accuracy, precision, recall and F1, used by KNN and decision trees too. This chapter only covers the threshold, because that is the part specific to models that output probabilities. Go there →
04

Failure modes, and how to counter each

4a The boundary is not a straight line

If one group sits in a ring around the other, no straight line will ever separate them. Same fix as linear regression: build new features out of the ones you have, and a line in the new space becomes a curve in the old one.

…accuracy …log loss
…

The algorithm did not change. Only what it was fed. The model is still drawing one straight boundary, it is just drawing it in a space where distance from the centre is one of the axes, and a straight line there is a circle back here.

4b One group is much rarer than the other

If 95 percent of students pass, a model that predicts pass for absolutely everyone is right 95 percent of the time and is completely useless. This is the single most common way a classifier fools the person who built it.

…accuracy …failing students it catches …accuracy of "everyone passes"
…

Slide it right and watch accuracy climb while the model gets less and less useful. At 99 percent the model catches almost nobody who is actually going to fail, and still scores 99. The counter: never quote accuracy on unbalanced data. Look at how many of the rare group you actually catch, weight the rare class more heavily when fitting, or resample so the classes are closer to even.

4c The groups separate perfectly

This one is counterintuitive. If a line splits the two groups cleanly with a gap between them, the model can always shrink its loss a bit further by making the curve steeper, and then steeper again, forever. Nothing stops it, and w runs away towards infinity.

The fix is regularization, the same lambda idea from linear regression, which adds a penalty for large weights and gives the runaway something to push back against.

Already covered: Linear regression §4b, where you can slide lambda and watch a model stop chasing its own tail. Go there →

4d Outliers, and one place this model is steadier

Worth a short comparison. In linear regression, one wild point could drag the whole line towards it, because squared error grows without limit. Here a single misplaced student can only ever contribute so much, because the sigmoid caps how wrong any one prediction can look. Logistic regression is genuinely steadier than linear regression on this one.

4e Confident far outside the data

Every student we have studied between 0 and 20 hours. Ask about someone who studied 60 and the model will happily say 99.99 percent. It has never seen anything like that and it will not tell you so.

4f The judgment point

The big one

A probability is not a verdict

When the model says 30 percent, it is not saying this student will fail. It is saying that among students who look like this one, roughly three in ten passed. That statement is about a group. The student in front of you is one person, and they are not obliged to match the average of a group they were sorted into by two numbers.

The failure is not statistical, it is what happens next. The moment a probability gets used to decide who receives help and who is written off, the model has stopped describing the world and started shaping it. Students predicted to fail get less attention, then fail more, and the model looks more correct every year.

05

What if there are more than two groups

Everything so far has been pass or fail. But plenty of questions have three or more answers: grade A, B or C, or which of four sports someone plays. Logistic regression only knows how to draw one boundary between two groups, so we build the multi-class version out of several two-group models.

One versus rest. Train one model per class, and each one answers a deliberately simple question: is this an A, or is it anything else? With three classes you get three models, each producing a probability, and you take whichever is highest. Three classes means three models.

One versus one. Train one model for every possible pair instead. A against B, A against C, B against C. Each model votes for one class, and the most voted class wins. Three classes means three models here too, but with five classes it is ten, and it grows fast.

grade A grade B grade C
…
…models trained …accuracy

Watch the boundaries appear one at a time. One versus rest draws one line per class, one versus one draws one line per pair, and the two schemes can disagree in the awkward middle region where the clouds meet, because neither has a rule for a point that every model wants to claim.

One versus rest is the usual default, because it stays cheap as classes pile up. One versus one is worth the cost when the classes are hard to tell apart in pairs, since each model only ever has to worry about two of them at a time.

CuriousThere is also a direct version

You do not strictly have to stitch together two-group models. There is a version called softmax regression which generalises the sigmoid to produce probabilities for all classes at once, adding up to 1, in a single model rather than several. It is fitted the same way, with the same loop, and it is what neural networks use at their final layer. When that chapter exists, this is the paragraph it starts from.

06

Prompting AI: making an assistant build it properly

An assistant will write you a working classifier from one lazy sentence. It will report an accuracy, it will sound pleased with itself, and it will quietly make three decisions that were yours to make.

"Predict whether students pass using logistic regression in Python."

What you typically get back

Five lines that call .predict(), print an accuracy of 0.85, and stop. .predict() already threw the probabilities away and applied a cut of 0.5 without mentioning it. Nothing says how many students failed in the data, so you cannot tell whether 0.85 is good or whether "everyone passes" would have scored the same. And nothing looks at which of the two mistakes it is making.

labels, not probabilitiesthreshold hidden at 0.5class balance unknownaccuracy quoted aloneno baselineno split
07

Beyond AI: choosing the tool, owning the call

It is tempting to file logistic regression as the simple thing you learn before the real models. That is the wrong framing. It is fast, it is auditable, and you can read straight off the weights which feature pushed the answer which way, which is exactly what almost nothing else on this site can offer. Often it is the right answer rather than a stepping stone, and the burden of proof sits with whatever you want to replace it with.

If your problem looks like thisReach forBecauseThe price you pay
Two groups, a roughly straight boundary, and somebody will ask you why the model said that Logistic regressionthis chapter Fast, needs little data, gives calibrated probabilities, and every weight is a sentence you can defend. One straight boundary only. Needs the features to already be informative.
The boundary is clearly curved, but you still want probabilities you can read Logistic regressionwith built features Squares, products and distances turn a straight boundary into a curved one, as in §4a. You have to know which features to build. Nothing suggests them for you.
Thresholds and interactions everywhere, and no clean line anywhere Decision treeor gradient boosting Finds the splits and the interactions on its own, and needs no scaling at all. Harder to audit, needs more data, and probabilities come out poorly calibrated.
The classes are separated by which examples they sit near, not by a line K-nearest neighboursthe next chapter No training step at all, and it copes with strange boundary shapes. Slow at prediction time, and it gives you no explanation whatsoever.
You are predicting a number, not a group Linear regressionprevious chapter Same machinery, one step shorter: no squash, and the error is measured in the units you care about. Different scores entirely, and one wild point can drag the whole line.

7b The judgment calls no prompt makes for you

Judgment call

Where you put the cut

Module 3 showed the threshold trading one mistake for the other. Which of the two you would rather make is a statement about consequences, not about data, and it belongs to whoever has to live with them. Writing it down is part of the model.

Judgment call

Which features are allowed

A weight is readable, which cuts both ways. If postcode earns a large weight you can see it, and then you have to decide whether a model is allowed to use it. Being able to read the answer creates the obligation to look.

Data reality

Who is missing from the data

Every student here sat the exam. The ones who dropped out earlier are not in the table at all, so the model has never seen them and cannot tell you it is missing them. No score notices an absence.

Responsibility

What happens to the person

The same probability can trigger an offer of help or a refusal. The maths does not change between those two uses, and the model has no idea which one it is feeding.