Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

OmarManzoor
Copy link
Contributor

@OmarManzoor OmarManzoor commented Sep 22, 2025

Reference Issues/PRs

Address #31869

What does this implement/fix? Explain your changes.

  • Attempts to add array API support for temperature scaling in CalibratedClassifierCV

Notes:

  • find_minimum from scipy.optimize.elementwise that supports the array API doesn't quite work because it works with arrays and if we use arrays as inputs and outputs it breaks for array-api-strict. If we stick with scalars it breaks at a point where an array is expected.
  • I tried simply adapting the multinomial loss for the array API and since minimize_scalar which we use currently simply uses scalar values within it's main computation loops, I convert the input to the array API when entering the _half_multinomial_loss function and converting to a float when returning from it. This has the drawback of converting back and forth between the cpu and gpu.
  • I don't think we can use array API consistently within CalibratedClassifierCV because it involves an estimator which we don't know supports array API or not and also involves cross validation before going on to the actual calibration computation.
  • I ran some benchmarks for just the _TemperatureScaling class on google colab and my local mac M1, the results are as follows which show they vary based on the scipy version:
scipy == 1.16.1

Avg execution_time for numpy: 7.254365730285644
Avg execution_time for torch mps: 7.542782831192016

Google colab with T4 GPU

Avg execution_time for numpy: 14.60176453590393
Avg execution_time for torch cuda: 10.203765702247619

___________________________________________________

scipy == 1.15.3

Avg execution_time for numpy: 7.495703768730164
Avg execution_time for torch mps: 1.769882321357727

Google colab with T4 GPU

Avg execution_time for numpy: 14.949613022804261
Avg execution_time for torch cuda: 0.7114695549011231

I noted the significant performance with scipy == 1.15.3 because I also tried running the benchmarks on a Kaggle kernel and since that supports Python 3.10 something the maximum scipy version that we can get there is 1.15.3.

Any other comments?

CC: @ogrisel @betatim @virchan @lesteve

What is your opinion? Should we support array API for this class?

Copy link

github-actions bot commented Sep 22, 2025

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: cdd876e. Link to the linter CI: here

@OmarManzoor
Copy link
Contributor Author

OmarManzoor commented Sep 22, 2025

Note: Since this is a DRAFT PR for now I left the bench.py file so that we can experiment if required.

@ogrisel
Copy link
Member

ogrisel commented Sep 23, 2025

find_minimum from scipy.optimize.elementwise that supports the array API doesn't quite work because it works with arrays and if we use arrays as inputs and outputs it breaks for array-api-strict. If we stick with scalars it breaks at a point where an array is expected.

Has this problem already been reported upstream?

I don't think we can use array API consistently within CalibratedClassifierCV because it involves an estimator which we don't know supports array API or not

I think it's fair for meta estimators to only support array API when the base estimator does (and let any exception raised by the underlying estimator bubble up otherwise).

and also involves cross validation before going on to the actual calibration computation.

How is that a problem? I think our cross-validation tools support array API, no? If not, we should fix that first.

I ran some benchmarks for just the _TemperatureScaling class on google colab and my local mac M1, the results are as follows which show they vary based on the scipy version:

Have you tried to use a profiler to understand the performance difference between scipy versions with torch/mps? Ideally, this should be reported as a performance regression upstream along with a minimal reproducer that does not involve scikit-learn.

@OmarManzoor
Copy link
Contributor Author

@ogrisel cross_val_predict currently does not support the array API.

Also I haven't reported either of the scipy issues that I noted. But wouldn't it be better for someone else to confirm my observations as well?

@ogrisel
Copy link
Member

ogrisel commented Sep 23, 2025

@ogrisel cross_val_predict currently does not support the array API.

Ah ok. This should indeed be addressed in a dedicated PR then.

@ogrisel
Copy link
Member

ogrisel commented Sep 23, 2025

I confirm that I observe a similar performance regression using SciPy 1.16 (vs 1.15) using the bench.py (with "mps" device and np/xp.float32 dtypes).

@OmarManzoor
Copy link
Contributor Author

I opened a scipy issue with respect to the slower runtimes that we noted: scipy/scipy#23670

Copy link
Member

@ogrisel ogrisel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is some preliminary feedback, some of which we already discussed in over discord.

calibrators.append(calibrator)
elif method == "temperature":
max_float_dtype = _max_precision_float_dtype(xp, xp_device)
predictions = xp.asarray(predictions, dtype=max_float_dtype, device=xp_device)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed if the underlying classifier supports array API.

Copy link
Contributor Author

@OmarManzoor OmarManzoor Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since we are going for the path in which we are supporting array API when the underlying estimator also supports array API, then yes these kinds of conversions in the CalibratedClassiferCV fit method and over here can be removed but I think we would first need to support array API for at least the cross_val_predict function so that we can deal with the case when ensemble=False. I think maybe this first PR should focus on ensemble being False. Because when we have ensemble=True I think our cv splitters are returning ndarrays for train, test indices which won't work when we are dealing with the array API. What do you think?

with config_context(array_api_dispatch=True):
cal_clf_xp = CalibratedClassifierCV(
FrozenEstimator(clf), cv=3, method="temperature", ensemble=False
).fit(X_cal_xp, y_cal_xp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should test with y_cal being an array of str instances here as well.

Copy link
Member

@virchan virchan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Array API version _half_multinomial_loss should be okay. I did some tests to compare it with HalfMultinomialLoss in addition to running test_half_multinomal_loss, and they all passed.

@OmarManzoor
Copy link
Contributor Author

The Array API version _half_multinomial_loss should be okay. I did some tests to compare it with HalfMultinomialLoss in addition to running test_half_multinomal_loss, and they all passed.

Thank you for verifying

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants