Skip to content

~/teaching · machine learning · session 4, thursday 15 october 2026

Metrics, calibration and a decision threshold under asymmetric costs, then a repository someone else can rerun

From a probability of default to a defended decision, and from a notebook to a repository that runs elsewhere.

Abstract

This deck closes the first block of the Machine Learning course at Centrale Méditerranée (option DDEFI, 2026-2027). It deals with two things many courses skip: the decision that comes out of a classifier, and the engineering that lets another team obtain the same numbers. The running case is credit scoring, where defaults are rare and the two kinds of error do not cost the same.

The first half reads the classic metrics with their limits: the confusion matrix, precision and recall, the ROC curve and its area, the precision-recall curve, then calibration and the Brier score. It ends with the decision threshold, derived in four lines from two business costs, and checked on a cost curve. Every result is recomputed by hand on ten loan applications small enough to verify with a pencil.

The second half is a discipline rather than a theorem: a result depends on the data, the code, the random seed and the environment, so all four are pinned. After the deck you can say what each metric hides, justify a threshold in front of a credit committee, and ship a repository with tests and continuous integration that reproduces your metrics with one command.

The slides

Decide under cost, ship a reproducible repository

Session 4, Thursday 15 October 2026. Machine Learning, Centrale Méditerranée, option DDEFI, 2026-2027.

Open the slides (password from your teacher)

The deck itself is encrypted for the students of the course; this page summarises it for everyone.

The course page · Attention and transformers, step by step

Plan of the deck

  1. Why accuracy misleads. With 2 % defaults, a model that never predicts a default looks excellent and catches nothing.
  2. Confusion matrix, precision, recall. Four cells, four rates with different denominators, and why precision depends on the base rate.
  3. ROC curve and AUC. One point per threshold, the area read as a probability, and its history from radar to radiology.
  4. Precision-recall and the F-beta score. The view to prefer when the positive class is rare.
  5. Calibration and the Brier score. Ranking well is not the same as giving true probabilities; Murphy's decomposition shows why.
  6. Class weights and their correction. Weighting the positives inflates the probabilities; a formula on the odds deflates them.
  7. The threshold under cost. Derivation, worked example with €600 and €4,500, and the expected cost curve.
  8. Reproducible engineering. Hashed data, frozen split, one seed, lockfile, five tests, one command, CI, branches and a decision log.

Accuracy, precision and recall when the positive class is rare

When only two applicants in a hundred default, a model that accepts everybody is right 98 times out of 100 and still useless, so we need rates that look at the defaults.

The deck keeps one illustrative portfolio all afternoon: 10,000 credit applications, 2 % of which default. A model that always answers "no default" scores 98 % accuracy and a recall of zero. A real model, at a given threshold, refuses 150 of the 200 defaults and lets 50 through, while wrongly refusing 800 of the 9,800 good clients. Its accuracy is (150 + 9,000) / 10,000 = 91.5 %, lower than the useless model, although it catches three defaults in four. Accuracy adds two errors that differ in count and in price.

Call a predicted default (a refusal) a positive. The four cells are TP, FP, FN and TN, with P=TP+FNP = TP + FN true defaults, N=FP+TNN = FP + TN good clients and base rate π=P/n\pi = P/n. The rates differ only by their denominator, which says what you are talking about: the refused column, the default row, or the good-client row.

precision=TPTP+FP,recall=TPP,FPR=FPN\text{precision} = \frac{TP}{TP+FP},\qquad \text{recall} = \frac{TP}{P},\qquad \mathrm{FPR} = \frac{FP}{N}
(1)

Two short rewritings explain the traps. Multiplying and dividing by PP and NN shows that accuracy is an average of recall and specificity weighted by the class sizes, so with π=0.02\pi = 0.02 the defaults weigh 2 % of it. Dividing precision by nn gives Bayes' formula in credit vocabulary.

accuracy=π recall+(1−π)(1−FPR)\text{accuracy} = \pi\,\text{recall} + (1-\pi)(1-\mathrm{FPR})
(2)
precision=π recallπ recall+(1−π) FPR\text{precision} = \frac{\pi\,\text{recall}}{\pi\,\text{recall} + (1-\pi)\,\mathrm{FPR}}
(3)
Table 1: The illustrative 10,000 applications (2 % defaults) at one threshold.
QuantityComputationValue
Precision150 / 95015.8 %
Recall150 / 20075 %
False positive rate800 / 9,8008.2 %
Accuracy9,150 / 10,00091.5 %

