Lesson 33 of 40

Chapter 33 — Final Test Evaluation

Freeze the model  •  Refit once •  Evaluate once  • Report honestly

Turning model development into a defensible final evaluation

BRIDGE FROM CHAPTER 32  After a fair comparison has produced one final candidate, the protected test set becomes relevant for the first time. Chapter 33 formalizes the last evaluation step: freeze every modeling choice, refit the final pipeline on the available development data, evaluate once on the untouched test set, and report both strengths and limitations.

Chapter map

Table 33.1. Chapter roadmap

Section

Main question

Core discipline

33.1 Role of the test setWhat is the test set for?A final unbiased estimate after all model and threshold decisions are frozen.
33.2 Retraining the final modelWhat should be refit?Use finalized preprocessing and hyperparameters; combine train + validation when appropriate.
33.3 Test reportWhat evidence belongs in the final report?Use task-appropriate metrics, residual/confusion analysis, threshold disclosure, and subgroup checks.
33.4 Honest reportingHow should results be communicated?Show favorable and unfavorable findings, uncertainty, limits, and intended use conditions.
Practical activityWhat should students deliver?A formal final-test evaluation section with reproducible code and a decision-ready interpretation.

 

Chapter overview

The final test evaluation is not another opportunity to improve the model. It is a measurement step. The test set should represent data that the model-development process has not used for fitting, feature selection, hyperparameter tuning, threshold experimentation, or model choice. Once the test results are observed, changing the model in response to those results turns the test set into a new validation set.

A defensible final report therefore begins with process discipline. The model architecture, preprocessing, hyperparameters, feature set, decision threshold, and primary metrics should already be fixed. The final pipeline can then be retrained on the complete development data and evaluated once under the conditions that best approximate intended deployment.

Learning objectives

  • Explain why a protected test set is used only after model selection is complete.
  • Distinguish validation-driven model development from final test measurement.
  • Retrain a finalized pipeline using training and validation data without changing its specification.
  • Build a formal classification or regression test report using task-appropriate metrics and diagnostic analysis.
  • Report thresholds, subgroup behavior, residuals, error ranges, and uncertainty rather than only one headline score.
  • Recognize selective reporting, test-set overuse, and post-test tuning as threats to honest evaluation.
  • Write a concise, decision-ready final-test evaluation section.

33.1 Role of the test set

The test set is a final measurement instrument. It estimates how the selected model is likely to perform on new observations drawn under conditions similar to those represented by the test data. Its value comes from independence: the test labels must not influence model-selection decisions.

Table 33.2. Development data versus test data

Activity

Training / validation data

Protected test data

Fit model parametersYesNo
Choose preprocessingYesNo
Engineer/select featuresYesNo
Tune hyperparametersYesNo
Choose algorithm familyYesNo
Tune decision thresholdValidation onlyNo, unless threshold is fixed elsewhere
Estimate final generalizationNot the final estimateYes

 

GOLDEN RULE   If a test result changes what model you deploy, the test set has participated in model selection. A new untouched evaluation set would then be needed for a genuinely final estimate.

 

Why repeated test evaluation is dangerous

  • Trying many models and reporting only the one with the best test score creates implicit test-set tuning.
  • Changing a threshold because test recall looks disappointing leaks test information into the operational decision.
  • Adding or removing features after viewing test errors uses the test set as a feature-selection signal.
  • Repeatedly checking the same test set can lead to an optimistic estimate even when each individual change seems small.

A simple data-flow discipline

Train  →   Validation / Cross-validation   →  Freeze choices  →   Refit  →  Test once

The test set is measured only after the development loop is complete.

 

PYTHON  •  Create a protected train / validation / test split

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

X, y = load_breast_cancer(return_X_y=True, as_frame=True)

X_dev, X_test, y_dev, y_test = train_test_split(
    X, y, test_size=0.20, stratify=y, random_state=42
)
X_train, X_val, y_train, y_val = train_test_split(
    X_dev, y_dev, test_size=0.25, stratify=y_dev, random_state=42
)

# X_test and y_test remain untouched until the final evaluation.

 

 

33.2 Retraining the final model

After model selection, the development phase should produce a complete frozen specification: preprocessing steps, feature set, model family, hyperparameters, random seeds where relevant, and any operational threshold. The final refit does not reopen these choices; it simply uses more labeled development data to estimate the already-chosen model parameters.

Table 33.3. What must be frozen before the final refit

Component

Example frozen choice

Can test results change it?

PreprocessingStandardScaler inside a pipelineNo
Feature setAll selected features from development processNo
AlgorithmLogistic RegressionNo
HyperparametersC = 1.0, L2 penaltyNo
Threshold0.37 selected on validation dataNo
Primary metricsRecall, F1, ROC AUCNo

 

