Question 1
Why should you not repeatedly select models using the test-set score?
TRACK A · COMPOSITE SOURCE · Composite source · mapped to lessons 1.2.4, 1.2.6, 1.4.3 and 1.5.1–1.5.7
Learn to evaluate models honestly: split training, validation and test data, choose regression and classification metrics, and understand class imbalance, thresholds, overfitting and data leakage.
A model can perform almost perfectly on training examples and poorly on new data. Quality must therefore be assessed on data that were not used to fit parameters. Evaluation is an experiment designed to approximate the conditions in which the model will be used.
The training set fits model parameters. The validation set compares configurations, selects hyperparameters and decision thresholds. The test set is reserved for a final evaluation. Repeatedly tuning against the test score makes it part of development and biases the result.
A baseline is a simple reference solution. For regression it may predict the training-set mean or median; for classification it may always predict the most frequent class. A complex model should demonstrate measurable improvement to justify its cost.
MAE is mean absolute error in the target's original units. MSE averages squared errors and penalizes large mistakes more strongly. RMSE is the square root of MSE and returns to original units. R² compares the model with a mean-prediction baseline; it can be negative and is not a percentage of correct predictions.
MAE: mean absolute error. MSE: mean squared error. RMSE: root mean squared error. R²: coefficient of determination relative to a mean baseline.
A confusion matrix contains TP (true positive), TN (true negative), FP (false positive) and FN (false negative). Accuracy is the fraction of correct predictions. Precision = TP/(TP+FP) measures the reliability of positive predictions. Recall = TP/(TP+FN) measures how many actual positives were detected. F1 is their harmonic mean.
Accuracy = (TP+TN)/N · Precision = TP/(TP+FP) · Recall = TP/(TP+FN) · F1 = 2PR/(P+R)
If only 10 of 1,000 inspections contain a failure, a model that always predicts 'no failure' achieves 99% accuracy but zero recall for failures. For rare events inspect the confusion matrix, precision, recall, precision–recall curves and the real cost of errors.
A classifier often produces a score or probability. A threshold converts it into a class. Lowering the threshold usually increases recall and false alarms; raising it may improve precision while missing more events. Choose the threshold using the consequences of FP and FN, not by assuming 0.5 is universally correct.
Underfitting means the model is too simple; overfitting means it adapts too closely to training data; generalization is performance on new data. Data leakage occurs when future or test-set information enters features or preprocessing. Imputation, scaling and feature selection must be fitted on training data only.
Random splitting is not always appropriate. For forecasting, split by time: train on the past and evaluate on the future. If multiple rows belong to one user, device or building, group splitting may keep related records together. Small datasets may use cross-validation with a suitable split strategy.
The ROC curve plots True Positive Rate (Recall) against False Positive Rate as the decision threshold changes. ROC-AUC is the area under this curve and describes how well the model ranks positive cases above negative cases. A value near 0.5 indicates random ranking; 1.0 indicates perfect separation on the evaluated sample. ROC-AUC does not identify the best operating threshold.
The Precision–Recall curve shows precision and recall across thresholds. Average Precision (AP) summarizes the precision-recall ranking. With a rare positive class, the PR curve is often more informative than ROC: many true negatives can keep the false-positive rate small even when the number of false alarms is substantial. The baseline AP depends on positive-class prevalence.
Neither ROC-AUC nor AP replaces confusion-matrix analysis, probability calibration, error-cost analysis or evaluation on independent data. Compare candidate models using the same split strategy.
K-fold cross-validation divides the training portion into K folds. The model is trained K times, each time using K−1 folds and validating on the remaining fold. The mean and spread of the metric show how sensitive the result is to a particular split. For imbalanced classification, StratifiedKFold approximately preserves class proportions. Use GroupKFold when observations from the same entity must stay together, and a chronological strategy such as TimeSeriesSplit for time series.
Run cross-validation only within the training portion. Keep the test set untouched until final evaluation. If CV is used to select hyperparameters, do not tune repeatedly against the test set. Put preprocessing steps in a Pipeline so they are fitted separately inside each training fold.
Compare a baseline and two models using the same split. For regression calculate MAE and RMSE; for classification calculate the confusion matrix, precision and recall. Document limitations. A metric is not absolute truth: it answers a specific question about specific data.
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.linear_model import LinearRegression
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = LinearRegression().fit(X_train, y_train)
pred = model.predict(X_test)
print("MAE:", mean_absolute_error(y_test, pred))
print("RMSE:", mean_squared_error(y_test, pred) ** 0.5)
print("R2:", r2_score(y_test, pred))Important: fit all data transformations on the training set only.
This experiment uses synthetic data to demonstrate stratified splitting, a baseline classifier, a preprocessing Pipeline and several metrics. In a real project, replace the generator with prepared features and define the costs of false alarms and missed cases.
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
from sklearn.dummy import DummyClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (confusion_matrix, precision_score, recall_score,
f1_score, roc_auc_score, average_precision_score)
X, y = make_classification(
n_samples=1200, n_features=12, weights=[0.9, 0.1],
class_sep=0.8, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, stratify=y, random_state=42
)
models = {
"baseline": DummyClassifier(strategy="prior"),
"logistic": make_pipeline(StandardScaler(),
LogisticRegression(max_iter=1000))
}
for name, model in models.items():
model.fit(X_train, y_train)
pred = model.predict(X_test)
score = model.predict_proba(X_test)[:, 1]
print("\\n", name)
print("confusion matrix:\\n", confusion_matrix(y_test, pred))
print("precision:", precision_score(y_test, pred, zero_division=0))
print("recall:", recall_score(y_test, pred, zero_division=0))
print("f1:", f1_score(y_test, pred, zero_division=0))
print("roc_auc:", roc_auc_score(y_test, score))
print("average_precision:", average_precision_score(y_test, score))
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(models["logistic"], X_train, y_train,
cv=cv, scoring="average_precision")
print("CV AP mean/std:", cv_scores.mean(), cv_scores.std())Compare the baseline and logistic model on exactly the same test cases. Inspect the confusion matrix and check whether a 0.5 threshold reflects the real costs of FP and FN. CV AP measures stability within the training data; the test score remains the final independent estimate.
Why should you not repeatedly select models using the test-set score?
Why can accuracy be misleading under class imbalance?
How should data be split for forecasting future events?
If accuracy is 99% and recall for a rare class is 0%, can the model miss every event in that class?