ins-pricing 0.4.5__py3-none-any.whl → 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ins_pricing/README.md +48 -22
- ins_pricing/__init__.py +142 -90
- ins_pricing/cli/BayesOpt_entry.py +58 -46
- ins_pricing/cli/BayesOpt_incremental.py +77 -110
- ins_pricing/cli/Explain_Run.py +42 -23
- ins_pricing/cli/Explain_entry.py +551 -577
- ins_pricing/cli/Pricing_Run.py +42 -23
- ins_pricing/cli/bayesopt_entry_runner.py +51 -16
- ins_pricing/cli/utils/bootstrap.py +23 -0
- ins_pricing/cli/utils/cli_common.py +256 -256
- ins_pricing/cli/utils/cli_config.py +379 -360
- ins_pricing/cli/utils/import_resolver.py +375 -358
- ins_pricing/cli/utils/notebook_utils.py +256 -242
- ins_pricing/cli/watchdog_run.py +216 -198
- ins_pricing/frontend/__init__.py +10 -10
- ins_pricing/frontend/app.py +132 -61
- ins_pricing/frontend/config_builder.py +33 -0
- ins_pricing/frontend/example_config.json +11 -0
- ins_pricing/frontend/example_workflows.py +1 -1
- ins_pricing/frontend/runner.py +340 -388
- ins_pricing/governance/__init__.py +20 -20
- ins_pricing/governance/release.py +159 -159
- ins_pricing/modelling/README.md +1 -1
- ins_pricing/modelling/__init__.py +147 -92
- ins_pricing/modelling/{core/bayesopt → bayesopt}/README.md +31 -13
- ins_pricing/modelling/{core/bayesopt → bayesopt}/__init__.py +64 -102
- ins_pricing/modelling/{core/bayesopt → bayesopt}/config_components.py +12 -0
- ins_pricing/modelling/{core/bayesopt → bayesopt}/config_preprocess.py +589 -552
- ins_pricing/modelling/{core/bayesopt → bayesopt}/core.py +987 -958
- ins_pricing/modelling/{core/bayesopt → bayesopt}/model_explain_mixin.py +296 -296
- ins_pricing/modelling/{core/bayesopt → bayesopt}/model_plotting_mixin.py +488 -548
- ins_pricing/modelling/{core/bayesopt → bayesopt}/models/__init__.py +27 -27
- ins_pricing/modelling/{core/bayesopt → bayesopt}/models/model_ft_components.py +349 -342
- ins_pricing/modelling/{core/bayesopt → bayesopt}/models/model_ft_trainer.py +921 -913
- ins_pricing/modelling/{core/bayesopt → bayesopt}/models/model_gnn.py +794 -785
- ins_pricing/modelling/{core/bayesopt → bayesopt}/models/model_resn.py +454 -446
- ins_pricing/modelling/bayesopt/trainers/__init__.py +19 -0
- ins_pricing/modelling/{core/bayesopt → bayesopt}/trainers/trainer_base.py +1294 -1282
- ins_pricing/modelling/{core/bayesopt → bayesopt}/trainers/trainer_ft.py +64 -56
- ins_pricing/modelling/{core/bayesopt → bayesopt}/trainers/trainer_glm.py +203 -198
- ins_pricing/modelling/{core/bayesopt → bayesopt}/trainers/trainer_gnn.py +333 -325
- ins_pricing/modelling/{core/bayesopt → bayesopt}/trainers/trainer_resn.py +279 -267
- ins_pricing/modelling/{core/bayesopt → bayesopt}/trainers/trainer_xgb.py +515 -313
- ins_pricing/modelling/bayesopt/utils/__init__.py +67 -0
- ins_pricing/modelling/bayesopt/utils/constants.py +21 -0
- ins_pricing/modelling/{core/bayesopt → bayesopt}/utils/distributed_utils.py +193 -186
- ins_pricing/modelling/bayesopt/utils/io_utils.py +7 -0
- ins_pricing/modelling/bayesopt/utils/losses.py +27 -0
- ins_pricing/modelling/bayesopt/utils/metrics_and_devices.py +17 -0
- ins_pricing/modelling/{core/bayesopt → bayesopt}/utils/torch_trainer_mixin.py +636 -623
- ins_pricing/modelling/{core/evaluation.py → evaluation.py} +113 -104
- ins_pricing/modelling/explain/__init__.py +55 -55
- ins_pricing/modelling/explain/metrics.py +27 -174
- ins_pricing/modelling/explain/permutation.py +237 -237
- ins_pricing/modelling/plotting/__init__.py +40 -36
- ins_pricing/modelling/plotting/compat.py +228 -0
- ins_pricing/modelling/plotting/curves.py +572 -572
- ins_pricing/modelling/plotting/diagnostics.py +163 -163
- ins_pricing/modelling/plotting/geo.py +362 -362
- ins_pricing/modelling/plotting/importance.py +121 -121
- ins_pricing/pricing/__init__.py +27 -27
- ins_pricing/pricing/factors.py +67 -56
- ins_pricing/production/__init__.py +35 -25
- ins_pricing/production/{predict.py → inference.py} +140 -57
- ins_pricing/production/monitoring.py +8 -21
- ins_pricing/reporting/__init__.py +11 -11
- ins_pricing/setup.py +1 -1
- ins_pricing/tests/production/test_inference.py +90 -0
- ins_pricing/utils/__init__.py +112 -78
- ins_pricing/utils/device.py +258 -237
- ins_pricing/utils/features.py +53 -0
- ins_pricing/utils/io.py +72 -0
- ins_pricing/utils/logging.py +34 -1
- ins_pricing/{modelling/core/bayesopt/utils → utils}/losses.py +125 -129
- ins_pricing/utils/metrics.py +158 -24
- ins_pricing/utils/numerics.py +76 -0
- ins_pricing/utils/paths.py +9 -1
- ins_pricing/utils/profiling.py +8 -4
- {ins_pricing-0.4.5.dist-info → ins_pricing-0.5.1.dist-info}/METADATA +1 -1
- ins_pricing-0.5.1.dist-info/RECORD +132 -0
- ins_pricing/modelling/core/BayesOpt.py +0 -146
- ins_pricing/modelling/core/__init__.py +0 -1
- ins_pricing/modelling/core/bayesopt/trainers/__init__.py +0 -19
- ins_pricing/modelling/core/bayesopt/utils/__init__.py +0 -86
- ins_pricing/modelling/core/bayesopt/utils/constants.py +0 -183
- ins_pricing/modelling/core/bayesopt/utils/io_utils.py +0 -126
- ins_pricing/modelling/core/bayesopt/utils/metrics_and_devices.py +0 -555
- ins_pricing/modelling/core/bayesopt/utils.py +0 -105
- ins_pricing/modelling/core/bayesopt/utils_backup.py +0 -1503
- ins_pricing/tests/production/test_predict.py +0 -233
- ins_pricing-0.4.5.dist-info/RECORD +0 -130
- {ins_pricing-0.4.5.dist-info → ins_pricing-0.5.1.dist-info}/WHEEL +0 -0
- {ins_pricing-0.4.5.dist-info → ins_pricing-0.5.1.dist-info}/top_level.txt +0 -0
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, Callable, Dict, Optional
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
classification_metrics,
|
|
10
|
-
regression_metrics,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass
|
|
15
|
-
class CalibrationResult:
|
|
16
|
-
method: str
|
|
17
|
-
calibrator: Any
|
|
18
|
-
|
|
19
|
-
def predict(self, scores: np.ndarray) -> np.ndarray:
|
|
20
|
-
if self.method == "sigmoid":
|
|
21
|
-
return self.calibrator.predict_proba(scores.reshape(-1, 1))[:, 1]
|
|
22
|
-
return self.calibrator.transform(scores)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def select_threshold(
|
|
26
|
-
y_true: np.ndarray,
|
|
27
|
-
y_pred: np.ndarray,
|
|
28
|
-
*,
|
|
29
|
-
metric: str = "f1",
|
|
30
|
-
min_positive_rate: Optional[float] = None,
|
|
31
|
-
grid: int = 99,
|
|
32
|
-
) -> Dict[str, float]:
|
|
33
|
-
y_true = np.asarray(y_true, dtype=float).reshape(-1)
|
|
34
|
-
y_pred = np.asarray(y_pred, dtype=float).reshape(-1)
|
|
35
|
-
thresholds = np.linspace(0.01, 0.99, max(2, int(grid)))
|
|
36
|
-
best = {"threshold": 0.5, "score": -1.0}
|
|
37
|
-
for thr in thresholds:
|
|
38
|
-
pred_label = (y_pred >= thr).astype(float)
|
|
39
|
-
pos_rate = float(np.mean(pred_label))
|
|
40
|
-
if min_positive_rate is not None and pos_rate < float(min_positive_rate):
|
|
41
|
-
continue
|
|
42
|
-
metrics = classification_metrics(y_true, y_pred, threshold=float(thr))
|
|
43
|
-
precision = metrics.get("precision", 0.0)
|
|
44
|
-
recall = metrics.get("recall", 0.0)
|
|
45
|
-
f1 = 0.0 if (precision + recall) == 0 else 2 * precision * recall / (precision + recall)
|
|
46
|
-
score = f1 if metric == "f1" else metrics.get(metric, f1)
|
|
47
|
-
if score > best["score"]:
|
|
48
|
-
best = {"threshold": float(thr), "score": float(score)}
|
|
49
|
-
return best
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def calibrate_predictions(
|
|
53
|
-
y_true: np.ndarray,
|
|
54
|
-
y_pred: np.ndarray,
|
|
55
|
-
*,
|
|
56
|
-
method: str = "sigmoid",
|
|
57
|
-
) -> CalibrationResult:
|
|
58
|
-
from sklearn.isotonic import IsotonicRegression
|
|
59
|
-
from sklearn.linear_model import LogisticRegression
|
|
60
|
-
|
|
61
|
-
y_true = np.asarray(y_true, dtype=float).reshape(-1)
|
|
62
|
-
y_pred = np.asarray(y_pred, dtype=float).reshape(-1)
|
|
63
|
-
method = str(method or "sigmoid").strip().lower()
|
|
64
|
-
if method in {"platt", "sigmoid", "logistic"}:
|
|
65
|
-
model = LogisticRegression(max_iter=200)
|
|
66
|
-
model.fit(y_pred.reshape(-1, 1), y_true)
|
|
67
|
-
return CalibrationResult(method="sigmoid", calibrator=model)
|
|
68
|
-
if method in {"isotonic"}:
|
|
69
|
-
model = IsotonicRegression(out_of_bounds="clip")
|
|
70
|
-
model.fit(y_pred, y_true)
|
|
71
|
-
return CalibrationResult(method="isotonic", calibrator=model)
|
|
72
|
-
raise ValueError(f"Unsupported calibration method: {method}")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def bootstrap_ci(
|
|
76
|
-
metric_fn: Callable[[np.ndarray, np.ndarray, Optional[np.ndarray]], float],
|
|
77
|
-
y_true: np.ndarray,
|
|
78
|
-
y_pred: np.ndarray,
|
|
79
|
-
*,
|
|
80
|
-
weight: Optional[np.ndarray] = None,
|
|
81
|
-
n_samples: int = 200,
|
|
82
|
-
ci: float = 0.95,
|
|
83
|
-
seed: Optional[int] = None,
|
|
84
|
-
) -> Dict[str, float]:
|
|
85
|
-
rng = np.random.default_rng(seed)
|
|
86
|
-
y_true = np.asarray(y_true).reshape(-1)
|
|
87
|
-
y_pred = np.asarray(y_pred).reshape(-1)
|
|
88
|
-
if weight is not None:
|
|
89
|
-
weight = np.asarray(weight).reshape(-1)
|
|
90
|
-
n = len(y_true)
|
|
91
|
-
stats = []
|
|
92
|
-
for _ in range(max(1, int(n_samples))):
|
|
93
|
-
idx = rng.integers(0, n, size=n)
|
|
94
|
-
y_t = y_true[idx]
|
|
95
|
-
y_p = y_pred[idx]
|
|
96
|
-
w_t = weight[idx] if weight is not None else None
|
|
97
|
-
stats.append(float(metric_fn(y_t, y_p, w_t)))
|
|
98
|
-
stats = np.asarray(stats, dtype=float)
|
|
99
|
-
alpha = (1.0 - float(ci)) / 2.0
|
|
100
|
-
low = float(np.quantile(stats, alpha))
|
|
101
|
-
high = float(np.quantile(stats, 1.0 - alpha))
|
|
102
|
-
return {"mean": float(np.mean(stats)), "low": low, "high": high}
|
|
103
|
-
|
|
104
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Callable, Dict, Optional
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from ins_pricing.production.monitoring import (
|
|
9
|
+
classification_metrics,
|
|
10
|
+
regression_metrics,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class CalibrationResult:
|
|
16
|
+
method: str
|
|
17
|
+
calibrator: Any
|
|
18
|
+
|
|
19
|
+
def predict(self, scores: np.ndarray) -> np.ndarray:
|
|
20
|
+
if self.method == "sigmoid":
|
|
21
|
+
return self.calibrator.predict_proba(scores.reshape(-1, 1))[:, 1]
|
|
22
|
+
return self.calibrator.transform(scores)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def select_threshold(
|
|
26
|
+
y_true: np.ndarray,
|
|
27
|
+
y_pred: np.ndarray,
|
|
28
|
+
*,
|
|
29
|
+
metric: str = "f1",
|
|
30
|
+
min_positive_rate: Optional[float] = None,
|
|
31
|
+
grid: int = 99,
|
|
32
|
+
) -> Dict[str, float]:
|
|
33
|
+
y_true = np.asarray(y_true, dtype=float).reshape(-1)
|
|
34
|
+
y_pred = np.asarray(y_pred, dtype=float).reshape(-1)
|
|
35
|
+
thresholds = np.linspace(0.01, 0.99, max(2, int(grid)))
|
|
36
|
+
best = {"threshold": 0.5, "score": -1.0}
|
|
37
|
+
for thr in thresholds:
|
|
38
|
+
pred_label = (y_pred >= thr).astype(float)
|
|
39
|
+
pos_rate = float(np.mean(pred_label))
|
|
40
|
+
if min_positive_rate is not None and pos_rate < float(min_positive_rate):
|
|
41
|
+
continue
|
|
42
|
+
metrics = classification_metrics(y_true, y_pred, threshold=float(thr))
|
|
43
|
+
precision = metrics.get("precision", 0.0)
|
|
44
|
+
recall = metrics.get("recall", 0.0)
|
|
45
|
+
f1 = 0.0 if (precision + recall) == 0 else 2 * precision * recall / (precision + recall)
|
|
46
|
+
score = f1 if metric == "f1" else metrics.get(metric, f1)
|
|
47
|
+
if score > best["score"]:
|
|
48
|
+
best = {"threshold": float(thr), "score": float(score)}
|
|
49
|
+
return best
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def calibrate_predictions(
|
|
53
|
+
y_true: np.ndarray,
|
|
54
|
+
y_pred: np.ndarray,
|
|
55
|
+
*,
|
|
56
|
+
method: str = "sigmoid",
|
|
57
|
+
) -> CalibrationResult:
|
|
58
|
+
from sklearn.isotonic import IsotonicRegression
|
|
59
|
+
from sklearn.linear_model import LogisticRegression
|
|
60
|
+
|
|
61
|
+
y_true = np.asarray(y_true, dtype=float).reshape(-1)
|
|
62
|
+
y_pred = np.asarray(y_pred, dtype=float).reshape(-1)
|
|
63
|
+
method = str(method or "sigmoid").strip().lower()
|
|
64
|
+
if method in {"platt", "sigmoid", "logistic"}:
|
|
65
|
+
model = LogisticRegression(max_iter=200)
|
|
66
|
+
model.fit(y_pred.reshape(-1, 1), y_true)
|
|
67
|
+
return CalibrationResult(method="sigmoid", calibrator=model)
|
|
68
|
+
if method in {"isotonic"}:
|
|
69
|
+
model = IsotonicRegression(out_of_bounds="clip")
|
|
70
|
+
model.fit(y_pred, y_true)
|
|
71
|
+
return CalibrationResult(method="isotonic", calibrator=model)
|
|
72
|
+
raise ValueError(f"Unsupported calibration method: {method}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def bootstrap_ci(
|
|
76
|
+
metric_fn: Callable[[np.ndarray, np.ndarray, Optional[np.ndarray]], float],
|
|
77
|
+
y_true: np.ndarray,
|
|
78
|
+
y_pred: np.ndarray,
|
|
79
|
+
*,
|
|
80
|
+
weight: Optional[np.ndarray] = None,
|
|
81
|
+
n_samples: int = 200,
|
|
82
|
+
ci: float = 0.95,
|
|
83
|
+
seed: Optional[int] = None,
|
|
84
|
+
) -> Dict[str, float]:
|
|
85
|
+
rng = np.random.default_rng(seed)
|
|
86
|
+
y_true = np.asarray(y_true).reshape(-1)
|
|
87
|
+
y_pred = np.asarray(y_pred).reshape(-1)
|
|
88
|
+
if weight is not None:
|
|
89
|
+
weight = np.asarray(weight).reshape(-1)
|
|
90
|
+
n = len(y_true)
|
|
91
|
+
stats = []
|
|
92
|
+
for _ in range(max(1, int(n_samples))):
|
|
93
|
+
idx = rng.integers(0, n, size=n)
|
|
94
|
+
y_t = y_true[idx]
|
|
95
|
+
y_p = y_pred[idx]
|
|
96
|
+
w_t = weight[idx] if weight is not None else None
|
|
97
|
+
stats.append(float(metric_fn(y_t, y_p, w_t)))
|
|
98
|
+
stats = np.asarray(stats, dtype=float)
|
|
99
|
+
alpha = (1.0 - float(ci)) / 2.0
|
|
100
|
+
low = float(np.quantile(stats, alpha))
|
|
101
|
+
high = float(np.quantile(stats, 1.0 - alpha))
|
|
102
|
+
return {"mean": float(np.mean(stats)), "low": low, "high": high}
|
|
103
|
+
|
|
104
|
+
|
|
105
105
|
def metrics_report(
|
|
106
106
|
y_true: np.ndarray,
|
|
107
107
|
y_pred: np.ndarray,
|
|
@@ -113,3 +113,12 @@ def metrics_report(
|
|
|
113
113
|
if str(task_type) == "classification":
|
|
114
114
|
return classification_metrics(y_true, y_pred, threshold=threshold)
|
|
115
115
|
return regression_metrics(y_true, y_pred, weight=weight)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
__all__ = [
|
|
119
|
+
"CalibrationResult",
|
|
120
|
+
"select_threshold",
|
|
121
|
+
"calibrate_predictions",
|
|
122
|
+
"bootstrap_ci",
|
|
123
|
+
"metrics_report",
|
|
124
|
+
]
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from .gradients import (
|
|
4
|
-
ft_integrated_gradients,
|
|
5
|
-
gradient_x_input_torch,
|
|
6
|
-
integrated_gradients_multi_input_torch,
|
|
7
|
-
integrated_gradients_torch,
|
|
8
|
-
resnet_integrated_gradients,
|
|
9
|
-
summarize_attributions,
|
|
10
|
-
)
|
|
11
|
-
from .metrics import (
|
|
12
|
-
auc_score,
|
|
13
|
-
logloss,
|
|
14
|
-
mae,
|
|
15
|
-
mape,
|
|
16
|
-
gamma_deviance,
|
|
17
|
-
poisson_deviance,
|
|
18
|
-
r2_score,
|
|
19
|
-
rmse,
|
|
20
|
-
tweedie_deviance,
|
|
21
|
-
resolve_metric,
|
|
22
|
-
)
|
|
23
|
-
from .permutation import permutation_importance
|
|
24
|
-
from .shap_utils import (
|
|
25
|
-
compute_shap_core,
|
|
26
|
-
compute_shap_ft,
|
|
27
|
-
compute_shap_glm,
|
|
28
|
-
compute_shap_resn,
|
|
29
|
-
compute_shap_xgb,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
__all__ = [
|
|
33
|
-
"auc_score",
|
|
34
|
-
"logloss",
|
|
35
|
-
"mae",
|
|
36
|
-
"mape",
|
|
37
|
-
"gamma_deviance",
|
|
38
|
-
"poisson_deviance",
|
|
39
|
-
"tweedie_deviance",
|
|
40
|
-
"r2_score",
|
|
41
|
-
"rmse",
|
|
42
|
-
"resolve_metric",
|
|
43
|
-
"permutation_importance",
|
|
44
|
-
"gradient_x_input_torch",
|
|
45
|
-
"integrated_gradients_torch",
|
|
46
|
-
"integrated_gradients_multi_input_torch",
|
|
47
|
-
"summarize_attributions",
|
|
48
|
-
"resnet_integrated_gradients",
|
|
49
|
-
"ft_integrated_gradients",
|
|
50
|
-
"compute_shap_core",
|
|
51
|
-
"compute_shap_glm",
|
|
52
|
-
"compute_shap_xgb",
|
|
53
|
-
"compute_shap_resn",
|
|
54
|
-
"compute_shap_ft",
|
|
55
|
-
]
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ins_pricing.modelling.explain.gradients import (
|
|
4
|
+
ft_integrated_gradients,
|
|
5
|
+
gradient_x_input_torch,
|
|
6
|
+
integrated_gradients_multi_input_torch,
|
|
7
|
+
integrated_gradients_torch,
|
|
8
|
+
resnet_integrated_gradients,
|
|
9
|
+
summarize_attributions,
|
|
10
|
+
)
|
|
11
|
+
from ins_pricing.modelling.explain.metrics import (
|
|
12
|
+
auc_score,
|
|
13
|
+
logloss,
|
|
14
|
+
mae,
|
|
15
|
+
mape,
|
|
16
|
+
gamma_deviance,
|
|
17
|
+
poisson_deviance,
|
|
18
|
+
r2_score,
|
|
19
|
+
rmse,
|
|
20
|
+
tweedie_deviance,
|
|
21
|
+
resolve_metric,
|
|
22
|
+
)
|
|
23
|
+
from ins_pricing.modelling.explain.permutation import permutation_importance
|
|
24
|
+
from ins_pricing.modelling.explain.shap_utils import (
|
|
25
|
+
compute_shap_core,
|
|
26
|
+
compute_shap_ft,
|
|
27
|
+
compute_shap_glm,
|
|
28
|
+
compute_shap_resn,
|
|
29
|
+
compute_shap_xgb,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"auc_score",
|
|
34
|
+
"logloss",
|
|
35
|
+
"mae",
|
|
36
|
+
"mape",
|
|
37
|
+
"gamma_deviance",
|
|
38
|
+
"poisson_deviance",
|
|
39
|
+
"tweedie_deviance",
|
|
40
|
+
"r2_score",
|
|
41
|
+
"rmse",
|
|
42
|
+
"resolve_metric",
|
|
43
|
+
"permutation_importance",
|
|
44
|
+
"gradient_x_input_torch",
|
|
45
|
+
"integrated_gradients_torch",
|
|
46
|
+
"integrated_gradients_multi_input_torch",
|
|
47
|
+
"summarize_attributions",
|
|
48
|
+
"resnet_integrated_gradients",
|
|
49
|
+
"ft_integrated_gradients",
|
|
50
|
+
"compute_shap_core",
|
|
51
|
+
"compute_shap_glm",
|
|
52
|
+
"compute_shap_xgb",
|
|
53
|
+
"compute_shap_resn",
|
|
54
|
+
"compute_shap_ft",
|
|
55
|
+
]
|
|
@@ -1,176 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from typing import Callable, Optional, Tuple
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from sklearn.metrics import roc_auc_score
|
|
9
|
-
except Exception: # pragma: no cover
|
|
10
|
-
roc_auc_score = None
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _to_numpy(arr) -> np.ndarray:
|
|
14
|
-
out = np.asarray(arr, dtype=float)
|
|
15
|
-
return out.reshape(-1)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _align(y_true, y_pred, sample_weight=None) -> Tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]:
|
|
19
|
-
y_t = _to_numpy(y_true)
|
|
20
|
-
y_p = _to_numpy(y_pred)
|
|
21
|
-
if y_t.shape[0] != y_p.shape[0]:
|
|
22
|
-
raise ValueError("y_true and y_pred must have the same length.")
|
|
23
|
-
if sample_weight is None:
|
|
24
|
-
return y_t, y_p, None
|
|
25
|
-
w = _to_numpy(sample_weight)
|
|
26
|
-
if w.shape[0] != y_t.shape[0]:
|
|
27
|
-
raise ValueError("sample_weight must have the same length as y_true.")
|
|
28
|
-
return y_t, y_p, w
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _weighted_mean(values: np.ndarray, weight: Optional[np.ndarray]) -> float:
|
|
32
|
-
if weight is None:
|
|
33
|
-
return float(np.mean(values))
|
|
34
|
-
total = float(np.sum(weight))
|
|
35
|
-
if total <= 0:
|
|
36
|
-
return float(np.mean(values))
|
|
37
|
-
return float(np.sum(values * weight) / total)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def rmse(y_true, y_pred, sample_weight=None) -> float:
|
|
41
|
-
y_t, y_p, w = _align(y_true, y_pred, sample_weight)
|
|
42
|
-
err = (y_t - y_p) ** 2
|
|
43
|
-
return float(np.sqrt(_weighted_mean(err, w)))
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def mae(y_true, y_pred, sample_weight=None) -> float:
|
|
47
|
-
y_t, y_p, w = _align(y_true, y_pred, sample_weight)
|
|
48
|
-
err = np.abs(y_t - y_p)
|
|
49
|
-
return _weighted_mean(err, w)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def mape(y_true, y_pred, sample_weight=None, eps: float = 1e-8) -> float:
|
|
53
|
-
y_t, y_p, w = _align(y_true, y_pred, sample_weight)
|
|
54
|
-
denom = np.maximum(np.abs(y_t), eps)
|
|
55
|
-
err = np.abs((y_t - y_p) / denom)
|
|
56
|
-
return _weighted_mean(err, w)
|
|
1
|
+
"""Thin wrappers for shared metric utilities."""
|
|
57
2
|
|
|
3
|
+
from __future__ import annotations
|
|
58
4
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def tweedie_deviance(
|
|
86
|
-
y_true,
|
|
87
|
-
y_pred,
|
|
88
|
-
sample_weight=None,
|
|
89
|
-
*,
|
|
90
|
-
power: float = 1.5,
|
|
91
|
-
eps: float = 1e-8,
|
|
92
|
-
) -> float:
|
|
93
|
-
"""Tweedie deviance (power=1 -> Poisson, power=2 -> Gamma, power=0 -> Normal)."""
|
|
94
|
-
if power < 0:
|
|
95
|
-
raise ValueError("power must be >= 0.")
|
|
96
|
-
y_t, y_p, w = _align(y_true, y_pred, sample_weight)
|
|
97
|
-
y_p = np.clip(y_p, eps, None)
|
|
98
|
-
y_t_safe = np.clip(y_t, eps, None)
|
|
99
|
-
|
|
100
|
-
if power == 0:
|
|
101
|
-
dev = (y_t - y_p) ** 2
|
|
102
|
-
elif power == 1:
|
|
103
|
-
dev = 2 * (y_t_safe * np.log(y_t_safe / y_p) - (y_t_safe - y_p))
|
|
104
|
-
elif power == 2:
|
|
105
|
-
ratio = y_t_safe / y_p
|
|
106
|
-
dev = 2 * ((ratio - 1) - np.log(ratio))
|
|
107
|
-
else:
|
|
108
|
-
term1 = np.power(y_t_safe, 2 - power) / ((1 - power) * (2 - power))
|
|
109
|
-
term2 = y_t_safe * np.power(y_p, 1 - power) / (1 - power)
|
|
110
|
-
term3 = np.power(y_p, 2 - power) / (2 - power)
|
|
111
|
-
dev = 2 * (term1 - term2 + term3)
|
|
112
|
-
return _weighted_mean(dev, w)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def poisson_deviance(y_true, y_pred, sample_weight=None, eps: float = 1e-8) -> float:
|
|
116
|
-
return tweedie_deviance(
|
|
117
|
-
y_true,
|
|
118
|
-
y_pred,
|
|
119
|
-
sample_weight=sample_weight,
|
|
120
|
-
power=1.0,
|
|
121
|
-
eps=eps,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def gamma_deviance(y_true, y_pred, sample_weight=None, eps: float = 1e-8) -> float:
|
|
126
|
-
return tweedie_deviance(
|
|
127
|
-
y_true,
|
|
128
|
-
y_pred,
|
|
129
|
-
sample_weight=sample_weight,
|
|
130
|
-
power=2.0,
|
|
131
|
-
eps=eps,
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def auc_score(y_true, y_pred, sample_weight=None) -> float:
|
|
136
|
-
if roc_auc_score is None:
|
|
137
|
-
raise RuntimeError("auc requires scikit-learn.")
|
|
138
|
-
y_t, y_p, w = _align(y_true, y_pred, sample_weight)
|
|
139
|
-
return float(roc_auc_score(y_t, y_p, sample_weight=w))
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def resolve_metric(
|
|
143
|
-
metric: str | Callable,
|
|
144
|
-
*,
|
|
145
|
-
task_type: Optional[str] = None,
|
|
146
|
-
higher_is_better: Optional[bool] = None,
|
|
147
|
-
) -> Tuple[Callable, bool, str]:
|
|
148
|
-
if callable(metric):
|
|
149
|
-
if higher_is_better is None:
|
|
150
|
-
raise ValueError("higher_is_better must be provided for custom metric.")
|
|
151
|
-
return metric, bool(higher_is_better), getattr(metric, "__name__", "custom")
|
|
152
|
-
|
|
153
|
-
name = str(metric or "auto").lower()
|
|
154
|
-
if name == "auto":
|
|
155
|
-
if task_type == "classification":
|
|
156
|
-
name = "logloss"
|
|
157
|
-
else:
|
|
158
|
-
name = "rmse"
|
|
159
|
-
|
|
160
|
-
mapping = {
|
|
161
|
-
"rmse": (rmse, False),
|
|
162
|
-
"mae": (mae, False),
|
|
163
|
-
"mape": (mape, False),
|
|
164
|
-
"r2": (r2_score, True),
|
|
165
|
-
"logloss": (logloss, False),
|
|
166
|
-
"poisson": (poisson_deviance, False),
|
|
167
|
-
"gamma": (gamma_deviance, False),
|
|
168
|
-
"tweedie": (tweedie_deviance, False),
|
|
169
|
-
"auc": (auc_score, True),
|
|
170
|
-
}
|
|
171
|
-
if name not in mapping:
|
|
172
|
-
raise ValueError(f"Unsupported metric: {metric}")
|
|
173
|
-
fn, hib = mapping[name]
|
|
174
|
-
if higher_is_better is not None:
|
|
175
|
-
hib = bool(higher_is_better)
|
|
176
|
-
return fn, hib, name
|
|
5
|
+
from ins_pricing.utils.metrics import (
|
|
6
|
+
auc_score,
|
|
7
|
+
gamma_deviance,
|
|
8
|
+
logloss,
|
|
9
|
+
mae,
|
|
10
|
+
mape,
|
|
11
|
+
poisson_deviance,
|
|
12
|
+
r2_score,
|
|
13
|
+
resolve_metric,
|
|
14
|
+
rmse,
|
|
15
|
+
tweedie_deviance,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"rmse",
|
|
20
|
+
"mae",
|
|
21
|
+
"mape",
|
|
22
|
+
"r2_score",
|
|
23
|
+
"logloss",
|
|
24
|
+
"tweedie_deviance",
|
|
25
|
+
"poisson_deviance",
|
|
26
|
+
"gamma_deviance",
|
|
27
|
+
"auc_score",
|
|
28
|
+
"resolve_metric",
|
|
29
|
+
]
|