Linear Regression: Cost Functions and the Line of Best Fit
The simplest possible answer to "fit a function to examples" — and the one that introduces every idea the rest of this track builds on.
Beginner
lines_changed. Plot
historical runs as points (lines changed, minutes taken), and the pattern is roughly a
straight line trending upward — bigger diffs tend to take longer to build and test.
Linear regression is the algorithm that finds the specific straight line that fits that
pattern best.
y = w·x + b: a weight w that
scales the input, plus a bias b that shifts the whole line up
or down. In ML terms, w and b are the model's
parameters — the numbers training will actually adjust. Everything
else about the setup (how many features, what learning rate, how many training passes) is
a hyperparameter: a choice you make before training starts, not something
the algorithm learns on its own.
predicted_minutes = w * lines_changed + b
# training doesn't touch this formula — it only searches for
# the specific numeric values of w and b that fit the data best
error_i = actual_i - predicted_i. The
standard way to turn a whole column of per-run errors into one overall score is
mean squared error (MSE):
MSE = (1/n) * sum((actual_i - predicted_i)^2 for i in 1..n)
| Cost function | Formula | Behavior |
|---|---|---|
| Mean Squared Error (MSE) | mean((actual - predicted)^2) | penalizes large errors heavily; sensitive to outliers |
| Mean Absolute Error (MAE) | mean(|actual - predicted|) | treats every unit of error equally; more robust when a few runs have wildly wrong durations (a hung runner, a stuck queue) |
w and b that
make MSE as small as possible. For plain linear regression with MSE, there's actually a
closed-form formula — the normal equation — that computes the optimal
w and b directly from the data in one shot, no trial and error
needed. It doesn't scale well to models with many features or to other model types, which is
why the more general tool — gradient descent, nudging w and
b a little at a time in the direction that reduces the cost — gets its own
dedicated page later in this track. Both approaches are minimizing the exact same MSE cost
function; they just get there differently.
predicted_minutes = (w1 * lines_changed
+ w2 * files_changed
+ w3 * touches_slow_suite # 0 or 1
+ b)
lines_changed might range into the thousands while
touches_slow_suite is only ever 0 or 1. Left unscaled, gradient descent
struggles because the cost surface is stretched much further in one direction than another —
covered properly in the feature engineering page later in this track.
predicted_minutes = w1 * lines_changed + w2 * lines_changed**2 + b
w1, w2, even though the prediction curve itself
is no longer a straight line. The obvious temptation is to keep adding higher powers until
the curve threads every single training point — at which point it's stopped modeling the
trend and started memorizing the noise. That failure mode, overfitting, gets
a full page of its own later in this track.
branch_type
(feature, release, hotfix) has to become a number
before it can be multiplied by a weight at all — but the obvious move,
assigning 0, 1, 2, is a trap: it silently tells the model that hotfix is
"twice as much" of something as feature, a relationship that was never actually
there. This is exactly the "other cases" pitfall for regression inputs, and it's worth
flagging here even though the fix (one-hot encoding, mostly) belongs to the feature
engineering page later in this track.