Skip to content

Commit

Permalink
fix: correct negative sigma preds in LocallyAdaptiveCP (#53)
Browse files Browse the repository at this point in the history
* LocallyAdaptiveCP: authorise negative MAD values

* ops: updated setup and doc config files

* doc: added experimental pytorch wrapper

---------

Co-authored-by: M-Mouhcine <[email protected]>
  • Loading branch information
jdalch and M-Mouhcine committed Mar 20, 2024
1 parent 3b9e4e5 commit 2c65579
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 12 deletions.
4 changes: 2 additions & 2 deletions deel/puncc/api/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"""
This module provides experimental features. Use cautiously.
"""
import pkgutil
import importlib.util

if pkgutil.find_loader("torch") is not None:
if importlib.util.find_spec("torch") is not None:
import torch

class TorchPredictor:
Expand Down
13 changes: 8 additions & 5 deletions deel/puncc/api/nonconformity_scores.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

import numpy as np

from deel.puncc.api.utils import EPSILON
from deel.puncc.api.utils import logit_normalization_check
from deel.puncc.api.utils import supported_types_check

Expand Down Expand Up @@ -206,7 +205,7 @@ def absolute_difference(y_pred: Iterable, y_true: Iterable) -> Iterable:
return abs(difference(y_pred, y_true))


def scaled_ad(Y_pred: Iterable, y_true: Iterable) -> Iterable:
def scaled_ad(Y_pred: Iterable, y_true: Iterable, eps: float = 1e-12) -> Iterable:
"""Scaled Absolute Deviation, normalized by an estimation of the conditional
mean absolute deviation (conditional MAD). Considering
:math:`Y_{\\text{pred}} = (\mu_{\\text{pred}}, \sigma_{\\text{pred}})`:
Expand All @@ -218,6 +217,7 @@ def scaled_ad(Y_pred: Iterable, y_true: Iterable) -> Iterable:
:param Iterable Y_pred: point and conditional MAD predictions.
:math:`Y_{\\text{pred}}=(y_{\\text{pred}}, \sigma_{\\text{pred}})`
:param Iterable y_true: true labels.
:param float eps: small positive value to avoid division by negative or zero.
:returns: scaled absolute deviation.
:rtype: Iterable
Expand All @@ -244,9 +244,12 @@ def scaled_ad(Y_pred: Iterable, y_true: Iterable) -> Iterable:

# MAD then Scaled MAD and computed
mean_absolute_deviation = absolute_difference(y_pred, y_true)
if np.any(sigma_pred < 0):
raise RuntimeError("All MAD predictions should be positive.")
return mean_absolute_deviation / (sigma_pred + EPSILON)
if np.any(sigma_pred + eps <= 0):
print("Warning: calibration points with MAD predictions"
" below -eps won't be used for calibration.")

nonneg = sigma_pred + eps > 0
return mean_absolute_deviation[nonneg] / (sigma_pred[nonneg] + eps)


def cqr_score(Y_pred: Iterable, y_true: Iterable) -> Iterable:
Expand Down
15 changes: 12 additions & 3 deletions deel/puncc/api/prediction_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def constant_interval(


def scaled_interval(
Y_pred: Iterable, scores_quantile: np.ndarray
Y_pred: Iterable, scores_quantile: np.ndarray, eps: float = 1e-12
) -> Tuple[np.ndarray]:
"""Scaled prediction interval centered on `y_pred`. Considering
:math:`Y_{\\text{pred}} = (\mu_{\\text{pred}}, \sigma_{\\text{pred}})`,
Expand All @@ -229,6 +229,7 @@ def scaled_interval(
:param Iterable y_pred: predictions.
:param ndarray scores_quantile: quantile of nonconformity scores computed
on a calibration set for a given :math:`\\alpha`.
:param float eps: small positive value to avoid singleton sets.
:returns: scaled prediction intervals :math:`I`.
:rtype: Tuple[ndarray]
Expand All @@ -249,8 +250,16 @@ def scaled_interval(
else:
y_pred, sigma_pred = Y_pred[:, 0], Y_pred[:, 1]

y_lo = y_pred - scores_quantile * sigma_pred
y_hi = y_pred + scores_quantile * sigma_pred
if np.any(sigma_pred + eps <= 0):
print("Warning: test points with MAD predictions below -eps"
" will have infinite sized prediction intervals.")

fints = sigma_pred + eps > 0
y_lo, y_hi = np.zeros_like(y_pred), np.zeros_like(y_pred)
y_lo[fints] = y_pred[fints] - scores_quantile * (sigma_pred[fints] + eps)
y_hi[fints] = y_pred[fints] + scores_quantile * (sigma_pred[fints] + eps)
y_lo[~fints] = -np.inf
y_hi[~fints] = np.inf
return y_lo, y_hi


Expand Down
1 change: 1 addition & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ API's Modules

conformalization
prediction
experimental
calibration
splitting
utils
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
author = "Mouhcine Mendil, Luca Mossina and Joseba Dalmau"

# The full version, including alpha/beta/rc tags
release = "0.7.6"
release = "0.7.7"


# -- General configuration ---------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions docs/source/experimental.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _experimental:

Experimental
============

.. automodule:: experimental
:members:
:undoc-members:
:show-inheritance:
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

setuptools.setup(
name="puncc",
version="0.7.6",
version="0.7.7",
author=", ".join(["Mouhcine Mendil", "Luca Mossina", "Joseba Dalmau"]),
author_email=", ".join(
[
Expand Down

0 comments on commit 2c65579

Please sign in to comment.