Remark (Same model, another population.). Recall and FPR describe the model; precision also describes the population. Keep recall 0.75 and FPR 0.0816 but move the default rate from 2 % to 22 %, and precision rises from 15.8 % to about 72 % without any change to the model.

Back to contents.

ROC curve, AUC and the precision-recall view

Sliding the threshold traces a curve of caught defaults against wrongly refused clients, and the area under it is the chance that a random default is scored above a random good client.

Each threshold gives a confusion matrix, hence a point with the false positive rate on the x-axis and recall (the true positive rate) on the y-axis. Moving the threshold from 1 down to 0 draws the ROC curve. Its area has three equivalent readings: a geometric area, a probability, and a count of well-ordered pairs, which is the Mann-Whitney statistic divided by the number of pairs. The step from the first to the second is a change of variable followed by the law of total probability.

AUC=∫01TPR d(FPR)=P(s+>s−)\mathrm{AUC} = \int_0^1 \mathrm{TPR}\,\mathrm{d}(\mathrm{FPR}) = \mathbb{P}\big(s^{+} > s^{-}\big)
(4)
AUC^=1n+n−∑i=1n+∑j=1n−1{si+>sj−}\widehat{\mathrm{AUC}} = \frac{1}{n^{+}n^{-}}\sum_{i=1}^{n^{+}}\sum_{j=1}^{n^{-}} \mathbf{1}\{s_i^{+} > s_j^{-}\}
(5)
Table 2: The ten illustrative validation applications used throughout the deck.
OutcomeApplicationPredicted probability
good client10.05
good client20.08
good client30.10
default40.15
good client50.20
good client60.30
good client70.40
default80.55
good client90.70
default100.90

Counting the pairs on the ten applications

  1. Pairs. Three defaults and seven good clients make 21 (default, good client) pairs.
  2. Application 4 (0.15). It is scored above only the three good clients at 0.05, 0.08 and 0.10, so it wins 3 pairs.
  3. Application 8 (0.55). It beats six good clients and loses only to application 9 at 0.70: 6 pairs.
  4. Application 10 (0.90). It beats all seven: 7 pairs.
  5. Result. U = 16, so the AUC is 16/21, about 0.762, and the Gini used in credit (2 AUC minus 1) is about 0.524.

Because only the order of the scores matters, any increasing transformation of the score leaves the AUC unchanged. That is its strength and its limit: it says nothing about the threshold, the costs, or whether the probabilities are right. The curve comes from radar signal detection (Peterson, Birdsall and Fox, 1954); Hanley and McNeil (1982) showed that its area is the normalised Mann-Whitney statistic, and Bradley (1997) brought it to the comparison of learning algorithms.

With a rare class, the ROC curve flatters. In the 10,000-application example, 800 false positives are 8 % of the good clients, which looks harmless, but 84 % of all refusals. The precision-recall curve shows that second fact; its random baseline is the default rate, not a diagonal. The FβF_\beta score summarises one point of it as a weighted harmonic mean, and β>1\beta > 1 gives more weight to recall. The deck warns that choosing β\beta is a cost decision in disguise.

Fβ=(1+β2) precision×recallβ2 precision+recallF_\beta = (1+\beta^{2})\,\frac{\text{precision}\times\text{recall}}{\beta^{2}\,\text{precision} + \text{recall}}
(6)

Back to contents.

Calibration and the Brier score

A model is calibrated when, among the applications it scores at 10 %, about one in ten actually defaults; a good ranking does not guarantee that.

Two models can have the same AUC while one of them announces 50 % where the observed default rate is 10 %. The reliability curve shows it: group the predictions into bins and plot the mean predicted probability against the observed frequency; a calibrated model sits on the diagonal. The Brier score, born in weather forecasting (Brier, 1950), measures the values themselves as the mean squared gap between the probability and the 0/1 outcome.

