Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/debias wrapper #152

Merged
merged 90 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 83 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
cff922f
Added v1 for DebiasWrapper
Mar 29, 2024
d068249
Fixed copyright year
Mar 29, 2024
aef087e
Added wrapper instanse for every ranking and cls metrics
Apr 3, 2024
b63d4b7
Renamed module for debias metrics
Apr 3, 2024
f45f593
Merge branch 'MobileTeleSystems:main' into feature/debias_wrapper
In48semenov Apr 3, 2024
ac28756
Added __new__ to DebiasWrapper and cls method
Apr 5, 2024
d7a8056
Merge branch 'feature/debias_wrapper' of https://github.com/In48semen…
Apr 5, 2024
12d7a65
Changed `DebiasWrapper` from class to function `debias_wrapper`, adde…
Apr 16, 2024
d11f002
Fixed bugs in make_downsampling and added test for this method.
Apr 19, 2024
37372f3
Added test for calc debias metrics and fixed bugs.
Apr 23, 2024
a8ff90f
Added test for debias metric and fixed bugs
Apr 24, 2024
b72a086
Merge branch 'feature/debias_wrapper' of https://github.com/In48semen…
Apr 24, 2024
cc04f96
Fixed code style
Apr 24, 2024
c35b8f1
Fixed code style.
Apr 25, 2024
faef485
Fixed docs
Apr 25, 2024
9fb5708
Fixed
Apr 25, 2024
d28a16e
Fixed
Apr 25, 2024
a6cb3ed
Merge branch 'd28a16e' into feature/debias_wrapper
Jun 4, 2024
92dc508
Changed `DebiasWrapper` from class to function `debias_wrapper`, adde…
Apr 16, 2024
4590ccd
Fixed code style.
Apr 25, 2024
27706a4
Fixed
Apr 25, 2024
a05d95f
Fixed
Apr 25, 2024
cc4baf5
Added `DebiasConfig`
Jun 4, 2024
aa80955
Removed unnecessary code, fixed `fit` method for MAP metric
Jun 5, 2024
7f05256
Fixed code style.
Jun 5, 2024
72e538b
Fixed bugs
Jun 5, 2024
8ee3d3a
Fixed bugs.
Jun 5, 2024
4d168c0
Removed `print`
Jun 6, 2024
f703045
Added propery method to `DebiasConfig`.
Jun 6, 2024
20b5b81
Fixed bugs and comment for pr.
Jun 7, 2024
3513c58
Made the DebiasConfig frozen.
Jun 7, 2024
41ffdae
Fixed random_state for DebiasConfig in tests
Jun 7, 2024
e0f90eb
Added boolean arg to cls metrics `is_confusion_df_debiased`, added De…
Jun 17, 2024
f86afb2
rewritten tests.
Jun 17, 2024
d579faa
Fixed classification metrics with debias.
Jun 18, 2024
17d7114
Update rectools/metrics/classification.py
In48semenov Jun 18, 2024
c8c8be9
Update rectools/metrics/classification.py
In48semenov Jun 18, 2024
20f8164
Removed unnecessary code.
Jun 18, 2024
dafce60
Fixed code style.
Jun 18, 2024
b1d26c6
Update rectools/metrics/debias.py
In48semenov Jun 18, 2024
e62bf15
Update rectools/metrics/debias.py
In48semenov Jun 18, 2024
0ebb65f
Fixed code style
Jun 18, 2024
7f6d0f0
Removed condition for column `rank`
Jun 18, 2024
5b62ec1
Fixed tests.
Jun 19, 2024
0e6dedd
Merge branch 'main' into feature/debias_wrapper
Jun 19, 2024
86e85fa
Updated the changelog file.
Jun 20, 2024
1170b9a
Removed unnecessary code.
Jun 22, 2024
cbd0999
Added `DebiasableMetricAtK` to `PartialAUC` and `PAP`
Jun 28, 2024
6c4e236
Merge branch 'main' into feature/debias_wrapper
Jul 1, 2024
596f915
Added new exception for auc and classification metircs with de-bias
Jul 1, 2024
102c8dc
Update rectools/metrics/debias.py
In48semenov Jul 1, 2024
fb13424
Fixed bug in name of the base class for de-bias `DebiasableMetrikAtK`
Jul 1, 2024
ed8c050
Fixed bugs, added tests to `auc` metrics, changed CHANGELOG.md
Jul 1, 2024
a700e57
Added tests and fixed code style.
Jul 2, 2024
6e54b15
Fixed code style.
Jul 2, 2024
23c171e
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
9927585
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
25f55f5
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
960c623
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
24a7038
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
75654b5
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
fd0882e
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
bb6d3a6
Update rectools/metrics/classification.py
In48semenov Jul 2, 2024
ced3bf7
Update rectools/metrics/auc.py
In48semenov Jul 2, 2024
9ac60ca
Update rectools/metrics/classification.py
In48semenov Jul 2, 2024
3bb01c6
Update rectools/metrics/classification.py
In48semenov Jul 2, 2024
af1b2b5
Fixed docs, add commom method `_check_debias` to class for de-biasing
Jul 2, 2024
49b8d8a
Apply suggestions from code review
In48semenov Jul 3, 2024
073cc96
Fixed code in accordance with the comments received
Jul 4, 2024
ab833c7
Changed name function to `calc_debiased_fit_task`, code for this func…
Jul 5, 2024
bb70c6d
Rewrote the tests and changed `debias_interactions` method to classme…
Jul 30, 2024
afebed8
Merge remote-tracking branch 'origin/main' into feature/debias_wrapper
Jul 30, 2024
995b583
Fixed code style.
Jul 30, 2024
fca0e12
Added new funciton for calc debiased different configs `calc_debiased…
Jul 30, 2024
83757a6
Made `debias_interactions` as functions, added `calc_debiased_differe…
Jul 31, 2024
6067a45
Droped condition for checking classificaiton metrics and test for it.
Jul 31, 2024
be073ad
Added test for `calc_debiased_different_configs`
Jul 31, 2024
9f8802a
Added new parameters `debiasing_interactions_prev` for calc_debiased …
Jul 31, 2024
ff6e221
Fixed docs.
Jul 31, 2024
11f1ba4
Updated CHANGELOG.md
Jul 31, 2024
f2cecd9
Removed unnecerassary code.
Aug 1, 2024
710e721
Renamed variables and functions.
Aug 1, 2024
4c4af8d
Fixed docs and renamed variables.
Aug 2, 2024
c1478fc
Fixed docs, updated CHANGELOG.md
Aug 4, 2024
7025cd8
Fixed variables
Aug 4, 2024
7d487cd
Removed duplicate code.
Aug 4, 2024
6130ab6
Fixed docs `DebiasConfig` and code in tests.
Aug 4, 2024
be6bcca
Fixed docs for debias and renamed variables in tests.
Aug 5, 2024
8e0783f
Fornatted and fixed tests.
Aug 5, 2024
db3cdff
Fixed mypy error: changed Dict to Mapping in `calc_metrics` and `sele…
Aug 5, 2024
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## [0.7.1] - x.x.2024

feldlime marked this conversation as resolved.
Show resolved Hide resolved
### Added
- `Debias` mechanism for classification, ranking and auc metrics. New parameter `is_debiased` to `calc_from_confusion_df`, `calc_per_user_from_confusion_df` methods of classification metrics, `calc_from_fitted`, `calc_per_user_from_fitted` methods of auc and rankning (`MAP`) metrics, `calc_from_merged`, `calc_per_user_from_merged` methods of ranking (`NDCG`, `MRR`) metrics. ([#152](https://github.com/MobileTeleSystems/RecTools/pull/152))

## [0.7.0] - 29.07.2024

Expand Down
5 changes: 5 additions & 0 deletions rectools/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@
`metrics.PairwiseDistanceCalculator`
`metrics.PairwiseHammingDistanceCalculator`
`metrics.SparsePairwiseHammingDistanceCalculator`
`metrics.DebiasConfig`
`metrics.debias_interactions`
"""

from .auc import PAP, PartialAUC
from .classification import MCC, Accuracy, F1Beta, HitRate, Precision, Recall
from .debias import DebiasConfig, debias_interactions
from .distances import (
PairwiseDistanceCalculator,
PairwiseHammingDistanceCalculator,
Expand Down Expand Up @@ -89,4 +92,6 @@
"SufficientReco",
"UnrepeatedReco",
"CoveredUsers",
"DebiasConfig",
"debias_interactions",
)
60 changes: 46 additions & 14 deletions rectools/metrics/auc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from attrs import define, field

from rectools import Columns
from rectools.metrics.base import MetricAtK, outer_merge_reco
from rectools.metrics.base import outer_merge_reco
from rectools.metrics.debias import DebiasableMetrikAtK, calc_debiased_fit_task, debias_interactions


class InsufficientHandling(str, Enum):
Expand Down Expand Up @@ -58,7 +59,7 @@ class AUCFitted:


@define
class _AUCMetric(MetricAtK):
class _AUCMetric(DebiasableMetrikAtK):
"""
ROC AUC based metric base class.

Expand Down Expand Up @@ -88,6 +89,8 @@ class _AUCMetric(MetricAtK):
until the model has non-zero scores for the item in item-item similarity matrix. So with
small `K` for neighbours in ItemKNN and big `K` for `recommend` and AUC based metric you
will still get an error when `insufficient_handling` is set to `raise`.
debias_config : DebiasConfig, default None
feldlime marked this conversation as resolved.
Show resolved Hide resolved
Config with debias method parameters (iqr_coef, random_state).
"""

insufficient_handling: str = field(default="ignore")
Expand Down Expand Up @@ -217,36 +220,46 @@ def calc_per_user(self, reco: pd.DataFrame, interactions: pd.DataFrame) -> pd.Se
pd.Series
Values of metric (index - user id, values - metric value for every user).
"""
is_debiased = False
if self.debias_config is not None:
interactions = debias_interactions(interactions, self.debias_config)
is_debiased = True

self._check(reco, interactions=interactions)
insufficient_handling_needed = self.insufficient_handling != InsufficientHandling.IGNORE
fitted = self.fit(reco, interactions, self.k, insufficient_handling_needed)
return self.calc_per_user_from_fitted(fitted)
return self.calc_per_user_from_fitted(fitted, is_debiased)

def calc_from_fitted(self, fitted: AUCFitted) -> float:
def calc_from_fitted(self, fitted: AUCFitted, is_debiased: bool = False) -> float:
"""
Calculate metric value from fitted data.

Parameters
----------
fitted : AUCFitted
Meta data that got from `.fit` method.
is_debiased : bool, default False
An indicator of whether the debias transformation has been applied before or not.

Returns
-------
float
Value of metric (average between users).
"""
per_user = self.calc_per_user_from_fitted(fitted)
per_user = self.calc_per_user_from_fitted(fitted, is_debiased)
return per_user.mean()

def calc_per_user_from_fitted(self, fitted: AUCFitted) -> pd.Series:
def calc_per_user_from_fitted(self, fitted: AUCFitted, is_debiased: bool = False) -> pd.Series:
"""
Calculate metric values for all users from from fitted data.

Parameters
----------
fitted : AUCFitted
Meta data that got from `.fit` method.
is_debiased : bool, default False
If ``True``, indicator that a debias mechanism has been applied before.
If ``False``, indicator that the debias mechanism has not been applied before.
feldlime marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
Expand Down Expand Up @@ -307,6 +320,8 @@ class PartialAUC(_AUCMetric):
until the model has non-zero scores for the item in item-item similarity matrix. So with
small `K` for neighbours in ItemKNN and big `K` for `recommend` and AUC based metric you
will still get an error when `insufficient_handling` is set to `raise`.
debias_config : DebiasConfig, default None
feldlime marked this conversation as resolved.
Show resolved Hide resolved
Config with debias method parameters (iqr_coef, random_state).

Examples
--------
Expand Down Expand Up @@ -339,25 +354,27 @@ def _get_sufficient_reco_explanation(self) -> str:
not too high.
"""

def calc_per_user_from_fitted(self, fitted: AUCFitted) -> pd.Series:
def calc_per_user_from_fitted(self, fitted: AUCFitted, is_debiased: bool = False) -> pd.Series:
"""
Calculate metric values for all users from from fitted data.

Parameters
----------
fitted : AUCFitted
Meta data that got from `.fit` method.
is_debiased : bool, default False
If ``True``, indicator that a debias mechanism has been applied before.
If ``False``, indicator that the debias mechanism has not been applied before.
feldlime marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
pd.Series
Values of metric (index - user id, values - metric value for every user).
"""
self._check_debias(is_debiased, obj_name="AUCFitted")
outer_merged = fitted.outer_merged_enriched

# Keep k first false positives for roc auc computation, keep all predicted test positives
cropped = outer_merged[(outer_merged["__fp_cumsum"] < self.k) & (~outer_merged[Columns.Rank].isna())]

cropped_suf, n_pos_suf = self._handle_insufficient_cases(
outer_merged=cropped, n_pos=fitted.n_pos, n_fp_insufficient=fitted.n_fp_insufficient
)
Expand Down Expand Up @@ -415,6 +432,8 @@ class PAP(_AUCMetric):
until the model has non-zero scores for the item in item-item similarity matrix. So with
small `K` for neighbours in ItemKNN and big `K` for `recommend` and AUC based metric you
will still get an error when `insufficient_handling` is set to `raise`.
debias_config : DebiasConfig, default None
Config with debias method parameters (iqr_coef, random_state).

Examples
--------
Expand Down Expand Up @@ -447,22 +466,25 @@ def _get_sufficient_reco_explanation(self) -> str:
for all users.
"""

def calc_per_user_from_fitted(self, fitted: AUCFitted) -> pd.Series:
def calc_per_user_from_fitted(self, fitted: AUCFitted, is_debiased: bool = False) -> pd.Series:
"""
Calculate metric values for all users from outer merged recommendations.

Parameters
----------
fitted : AUCFitted
Meta data that got from `.fit` method.
is_debiased : bool, default False
If ``True``, indicator that a debias mechanism has been applied before.
If ``False``, indicator that the debias mechanism has not been applied before

Returns
-------
pd.Series
Values of metric (index - user id, values - metric value for every user).
"""
self._check_debias(is_debiased, obj_name="AUCFitted")
outer_merged = fitted.outer_merged_enriched

# Keep k first false positives and k first predicted test positives for roc auc computation
cropped = outer_merged[
(outer_merged["__test_pos_cumsum"] <= self.k)
Expand Down Expand Up @@ -513,12 +535,22 @@ def calc_auc_metrics(
"""
results = {}

k_max = max(metric.k for metric in metrics.values())
insufficient_handling_needed = any(
metric.insufficient_handling != InsufficientHandling.IGNORE for metric in metrics.values()
)
fitted = _AUCMetric.fit(reco, interactions, k_max, insufficient_handling_needed)

debiased_fit_task = calc_debiased_fit_task(metrics.values(), interactions)
fitted_debiased = {}
for debias_config_name, (k_max_d, interactions_d) in debiased_fit_task.items():
fitted_debiased[debias_config_name] = _AUCMetric.fit(
reco, interactions_d, k_max_d, insufficient_handling_needed
)

for name, metric in metrics.items():
results[name] = metric.calc_from_fitted(fitted)
is_debiased = metric.debias_config is not None
results[name] = metric.calc_from_fitted(
fitted=fitted_debiased[metric.debias_config],
is_debiased=is_debiased,
)

return results
Loading
Loading