Put prices on the error cells 8 min live
The cost matrix is the confusion matrix with money in it. At Lumen: a false positive is ~$2 of wasted outreach; a false negative is a ~$74 order that never happens. Once those two numbers exist, "how good is the model?" stops being a metrics debate and becomes arithmetic: expected cost = FP x $2 + FN x $74, and the best threshold is whichever one minimizes it. This is not a niche trick - it is a 2001 result (Elkan, "The Foundations of Cost-Sensitive Learning") that sklearn's own documentation now builds its threshold-tuning guide around.
LiveWhere the prices come from (and how rough is fine)5 min▶
Teams stall here: "we don't know the exact cost of a false negative." You do not need exact - you need the RATIO roughly right, because the optimal threshold moves with the ratio, not the absolute figures. Getting prices:
- FP price - usually operational: cost of the outreach, the analyst-minutes to triage an alert, the goodwill hit of a wrong takedown. Lumen: ~$2 a nudge.
- FN price - usually opportunity: the average order (~$74 from the Lumen canon), the average fraud loss, the downstream cost of the missed defect.
- Disagree about the number? Run the evaluator at BOTH candidate prices - if the chosen threshold barely moves, the disagreement doesn't matter; if it moves a lot, you found a genuinely important business question, and it belongs to the business owner, not the notebook.
sklearn's canonical example does exactly this with the German Credit dataset - a cost matrix where misclassifying bad credit as good is 5x more costly, shipped WITH the dataset since the 1990s. The field has always known; the defaults just made everyone forget.
The quarter spent polishing F1. A churn team pushed F1 from 0.61 to 0.68 over a quarter - reprocessing pipelines, new features, a bigger model. Finance saw nothing. Post-mortem: their retention offer cost $8, a churned customer cost $310 - the cost-optimal threshold sat far to the recall side of their F1-optimal one the whole time. Moving one number on day one would have delivered more than the quarter of modeling. You will reproduce their mistake with one button in Demo 1, safely.
Self-studyThe sklearn mechanics, exactly4 min read▶
Two tools, both in sklearn >= 1.5, both from the threshold-tuning user guide:
from sklearn.model_selection import TunedThresholdClassifierCV
from sklearn.metrics import make_scorer, confusion_matrix
def lumen_gain(y, y_pred): # negative cost = gain
tn, fp, fn, tp = confusion_matrix(y, y_pred).ravel()
return -(fp * 2 + fn * 74)
tuned = TunedThresholdClassifierCV(
model, scoring=make_scorer(lumen_gain)
).fit(X_train, y_train) # NEVER tune on training data alone -
print(tuned.best_threshold_) # it cross-validates internally
And for a threshold someone hands you (a policy decision, a regulator): FixedThresholdClassifier. The guide's two warnings, verbatim in spirit: default 0.5 cutoffs are "most certainly not ideal for most use cases," and never use the same data to train the classifier AND tune the threshold.
When the metric becomes the target 6 min live
Goodhart's law: when a measure becomes a target, it ceases to be a good measure. In evaluation it has a precise mechanism - every summary metric bakes in a cost assumption, and optimizing the metric optimizes that hidden assumption, not your business. F1 assumes false positives and false negatives cost the same. At Lumen they differ by 37x. So maximizing F1 quietly maximizes the wrong objective - politely, reproducibly, with great-looking dashboards.
LiveNg's escape hatch: satisficing vs optimizing4 min▶
Andrew Ng's Structuring ML Projects doctrine, still the cleanest fix:
- Pick ONE optimizing metric - the number you genuinely try to maximize. Make it the money one when you can (expected cost/gain), a proxy only when you must.
- Make everything else a satisficing metric - a constraint with a floor or ceiling: recall >= 0.9, latency <= 200ms, cost per query <= $0.01. You don't optimize constraints; you respect them.
- His companion rule - the single-number evaluation metric - is about ITERATION SPEED, not truth: one number to rank experiments fast, with the cost matrix deciding what that number is.
Anthropic's own success-criteria guidance says the same thing for LLM systems: "most use cases need multidimensional evaluation" - their worked example is an F1 floor AND a toxicity ceiling AND a latency ceiling. One optimizing number, several guardrails. That shape repeats in b8.
Self-studyThe two-hop proxy chain3 min read▶
Google's Rules of ML, Rule #39, generalizes the trap: launch metrics are themselves proxies. The full chain is offline eval score -> online metric -> long-term business goal, and each hop can break independently:
- Offline-to-online breaks via drift, serving skew (Rule #37 - b9's whole story), or a metric detached from user pain.
- Online-to-business breaks when engagement rises while satisfaction falls - the classic clickbait failure.
The defense is not a cleverer metric - it is knowing which hop you are standing on when you read a number, and never celebrating hop-one progress as hop-three victory. Tattoo for the capstone: my eval score is a proxy for a proxy.
The $704 button ★ 14 min · everyone runs it
The evaluator below has two buttons. Optimize F1 sweeps every threshold and jumps to the best F1 the model can produce - the number most teams would put on the slide. Optimize $ sweeps the same thresholds and minimizes expected cost with Lumen's prices. Press both. Read what each one says about the other.
Press Optimize F1: threshold 0.61, F1 0.706, precision 100% - a gorgeous slide. Now read the cost tile: $1,850, and the verdict: $704 worse than the cost-optimal point. The metric went up. The money went down.
Press Optimize $: threshold 0.22, cost $1,146 - and look at the metrics card: F1 0.161, precision 8.8%, recall 100%. The BEST business operating point has the WORST-looking F1 on the card. If your review process kills anything with ugly metrics, it kills the right answer.
Understand WHY: a missed converter costs 37x a wasted nudge, so the money wants recall - flag broadly, tolerate cheap false alarms. F1 weighs both errors equally and refuses that trade. Neither number is lying; they optimize different worlds.
Now change the world: set FP cost to $40 (imagine each flag burns analyst time) and press Optimize $ again. Watch the optimal threshold march toward the F1 point. The cost matrix IS the decision.
Say the lesson out loud: F1-optimal is cost-optimal only when the errors cost the same - which is a business claim nobody at Lumen would sign.
Write Lumen's eval line the grown-up way ★ 8 min · workshop
Turn what you just saw into the one-line eval spec this course will keep reusing - one optimizing metric, satisficing constraints, prices declared. Draft it together, then stress-test it against the evaluator.
The line: "Minimize expected error cost at FP=$2 / FN=$74 on the held-out slice; subject to precision >= 8% (ops can absorb the alert volume) and no segment's recall below 80%." Every clause is a decision someone can veto - that is a feature.
Stress-test clause 1: at the cost-optimal threshold, is precision above the 8% floor? (Barely - 8.8%. If ops pushes the floor to 15%, find the new best threshold BY HAND with the slider and read what it costs: constraints have prices too.)
Note what is NOT in the line: accuracy (base-rate theater), F1 (an equal-costs assumption Lumen rejects). They can appear on a dashboard; they cannot be the target.
This line generalizes beyond classifiers. Swap the middle and it is an LLM spec: "maximize task success on the golden set; subject to faithfulness >= 0.9, TTFT <= 800ms, cost <= $0.02/query." Same architecture - one optimizing number, priced constraints. b8 fills in the latency and cost pieces; b10's capstone writes both versions in full.
Try it yourself - this week ◐ 30-40 min total
- Take the confusion matrix you drew for your own problem in b1's homework and price both error cells - rough is fine, ratio matters. Then write your one-line eval spec (optimizing metric + satisficing constraints).
- Run the evaluator at your ratio (scale to the $2/$74 boxes) and note how far cost-optimal sits from F1-optimal. One sentence: who in your org currently picks the threshold, and do they know they are picking it?
- Find one dashboard at work that shows F1 or accuracy with no cost context. Write the two clarifying questions you would now ask.
- Optional deep read: sklearn's "Post-tuning the decision threshold for cost-sensitive learning" example end to end - the German Credit walkthrough is this session in code.
Official sources covered
Certificates and graded assessments stay with the official providers (linked each session). This page covers:
Three questions before you go 🎯 ◐ 90 seconds
1 · On Lumen's slice, the F1-optimal threshold costs $704 more than the cost-optimal one. What is the root cause?
Every summary metric embeds a cost assumption. F1's is equal costs - optimize it and you optimize that assumption. The $2-vs-$74 asymmetry wants a recall-heavy threshold F1 refuses to choose.
2 · The cost-optimal operating point shows precision 8.8% and F1 0.161. What should a review committee do?
When the miss is 37x the false alarm, the money WANTS low precision and total recall. A committee that requires pretty metrics forces the expensive threshold. (C is usually impossible - the two optima coincide only at equal costs.)
3 · In Ng's vocabulary, "latency under 200ms" and "expected cost minimized" are, respectively:
One optimizing metric for iteration speed; everything else becomes a constraint with a threshold. Anthropic's multidimensional success criteria are the same architecture for LLM systems.