E[ Y∣s(X)=s ]=s\mathbb{E}[\,Y \mid s(X) = s\,] = s
(7)
Brier=1n∑i=1n(si−yi)2\text{Brier} = \frac{1}{n}\sum_{i=1}^{n}(s_i - y_i)^2
(8)

Inside one bin of score pp with default frequency π\pi, adding and subtracting π\pi and expanding the square kills the cross term, leaving the squared calibration error plus the Bernoulli variance π(1−π)\pi(1-\pi). Averaging over bins and applying the law of total variance gives Murphy's decomposition (1973).

Brier=Ep[(p−πp)2]⏟reliability−Var⁡p(πp)⏟resolution+πˉ(1−πˉ)⏟uncertainty\text{Brier} = \underbrace{\mathbb{E}_p\big[(p-\pi_p)^2\big]}_{\text{reliability}} - \underbrace{\operatorname{Var}_p(\pi_p)}_{\text{resolution}} + \underbrace{\bar\pi(1-\bar\pi)}_{\text{uncertainty}}
(9)

Worked check on the ten applications

  1. Squared gaps. The ten values of (p minus y) squared sum to 1.734, so the Brier score is about 0.173.
  2. The misranked default. Application 4, a default scored 0.15, costs 0.85 squared, 0.7225, that is 0.072 of the 0.173 on its own.
  3. Constant model. Saying 0.3 to everyone is calibrated but has a single bin, so its Brier is the uncertainty, 0.3 times 0.7, which is 0.21.
  4. Reading. The model beats the constant: its resolution earns more than its calibration errors cost.

The same reasoning gives the project's reference point: on the UCI Default dataset, with 22.1 % defaults, the constant model has a Brier of about 0.172, and a baseline should do better on validation. To recalibrate, fit Platt scaling (a logistic on the score) or isotonic regression (piecewise increasing) on the validation set, never on the test set. In banking this is not optional: the EBA guidelines on PD estimation (EBA/GL/2017/16, 2017) require probabilities of default aligned with the long-run default rate.

Back to contents.

Class weights inflate probabilities: correct them before deciding

Training with extra weight on defaults makes the model believe defaults are common, so its outputs must be scaled back before any threshold is applied.

Weighting the positives by ww, or duplicating them, is training on a population where the prior of defaults is ww times larger while the class-conditional distributions are unchanged. By Bayes' formula the posterior odds are multiplied by ww. Inverting gives back the true probability. For a logistic regression this only shifts the intercept, by the logarithm of ww, although in practice the slopes move a little too, so the correction is followed by a recalibration.

pw1−pw=w p1−p\frac{p_w}{1-p_w} = w\,\frac{p}{1-p}
(10)
p=pww−(w−1) pwp = \frac{p_w}{w - (w-1)\,p_w}
(11)
Table 3: With class_weight='balanced' at 2 % defaults, the weight ratio is 98/2 = 49 (illustrative).
True probabilityOddsOdds × 49Displayed probability
0.020.0201.000.500
0.120.1366.680.870
0.300.42921.00.955

A 12 % threshold applied to the displayed numbers would refuse all three applications, including the 2 % one the bank wants. The round trip checks the formula: 0.12 goes up to 0.870, and 0.870 divided by 49 minus 48 × 0.870 (that is 7.24) comes back to 0.120. The order of operations is therefore: correct, recalibrate on validation, and only then threshold. The deck calls a forgotten correction the most frequent bug in junior notebooks.

Back to contents.

Deriving the decision threshold from asymmetric costs

Refuse an application when refusing costs less on average than accepting it; the threshold that follows depends only on the two costs, not on the model.

For one application with calibrated probability of default pp, accepting loses cFNc_{FN} with probability pp and refusing loses cFPc_{FP} with probability 1−p1-p. Refuse when the second expectation is smaller. Expanding, moving the pp term to one side and dividing by the positive sum of costs gives the rule. The default 0.5 of scikit-learn is the special case of equal costs, which credit never sees.

refuse  ⟺  (1−p) cFP<p cFN  ⟺  p>t∗=cFPcFP+cFN\text{refuse} \iff (1-p)\,c_{FP} < p\,c_{FN} \iff p > t^{*} = \frac{c_{FP}}{c_{FP} + c_{FN}}
(12)