Combining training and validation data

Once the model specification is fixed, it is common to combine the original training and validation partitions and refit the final pipeline. This gives the final model access to more labeled development observations while preserving the untouched test set for evaluation.

IMPORTANT   Do not combine the test set into the final fit before reporting test performance. That would measure the model on observations it has already seen during training.

 

PYTHON  •  Choose a threshold on validation data before the final refit

import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_recall_curve

selected_model = make_pipeline(
    StandardScaler(),
    LogisticRegression(C=1.0, max_iter=2000, random_state=42),
)
selected_model.fit(X_train, y_train)
val_prob = selected_model.predict_proba(X_val)[:,  1]

precision, recall, thresholds = precision_recall_curve(y_val, val_prob)
valid = recall[:-1>=0.90
threshold = thresholds[valid][np.argmax(precision[:-1][valid])]
print(f"Frozen threshold: {threshold:.3f}")

 

 

PYTHON  •  Refit the finalized pipeline on all development data

import pandas as pd

X_final = pd.concat([X_train, X_val], axis=0)
y_final = pd.concat([y_train, y_val], axis=0)

final_model = make_pipeline(
    StandardScaler(),
    LogisticRegression(C=1.0, max_iter=2000, random_state=42),
)
final_model.fit(X_final, y_final)

# Model, preprocessing, and threshold are now frozen.

 

 

33.3 Test report

A final test report should answer more than “What is the score?” It should document the test population, model version, threshold or prediction rule, primary metrics, important secondary metrics, error patterns, subgroup behavior, and any limitations that affect interpretation.

Classification test report

Table 33.4. Recommended classification evidence

Item

What to report

Why it matters

Confusion matrixTP, TN, FP, FNShows the actual error types at the deployed threshold.
PrecisionTP / (TP + FP)Quantifies how often positive predictions are correct.
RecallTP / (TP + FN)Quantifies how many positive cases are detected.
F1-scoreHarmonic mean of precision and recallSummarizes the precision-recall trade-off.
ROC AUC / PR AUCThreshold-independent ranking qualityProvides probability/score discrimination evidence.
ThresholdExact fixed operating thresholdMakes the classification rule reproducible.
Subgroup analysisMetrics by relevant groupsChecks whether aggregate performance hides weak segments.

 

PYTHON  •  Evaluate the final classifier once on the test set

from sklearn.metrics import (
    confusion_matrix, precision_score, recall_score, f1_score,
    roc_auc_score, average_precision_score,
)

test_prob = final_model.predict_proba(X_test)[:,  1]
test_pred = (test_prob >= threshold).astype(int)

cm = confusion_matrix(y_test, test_pred)
results = {
    "precision": precision_score(y_test, test_pred),
    "recall": recall_score(y_test, test_pred),
    "f1": f1_score(y_test, test_pred),
    "roc_auc": roc_auc_score(y_test, test_prob),
    "pr_auc": average_precision_score(y_test, test_prob),
}
print(cm)
print(results)

 

 

Classification subgroup analysis

Subgroup analysis should be defined before inspecting test outcomes whenever possible. Groups may reflect operational segments, data sources, acquisition channels, geography, device type, target prevalence, or demographic attributes when their use is legally and ethically appropriate.

PYTHON  •  Compare classification performance across a pre-defined feature band

import pandas as pd
from sklearn.metrics import precision_score, recall_score, f1_score

report = X_test.copy()
report["y_true"= y_test.to_numpy()
report["y_pred"= test_pred
report["radius_band"= pd.qcut(
    X_final["mean radius"], q=3, labels=["low""mid""high"]
).reindex(X_test.index)

rows = []
for name, g in report.groupby("radius_band", observed=True):
    rows.append({
        "group": name,
        "n"len(g),
        "precision": precision_score(g.y_true, g.y_pred, zero_division=0),
        "recall": recall_score(g.y_true, g.y_pred, zero_division=0),
        "f1": f1_score(g.y_true, g.y_pred, zero_division=0),
    })
print(pd.DataFrame(rows))

 

 

INTERPRETATION  A subgroup result based on very few observations should be treated as uncertain evidence, not as a definitive ranking. Always report subgroup sample sizes together with performance.

 

Regression test report

Table 33.5. Recommended regression evidence

Item

What to report

Why it matters

MAEMean absolute errorAverage absolute error in target units.
RMSERoot mean squared errorEmphasizes larger errors and stays in target units.
Coefficient of determinationCompares squared error with a mean-prediction baseline.
Residual analysisResidual distribution and plotsReveals bias, heteroscedasticity, outliers, and nonlinearity.
Error by target rangeMAE / RMSE across target bandsShows whether performance changes with target magnitude.
Subgroup analysisMetrics by operational groupsChecks whether aggregate accuracy hides weak segments.

 

PYTHON  •  Regression: train a finalized pipeline and evaluate the protected test set

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

Xr, yr = load_diabetes(return_X_y=True, as_frame=True)
Xr_dev, Xr_test, yr_dev, yr_test = train_test_split(
    Xr, yr, test_size=0.20, random_state=42
)

final_reg = make_pipeline(StandardScaler(), Ridge(alpha=10.0))
final_reg.fit(Xr_dev, yr_dev)  # alpha was assumed frozen earlier
pred = final_reg.predict(Xr_test)

mae = mean_absolute_error(yr_test, pred)
rmse = mean_squared_error(yr_test, pred) ** 0.5
r2 = r2_score(yr_test, pred)
print({"MAE": mae, "RMSE": rmse, "R2": r2})

 

 

PYTHON  •  Regression: residual and target-range analysis

import pandas as pd

reg_report = pd.DataFrame({
    "y_true": yr_test.to_numpy(),
    "y_pred": pred,
})
reg_report["residual"= reg_report.y_true - reg_report.y_pred
reg_report["abs_error"= reg_report.residual.abs()
reg_report["target_band"= pd.qcut(
    reg_report.y_true, q=4, labels=["Q1""Q2""Q3""Q4"]
)

print(reg_report.groupby("target_band", observed=True)["abs_error"]
      .agg(["count""mean""median""max"]))

 

 

33.4 Honest reporting

A final evaluation has scientific and operational value only when it is reported honestly. Strong reporting describes what the model does well, where it fails, how certain the estimates are, and under what conditions the results are expected to generalize. Hiding unfavorable evidence makes the report less useful for deployment decisions.

Table 33.6. Honest-reporting checklist

Principle

Good reporting practice

Weak reporting practice

Report positive and negative resultsShow both strong metrics and important error modes.Show only the strongest metric.
Avoid metric cherry-pickingUse pre-specified primary/secondary metrics.Choose whichever metric makes the model look best.
State data limitsDescribe sample size, coverage, missing populations, and collection conditions.Present results as universal without discussing coverage.
State expected use conditionsDescribe intended population, operating range, and threshold.Leave deployment conditions implicit.
State uncertaintyReport variability, confidence intervals, or sample-size caveats when useful.Treat one point estimate as exact.
Document deviationsExplain any protocol changes or post-hoc analyses.Quietly alter the process after seeing test results.

 

Uncertainty around a final score

A test metric is an estimate based on a finite sample. If the test set is small, a few observations can materially change the reported value. Confidence intervals or bootstrap intervals can communicate this uncertainty when the application requires more than a point estimate.

PYTHON  •  Bootstrap a confidence interval for classification F1

import numpy as np
from sklearn.metrics import f1_score

rng = np.random.default_rng(42)
scores = []
y_true = y_test.to_numpy()

forinrange(2000):
    idx = rng.integers(0,  len(y_true), size=len(y_true))
    scores.append(f1_score(y_true[idx], test_pred[idx], zero_division=0))

low, high = np.percentile(scores, [2.597.5])
print(f"F1 95% bootstrap interval: [{low:.3f}, {high:.3f}]")

 

 

CAUTION   A confidence interval quantifies sampling uncertainty under the observed test distribution. It does not automatically capture future distribution shift, data-quality changes, or deployment feedback effects.

 

Practical activity — Produce a formal final-test evaluation section

Students now act as the final evaluation team. They receive a model whose architecture, preprocessing, hyperparameters, selected features, and decision threshold have already been finalized. Their task is to evaluate the model once on the protected test set and write a formal report section that could be included in a technical document or project submission.

Activity requirements

  1. State the model specification and confirm that all development choices were frozen before test evaluation.
  2. Describe the test set: size, target distribution, important inclusion/exclusion conditions, and whether it reflects the expected use environment.
  3. Evaluate the final model using the primary and secondary metrics defined before opening the test results.
  4. Include the confusion matrix for classification or residual/error analysis for regression.
  5. Report the fixed classification threshold when applicable.
  6. Perform at least one pre-defined subgroup or target-range analysis and include subgroup sample sizes.
  7. Identify at least two strengths and two limitations revealed by the final evaluation.
  8. State uncertainty and any conditions under which the reported results may not generalize.
  9. Conclude with a deployment recommendation: proceed, proceed with constraints/monitoring, or do not proceed.

Formal classification template

Table 33.7. Suggested final-test report structure

Subsection

What the student should write

Final modelPipeline, features, algorithm, hyperparameters, frozen threshold, model version.
Test protocolHow the test set was protected and when it was opened.
Test populationSample size, class balance, relevant operational characteristics.
Main resultsConfusion matrix, precision, recall, F1, ROC AUC / PR AUC, threshold.
Subgroup resultsMetrics and sample size for pre-defined groups.
Error analysisImportant false positives / false negatives and plausible patterns.
Uncertainty & limitsSampling uncertainty, dataset coverage, likely shift risks.
RecommendationDeployment decision and required monitoring or constraints.

 

Formal regression template

Table 33.8. Suggested regression final-test report structure

Subsection

What the student should write

Final modelPipeline, features, algorithm, hyperparameters, model version.
Test protocolEvidence that the test set was not used during selection or tuning.
Main resultsMAE, RMSE, R² and target-unit interpretation.
Residual analysisBias, spread, outliers, heteroscedasticity, nonlinearity.
Target-range resultsError by low / medium / high target bands or quantiles.
Subgroup resultsOperational-group error and sample sizes.
Uncertainty & limitsFinite-sample uncertainty and dataset coverage.
RecommendationDeployment decision, safeguards, and monitoring requirements.

 

SUBMISSION STANDARD  The final-test section should be reproducible, concise, and self-contained. A reader should be able to understand what was tested, how it was tested, the exact operating rule, the observed performance, the main weaknesses, and the deployment recommendation without reading the entire development notebook.

 

Discussion questions

1. Why is a high validation score not a substitute for a final test evaluation?

2. What should happen if the final test recall is lower than expected?

3. Why must a tuned decision threshold be frozen before the test set is opened?

4. When is it reasonable to combine training and validation data for the final refit?

5. Why should subgroup sample sizes appear next to subgroup metrics?

6. What is the difference between sampling uncertainty and distribution-shift risk?

7. Which result would make you recommend “proceed with constraints” rather than “proceed”?

Chapter summary

Table 33.9. Final-test discipline at a glance

Stage

Correct action

Key question

Before testFreeze model, preprocessing, hyperparameters, features, threshold, and metrics.Are all decisions complete?
Final refitTrain the fixed pipeline on the full development data.Am I only re-estimating parameters?
Test evaluationEvaluate once under fixed conditions.Is this a measurement rather than another experiment?
DiagnosticsInspect confusion/residuals, target ranges, and pre-defined subgroups.Where does the model fail?
ReportingInclude positive and negative findings, uncertainty, and dataset limits.Would a skeptical reader understand the risks?
DecisionRecommend proceed / constrained proceed / do not proceed.Is the evidence sufficient for the intended use?

 

NEXT STEP   A final test report closes model development, but not the model lifecycle. Deployment requires versioning, monitoring, drift detection, incident procedures, and a plan for future re-evaluation when data or operating conditions change.
Train a Supervised Machine Learning Model
1 Chapter 1 — Introduction to Machine Learning 2 Chapter 2 — Understanding Supervised Learning 3 Chapter 3 — The Complete Supervised Learning Workflow 4 Chapter 4 — Defining the Machine Learning Problem 5 Chapter 5 — Loading and Inspecting Data 6 Chapter 6 — Exploratory Data Analysis 7 Chapter 7 — Cleaning the Dataset 8 Chapter 8 — Feature and Target Preparation 9 Chapter 9 — Splitting the Dataset Correctly 10 Chapter 10 — Numerical Feature Preprocessing 11 Chapter 11 — Encoding Categorical Features 12 Chapter 12 — Preprocessing Pipelines 13 Chapter 13 — Baseline Models 14 Chapter 14 — Logistic Regression 15 Chapter 15 — K-Nearest Neighbors Classification 16 Chapter 16 — Decision Tree Classification 17 Chapter 17 — Ensemble Classification Models 18 Chapter 18 — Support Vector Machines 19 Chapter 19 — Linear Regression 20 Chapter 20 — Regularized Regression 21 Chapter 21 — Tree-Based Regression 22 Chapter 22 — Confusion Matrix and Basic Metrics 23 Chapter 23 — Probability-Based Classification Evaluation 24 Chapter 24 — Regression Metrics 25 Chapter 25 — Residual Analysis 26 Chapter 26 — Underfitting and Overfitting 27 Chapter 27 — Cross-Validation 28 Chapter 28 — Feature Engineering 29 Chapter 29 — Feature Selection 30 Chapter 30 — Hyperparameter Tuning 31 Chapter 31 — Handling Imbalanced Classification 32 Chapter 32 — Designing a Fair Model Comparison 33 Chapter 33 — Final Test Evaluation 34 Chapter 34 — Global Model Interpretation 35 Chapter 35 — Local Prediction Explanation 36 Chapter 36 — Error Analysis and Robustness 37 Chapter 37 — Fairness and Ethical Considerations 38 Chapter 38 — Model Persistence 39 Chapter 39 — Building a Basic Prediction Application 40 Chapter 40 — Monitoring a Supervised Model