Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/aix_page/calibration/calibrators.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@ def pseudo_rows(
return x[keep], y[keep], w[keep]


def _logit(probabilities: np.ndarray) -> np.ndarray:
"""Apply the historical probability clipping contract before taking log odds."""
return np.asarray(
np.log(np.clip(probabilities, 1e-8, 1 - 1e-8) / np.clip(1 - probabilities, 1e-8, 1))
)


class Platt:
def __init__(self) -> None:
self.model = LogisticRegression(C=1e6)

def fit(self, click: np.ndarray, impression: np.ndarray, p: np.ndarray) -> Platt:
x, y, w = pseudo_rows(
click, impression, np.log(np.clip(p, 1e-8, 1 - 1e-8) / np.clip(1 - p, 1e-8, 1))
)
x, y, w = pseudo_rows(click, impression, _logit(p))
self.model.fit(x, y, sample_weight=w)
return self

def predict(self, p: np.ndarray) -> np.ndarray:
logit = np.log(np.clip(p, 1e-8, 1 - 1e-8) / np.clip(1 - p, 1e-8, 1))
logit = _logit(p)
return np.asarray(self.model.predict_proba(logit[:, None])[:, 1], dtype=np.float64)


Expand Down
22 changes: 22 additions & 0 deletions tests/unit/test_calibration_polish.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Confirm Platt log-odds clipping remains numerically identical."""

import numpy as np

from aix_page.calibration.calibrators import _logit, pseudo_rows


def test_logit_reuses_exact_historical_clipping_formula():
probabilities = np.array([0.0, 1e-9, 0.1, 0.5, 0.9, 1.0])
expected = np.log(np.clip(probabilities, 1e-8, 1 - 1e-8) / np.clip(1 - probabilities, 1e-8, 1))
np.testing.assert_array_equal(_logit(probabilities), expected)


def test_pseudo_rows_keep_positive_and_negative_weights():
x, labels, weights = pseudo_rows(
np.array([2.0, 0.0]),
np.array([10.0, 5.0]),
np.array([0.2, 0.8]),
)
assert x.shape == (3, 1)
assert labels.tolist() == [1.0, 0.0, 0.0]
assert weights.tolist() == [2.0, 8.0, 5.0]
Loading