The deck's example, with written assumptions

  1. Costs. A refused good client loses €600 of margin; an accepted default on a €10,000 loan loses €4,500, a loss given default of 45 % (the Basel II foundation value for senior unsecured corporate claims, used as a benchmark only).
  2. Threshold. 600 / 5,100 is about 0.1176, so the bank refuses from roughly 12 % risk, because a default costs 7.5 times a wrong refusal.
  3. Indifference. At p equal to the threshold, accepting costs 0.1176 × 4,500 and refusing 0.8824 × 600, both about €529.
  4. Three applications. At 5 % risk, accepting costs €225 against €570 for refusing, so accept; at 30 %, €1,350 against €420, so refuse.

If the calibration is doubtful, the robust version minimises the total cost counted on the validation set, threshold by threshold. When the probabilities are calibrated, replacing each yiy_i by pip_i shows that every term of the expected cost is minimised by the same rule, so the empirical minimum should fall near t∗t^{*}, and a large gap is itself a diagnosis of poor calibration.

C(t)=cFP FP(t)+cFN FN(t)C(t) = c_{FP}\,FP(t) + c_{FN}\,FN(t)
(13)
Table 4: Total cost on the ten applications, with €600 and €4,500 (illustrative).
ThresholdFPFNTotal cost
1 (accept all)03€13,500
0.5011€5,100
0.1240€2,400
0 (refuse all)70€4,200

On the 10,000 applications, accepting everyone costs 200 × €4,500 = €900,000, which is what the bank pays without a model, and refusing everyone costs 9,800 × €600 = €5.88 million. At the cost threshold, with 800 false positives and 50 false negatives, the bill is €705,000: the model saves about €195,000 per 10,000 applications. At 0.5 the deck's curve reads about €870,000, close to accepting everyone, because with a rare class few applications ever exceed one half.

Remark (A decision, not a setting.). The idea goes back to Neyman and Pearson (1933) and Wald's decision theory; Elkan (2001) gave the cost threshold for machine learning, and scikit-learn 1.5 (May 2024) added TunedThresholdClassifierCV. Two banks with the same model and different margins get two thresholds, and both are right. The costs belong in the configuration and their justification in a written log, revisable without retraining. Session 8 goes further with conformal prediction.

Back to contents.

A reproducible ML repository: seed, environment, tests and CI

A reported score depends on the data, the code, the random seed and the software versions, so each of the four is pinned and a neutral machine reruns everything on every push.

result=F(data, code, seed, environment)\text{result} = F(\text{data},\ \text{code},\ \text{seed},\ \text{environment})
(14)

An AUC of 0.78 means nothing on its own. If any of the four inputs moves silently, the number moves and nobody knows why. The course project repository fixes each of them: the CSV is hashed with SHA-256 and the hash stored; the train, validation and test split is frozen by a seed and checked by its own hash; the code lives in importable modules under git, which notebooks import rather than copy; a single seed sits in config.yaml next to the costs and the threshold; the environment is a lockfile plus a fixed Python version in a devcontainer.

  • Lockfile. A bare scikit-learn line means "whatever version is current on install day"; months later a changed default can shift the AUC with no code change. The lockfile pins exact versions and package hashes, is generated from a short input file (with pip-compile --generate-hashes or uv lock), never edited by hand, and committed.
  • Five tests, under a minute. A schema contract on the incoming data (types, bounds, missing rate), a split with the expected hash, AUC equal to 1 on separable data, predictions unchanged when rows are permuted, and a reloaded pipeline that predicts like the trained one to 10−1210^{-12}. A test is a property that must always hold, not "the AUC looks good".
  • One command. make reproduce checks the data hash, redoes the split, trains the sklearn Pipeline (fitted on the training fold only, so no leakage by construction) and writes the metrics file. Running it twice must give identical files; a difference means a loose seed.
  • Continuous integration. On every push and pull request, a clean machine installs the lockfile, runs the tests and the reproduction, then compares AUC and Brier with a committed reference within 0.005. A red build is information: either the change was intended and the reference moves with an explanation, or a bug has just been found early.
  • Branches and review. Nobody pushes to the main branch. One branch per task, one small pull request, a review checklist (green CI, a test for the change, nothing from the test set in the model, metrics and log updated when a number moves, readable without the author) and a merge by someone else.
  • A decision log. Each entry gives the date, the choice, the context, the rejected alternative with the number that ruled it out, and the command that reproduces it. For example: threshold 0.12 rather than 0.5, €705k against €870k on validation. A tool can generate a model; it cannot write the history of the team's hesitations.

