From 90a2aad03c60ca2747e534f08f677a68080c5f82 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Thu, 29 Oct 2026 15:39:35 +0000 Subject: [PATCH] [tutorials] Drop sklearn dependency from RDataLoader Higgs tutorial Right now, the `ml_dataloader_Higgs_Classification.py` tutorial test is failing when sklearn is not installed. The tutorial only used scikit-learn for `roc_curve`, which is a few lines of NumPy. Replace it with a small local helper that is equivalent to `sklearn.metrics.roc_curve(..., drop_intermediate=False)`, so the tutorial can run without scikit-learn installed. Also plot the full ROC curve: the previous code dropped the last point, i.e. the (1, 1) end of the curve. Register the tutorial in the `requires_numpy` fixture list, since it now imports NumPy directly, and in `requires_matplotlib`, which it had always needed but never declared. The alternative to this commit would have been to register the `sklearn` dependency in the `tutorials/CMakeLists.txt`, but reducing the test dependency on external Python packages is also a goal in itself. (cherry picked from commit 6f7cb11d8273fbda7be4cffa3b968d9a19f6e9f7) --- tutorials/CMakeLists.txt | 2 ++ .../ml_dataloader_Higgs_Classification.py | 32 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 2ec7daf493ea6..50db54b72ea18 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -912,6 +912,7 @@ if(pyroot) hist/hist007_TH1_liveupdate_uhi.py hist/hist010_TH1_two_scales_uhi.py hist/hist015_TH1_read_and_draw_uhi.py + machine_learning/ml_dataloader_Higgs_Classification.py ) file(GLOB requires_numba RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -959,6 +960,7 @@ if(pyroot) hist/hist007_TH1_liveupdate_uhi.py hist/hist010_TH1_two_scales_uhi.py hist/hist015_TH1_read_and_draw_uhi.py + machine_learning/ml_dataloader_Higgs_Classification.py ) file(GLOB requires_mplhep RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tutorials/machine_learning/ml_dataloader_Higgs_Classification.py b/tutorials/machine_learning/ml_dataloader_Higgs_Classification.py index 115cb6e41860c..67307a51fe0cd 100644 --- a/tutorials/machine_learning/ml_dataloader_Higgs_Classification.py +++ b/tutorials/machine_learning/ml_dataloader_Higgs_Classification.py @@ -13,8 +13,8 @@ ## \authors Jonah Ascoli (CERN), Martin Foll (CERN, University of Oslo (UiO)), Silia Taider (CERN) import matplotlib.pyplot as plt +import numpy as np import ROOT -import sklearn.metrics as skl import torch from matplotlib import use from torch import nn @@ -239,10 +239,36 @@ def print_epoch_summary(epoch: int, val_loss: float, val_accuracy: float): plt.savefig("loss_curve") print("Loss curve saved to loss_curve.png") -fpr, tpr, thresholds = skl.roc_curve(test_true, test_preds) + +def roc_curve(y_true, y_score): + """Compute the receiver operating characteristic (ROC) curve. + + Returns the false positive rate and the true positive rate obtained when + classifying as signal everything with a score above a threshold, for every + distinct score value used as threshold, starting with the (0, 0) point. + + This is equivalent to sklearn.metrics.roc_curve(y_true, y_score, drop_intermediate=False), + which additionally returns the thresholds. + """ + y_true = np.asarray(y_true, dtype=bool).ravel() + y_score = np.asarray(y_score, dtype=float).ravel() + # Sort by decreasing score, so that lowering the threshold means walking along the arrays + order = np.argsort(-y_score, kind="stable") + y_true = y_true[order] + y_score = y_score[order] + # Keep only the last entry of each run of equal scores: tied scores share a threshold + distinct = np.r_[np.where(np.diff(y_score))[0], y_true.size - 1] + true_positives = np.cumsum(y_true)[distinct] + false_positives = np.cumsum(~y_true)[distinct] + tpr = np.r_[0.0, true_positives / true_positives[-1]] + fpr = np.r_[0.0, false_positives / false_positives[-1]] + return fpr, tpr + + +fpr, tpr = roc_curve(test_true, test_preds) fig = plt.figure() ax = plt.axes() -ax.plot(fpr[:-1], tpr[:-1]) +ax.plot(fpr, tpr) plt.title("ROC curve") plt.xlabel("False positive rate") plt.ylabel("True positive rate")