the problem: back to pass/fail

Linear Regression left off with a specific complaint: reusing a straight line for the CI pass/fail problem produces an unbounded score that a single outlier diff can drag around, with no natural floor or ceiling to make it behave like a probability. Logistic regression is a small, deliberate fix to exactly that gap — take the same weighted-sum score linear regression already computes, and pass it through a function that squashes it into the range (0, 1), so the output can actually be interpreted as "probability this run fails."

squashing a line into a probability: the sigmoid

The sigmoid function takes any real number and maps it into (0, 1):

sigmoid(z) = 1 / (1 + e^(-z))

sigmoid(-10)  ->  0.00005   # very negative input -> probability near 0
sigmoid(0)    ->  0.5       # exactly zero -> perfectly undecided
sigmoid(10)   ->  0.99995   # very positive input -> probability near 1
            
Large negative inputs get squashed toward 0, large positive inputs toward 1, and the function is smooth and symmetric around z = 0, where it outputs exactly 0.5. That's the whole mechanism — nothing about the underlying linear score changes; only what's done with it after does.

the model: linear regression, then squash

Logistic regression computes the exact same weighted sum as linear regression, then feeds it through sigmoid:

z = w1 * lines_changed + w2 * files_changed + w3 * touches_slow_suite + b
p_fail = sigmoid(z)     # a number strictly between 0 and 1

# turn the probability into a hard yes/no with a threshold
prediction = "fail" if p_fail > 0.5 else "pass"
            
The parameters being trained — w1, w2, w3, b — are the same kind of numbers as in linear regression, and training is still "search for the values that make predictions match reality as closely as possible." What changes is what "closely" means, covered below.

the decision boundary

Because sigmoid(z) = 0.5 exactly when z = 0, the 0.5 threshold on the probability corresponds to a very simple condition on the underlying linear score: w·x + b = 0. With two features that's a line splitting the feature plane in half; with more features it's a flat hyperplane. Everything on one side of it gets classified "fail," everything on the other "pass" — which is why logistic regression is called a linear classifier: the boundary between classes is always flat, never curved, no matter how the sigmoid bends the probability itself.

why squared error breaks down here

It's tempting to train this exactly like linear regression — minimize mean squared error between the predicted probability and the actual 0/1 label. The problem is deeper than "it works less well": once MSE is combined with a sigmoid squashed into the mix, the resulting cost surface stops being convex. A convex cost function has one clean bowl-shaped minimum, which is exactly the shape gradient descent is good at finding; a non-convex surface can have multiple local dips, and gradient descent can get stuck in one of them without ever finding the actual best fit. Linear regression's MSE is convex because the model itself is linear; squashing it through sigmoid first breaks that property.

the fix: log loss (binary cross-entropy)

Logistic regression uses a different per-example loss instead — designed specifically to stay convex through the sigmoid and to reflect what actually matters for a probability: how far off it was, on a scale that punishes confident-and-wrong far more than uncertain-and-wrong.

# actual is 1 or 0; p_fail is the model's predicted probability of "fail"
loss = -log(p_fail)       if actual == 1   # penalize a low probability when the true answer was "fail"
loss = -log(1 - p_fail)   if actual == 0   # penalize a high probability when the true answer was "pass"
            
Read what happens at the extremes: if the true label is "fail" and the model predicted p_fail = 0.99, -log(0.99) is a tiny loss — barely any penalty for being confidently right. If it predicted p_fail = 0.01 for an actual failure, -log(0.01) is a huge loss — a steep penalty for being confidently wrong. That asymmetric, unbounded penalty for confident mistakes is exactly what pushes training to separate the classes cleanly rather than hedge toward 0.5 everywhere. Averaged over every training example, this per-example loss becomes the cost function that gradient descent minimizes — the same gradient descent covered in depth later in this track, just applied to a different cost function than linear regression's.

beyond two classes: softmax, briefly

Sigmoid handles exactly two outcomes. If the CI problem were "predict which of five failure categories this run belongs to" instead of a binary pass/fail, the natural extension is softmax: it takes a raw score for every class and turns the whole set into a probability distribution that sums to 1 across all classes, rather than a single independent 0-to-1 number per class. Sigmoid is the special case of softmax with exactly two classes. This gets picked up again once neural network output layers are covered later in this site's Deep Learning track.

practical notes

from sklearn.linear_model import LogisticRegression| https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html | despite the name, this is scikit-learn's classifier -- fits via log loss, not squared error |'logr_sk1'
model.predict_proba(X_test)| | returns the actual probabilities -- predict() alone only gives you the thresholded 0/1 class, throwing away information you often want |'logr_sk2'
LogisticRegression(class_weight='balanced')| | the same class-imbalance fix introduced in the ML pipeline page -- most CI runs pass, so an unweighted model can default toward always predicting "pass" |'logr_sk3'
0.5 is a default threshold, not a law of nature. If a missed failure (predicting "pass" when the run actually fails, wasting a slow test cycle) is more costly than a false alarm (predicting "fail" and running extra checks unnecessarily), lowering the threshold to, say, 0.3 trades some false alarms for fewer missed failures. Choosing that threshold is a business decision layered on top of the model's raw probability output, not something the model decides for you.

where to go from here

Linear Regression — the regression counterpart this page builds directly on.
Decision Trees — a completely different way to draw a decision boundary, no weighted sum involved.
The ML Pipeline — where model selection (stage 4) fits into the bigger picture.

reference

scikit-learn — Logistic Regression
Google ML Crash Course — Logistic Regression