The same logic runs through the earlier sessions of the course, from least squares to the logistic regression and its regularisation: simple models, done cleanly and explained. AI assistants are welcome for plumbing such as a CI workflow or a Makefile, provided their use is declared; the threshold derivation and the modelling choices stay with the team.

Back to contents.

Key takeaways

  1. Accuracy is a weighted average of recall and specificity, so with a rare class it mostly measures the majority.
  2. Recall and the false positive rate describe the model; precision also depends on how rare the positive class is.
  3. The AUC is the probability that a default is scored above a good client: it measures order, not probabilities, thresholds or costs.
  4. Calibration is a separate quality, measured by the reliability curve and the Brier score, and it is what a banking supervisor reads first.
  5. With calibrated probabilities, refuse when the probability exceeds the false positive cost divided by the sum of both costs; with €600 and €4,500 that is about 0.12, not 0.5.
  6. Class weights multiply the odds; correct and recalibrate before applying any threshold.
  7. Pin data, code, seed and environment, test properties, rerun everything with one command, and let CI compare the metrics on every push.

Glossary

Confusion matrix
The 2 × 2 table of true positives, false positives, false negatives and true negatives at a given threshold.
Precision, recall
Share of true defaults among refusals; share of true defaults that are caught.
False positive rate
Share of good clients wrongly refused; the x-axis of the ROC curve.
ROC curve, AUC
Recall against false positive rate over all thresholds; its area is the probability that a positive is scored above a negative.
Precision-recall curve
Precision against recall over all thresholds; more informative than ROC when the positive class is rare.
Calibration, reliability curve
Agreement between predicted probabilities and observed frequencies, plotted bin by bin against the diagonal.
Brier score
Mean squared gap between the predicted probability and the 0/1 outcome; reliability minus resolution plus uncertainty.
Decision threshold
The probability above which an application is refused; under calibrated probabilities, a ratio of costs.
Expected cost
Each possible loss times the probability that it occurs, summed; the quantity the threshold minimises.
Random seed
The number that fixes every random draw (split, shuffles, initialisation) so that a run can be repeated.
Lockfile
A generated file pinning exact package versions and hashes, so that the environment can be rebuilt identically.
Continuous integration (CI)
Automatic rerun of tests and reproduction on a clean machine at every push or pull request.
Reproducibility
Another person, with the same data, code, seed and environment, obtains the same numbers.

The other notes of the course

One public summary per deck, in the order of the course.

  1. IntroductionAI winters, jobs and method
  2. ToolsKaggle, GitHub, Python: the tools
  3. Session 1Project framing and least squares
  4. Session 2Maximum likelihood and logistic regression
  5. Session 3Gradient descent, ridge and lasso
  6. Session 4Decision threshold and reproducible ML
  7. HomeworkA defensible baseline model
  8. Session 5Kernels, SVM and decision trees
  9. Session 6Forests, boosting, honest comparison
  10. Session 7SHAP, fairness, PCA and clustering
  11. HomeworkA documented champion model
  12. Session 8Neural networks, drift, conformal prediction
  13. HomeworkA delivered machine learning project
  14. Session 9Defending a machine learning project
  15. ReferenceCredit default scoring project
  16. ReferenceAsset returns: XGBoost and sentiment
  17. ReferencePython code for every ML model
  18. ReferenceDeep learning essentials
  19. ReferenceOpen data ML projects by sector
  20. ReferenceData and AI engineer handbook
  21. ReferenceData engineering, SRE and cloud
  22. ReferencePanorama of machine learning models

All the notes, as a reading list

Attention and transformers, step by step

About these notes

Written by the lecturer from the slides of the deck, as a public summary: the key ideas, the formulas and one worked example. The exercises, their solutions, the tests and the grading stay in the deck, for the students of the course.

Revision: .