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

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
2 changes: 1 addition & 1 deletion doc/metadata_routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ Meta-estimators and functions supporting metadata routing:

- :class:`sklearn.calibration.CalibratedClassifierCV`
- :class:`sklearn.compose.ColumnTransformer`
- :class:`sklearn.covariance.GraphicalLassoCV`
- :class:`sklearn.ensemble.VotingClassifier`
- :class:`sklearn.ensemble.VotingRegressor`
- :class:`sklearn.ensemble.BaggingClassifier`
Expand Down Expand Up @@ -313,7 +314,6 @@ Meta-estimators and functions supporting metadata routing:
Meta-estimators and tools not supporting metadata routing yet:

- :class:`sklearn.compose.TransformedTargetRegressor`
- :class:`sklearn.covariance.GraphicalLassoCV`
- :class:`sklearn.ensemble.AdaBoostClassifier`
- :class:`sklearn.ensemble.AdaBoostRegressor`
- :class:`sklearn.ensemble.StackingClassifier`
Expand Down
4 changes: 4 additions & 0 deletions doc/whats_new/v1.5.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ more details.
:class:`model_selection.GridSearchCV` object or the underlying scorer.
:pr:`27560` by :user:`Omar Salman <OmarManzoor>`.

- |Feature| :class:`GraphicalLassoCV` now supports metadata routing in it's
`fit` method and routes metadata to the CV splitter.
:pr:`27566` by :user:`Omar Salman <OmarManzoor>`.

- |Feature| :class:`linear_model.RANSACRegressor` now supports metadata routing
in its ``fit``, ``score`` and ``predict`` methods and route metadata to its
underlying estimator's' ``fit``, ``score`` and ``predict`` methods.
Expand Down
54 changes: 50 additions & 4 deletions sklearn/covariance/_graph_lasso.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,15 @@
from ..linear_model import _cd_fast as cd_fast # type: ignore
from ..linear_model import lars_path_gram
from ..model_selection import check_cv, cross_val_score
from ..utils import Bunch
from ..utils._param_validation import Interval, StrOptions, validate_params
from ..utils.metadata_routing import _RoutingNotSupportedMixin
from ..utils.metadata_routing import (
MetadataRouter,
MethodMapping,
_raise_for_params,
_routing_enabled,
process_routing,
)
from ..utils.parallel import Parallel, delayed
from ..utils.validation import (
_is_arraylike_not_scalar,
Expand Down Expand Up @@ -721,7 +728,7 @@ def graphical_lasso_path(
return covariances_, precisions_


class GraphicalLassoCV(_RoutingNotSupportedMixin, BaseGraphicalLasso):
class GraphicalLassoCV(BaseGraphicalLasso):
"""Sparse inverse covariance w/ cross-validated choice of the l1 penalty.

See glossary entry for :term:`cross-validation estimator`.
Expand Down Expand Up @@ -942,7 +949,7 @@ def __init__(
self.n_jobs = n_jobs

@_fit_context(prefer_skip_nested_validation=True)
def fit(self, X, y=None):
def fit(self, X, y=None, **params):
"""Fit the GraphicalLasso covariance model to X.

Parameters
Expand All @@ -953,12 +960,25 @@ def fit(self, X, y=None):
y : Ignored
Not used, present for API consistency by convention.

**params : dict, default=None
Parameters to be passed to the CV splitter and the
cross_val_score function.

.. versionadded:: 1.5
Only available if `enable_metadata_routing=True`,
which can be set by using
``sklearn.set_config(enable_metadata_routing=True)``.
See :ref:`Metadata Routing User Guide <metadata_routing>` for
more details.

Returns
-------
self : object
Returns the instance itself.
"""
# Covariance does not make sense for a single feature
_raise_for_params(params, self, "fit")

X = self._validate_data(X, ensure_min_features=2)
if self.assume_centered:
self.location_ = np.zeros(X.shape[1])
Expand Down Expand Up @@ -991,6 +1011,11 @@ def fit(self, X, y=None):
alpha_0 = 1e-2 * alpha_1
alphas = np.logspace(np.log10(alpha_0), np.log10(alpha_1), n_alphas)[::-1]

if _routing_enabled():
routed_params = process_routing(self, "fit", **params)
else:
routed_params = Bunch(splitter=Bunch(split={}))

t0 = time.time()
for i in range(n_refinements):
with warnings.catch_warnings():
Expand All @@ -1015,7 +1040,7 @@ def fit(self, X, y=None):
verbose=inner_verbose,
eps=self.eps,
)
for train, test in cv.split(X, y)
for train, test in cv.split(X, y, **routed_params.splitter.split)
)

# Little danse to transform the list in what we need
Expand Down Expand Up @@ -1081,6 +1106,7 @@ def fit(self, X, y=None):
cv=cv,
n_jobs=self.n_jobs,
verbose=inner_verbose,
params=params,
)
)
grid_scores = np.array(grid_scores)
Expand Down Expand Up @@ -1108,3 +1134,23 @@ def fit(self, X, y=None):
eps=self.eps,
)
return self

def get_metadata_routing(self):
"""Get metadata routing of this object.

Please check :ref:`User Guide <metadata_routing>` on how the routing
mechanism works.

.. versionadded:: 1.5

Returns
-------
routing : MetadataRouter
A :class:`~sklearn.utils.metadata_routing.MetadataRouter` encapsulating
routing information.
"""
router = MetadataRouter(owner=self.__class__.__name__).add(
splitter=check_cv(self.cv),
method_mapping=MethodMapping().add(callee="split", caller="fit"),
)
return router
76 changes: 61 additions & 15 deletions sklearn/covariance/tests/test_graphical_lasso.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
graphical_lasso,
)
from sklearn.datasets import make_sparse_spd_matrix
from sklearn.model_selection import GroupKFold
from sklearn.utils import check_random_state
from sklearn.utils._testing import (
_convert_container,
Expand Down Expand Up @@ -254,12 +255,71 @@ def test_graphical_lasso_cv_scores():
X
)

_assert_graphical_lasso_cv_scores(
cov=cov,
n_splits=splits,
n_refinements=n_refinements,
n_alphas=n_alphas,
)


# TODO(1.5): remove in 1.5
Copy link
Member

Choose a reason for hiding this comment

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

this is already removed?

Copy link
Contributor Author

@OmarManzoor OmarManzoor Mar 6, 2024

Choose a reason for hiding this comment

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

I don't think this is removed in main yet. I think you see this change only because of the rearrangement otherwise this is not part of this PR.

def test_graphical_lasso_cov_init_deprecation():
"""Check that we raise a deprecation warning if providing `cov_init` in
`graphical_lasso`."""
rng, dim, n_samples = np.random.RandomState(0), 20, 100
prec = make_sparse_spd_matrix(dim, alpha=0.95, random_state=0)
cov = linalg.inv(prec)
X = rng.multivariate_normal(np.zeros(dim), cov, size=n_samples)

emp_cov = empirical_covariance(X)
with pytest.warns(FutureWarning, match="cov_init parameter is deprecated"):
graphical_lasso(emp_cov, alpha=0.1, cov_init=emp_cov)


@pytest.mark.usefixtures("enable_slep006")
def test_graphical_lasso_cv_scores_with_routing(global_random_seed):
Copy link
Member

Choose a reason for hiding this comment

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

why do we need this on top of test_metadata_is_routed_correctly_to_splitter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a test which checks when we route the groups parameter the graphical lasso cv still operates correctly and gives the desired results.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but we don't have the equivalent test for all the other *CV classes. I'm thinking what could go wrong if we don't have this test.

Copy link
Member

Choose a reason for hiding this comment

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

I still don't see what this test brings on top of the common test we have

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This checks for the actual results and values to ensure the output is as desired after routing parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think even if this test might be additional or extra maybe it would still be nice to have it since it compares the values? Let me know what you think @adrinjalali @glemaitre . Otherwise we can remove it to finalize this PR.

"""Check that `GraphicalLassoCV` internally dispatches metadata to
the splitter.
"""
splits = 5
n_alphas = 5
n_refinements = 3
true_cov = np.array(
[
[0.8, 0.0, 0.2, 0.0],
[0.0, 0.4, 0.0, 0.0],
[0.2, 0.0, 0.3, 0.1],
[0.0, 0.0, 0.1, 0.7],
]
)
rng = np.random.RandomState(global_random_seed)
X = rng.multivariate_normal(mean=[0, 0, 0, 0], cov=true_cov, size=300)
n_samples = X.shape[0]
groups = rng.randint(0, 5, n_samples)
params = {"groups": groups}
cv = GroupKFold(n_splits=splits)
cv.set_split_request(groups=True)

cov = GraphicalLassoCV(cv=cv, alphas=n_alphas, n_refinements=n_refinements).fit(
X, **params
)

_assert_graphical_lasso_cv_scores(
cov=cov,
n_splits=splits,
n_refinements=n_refinements,
n_alphas=n_alphas,
)


def _assert_graphical_lasso_cv_scores(cov, n_splits, n_refinements, n_alphas):
cv_results = cov.cv_results_
# alpha and one for each split

total_alphas = n_refinements * n_alphas + 1
keys = ["alphas"]
split_keys = [f"split{i}_test_score" for i in range(splits)]
split_keys = [f"split{i}_test_score" for i in range(n_splits)]
for key in keys + split_keys:
assert key in cv_results
assert len(cv_results[key]) == total_alphas
Expand All @@ -270,17 +330,3 @@ def test_graphical_lasso_cv_scores():

assert_allclose(cov.cv_results_["mean_test_score"], expected_mean)
assert_allclose(cov.cv_results_["std_test_score"], expected_std)


# TODO(1.5): remove in 1.5
def test_graphical_lasso_cov_init_deprecation():
"""Check that we raise a deprecation warning if providing `cov_init` in
`graphical_lasso`."""
rng, dim, n_samples = np.random.RandomState(0), 20, 100
prec = make_sparse_spd_matrix(dim, alpha=0.95, random_state=0)
cov = linalg.inv(prec)
X = rng.multivariate_normal(np.zeros(dim), cov, size=n_samples)

emp_cov = empirical_covariance(X)
with pytest.warns(FutureWarning, match="cov_init parameter is deprecated"):
graphical_lasso(emp_cov, alpha=0.1, cov_init=emp_cov)
8 changes: 7 additions & 1 deletion sklearn/tests/test_metaestimators_metadata_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,13 @@ def enable_slep006():
"cv_name": "cv",
"cv_routing_methods": ["fit"],
},
{
"metaestimator": GraphicalLassoCV,
"X": X,
"y": y,
"cv_name": "cv",
"cv_routing_methods": ["fit"],
},
]
"""List containing all metaestimators to be tested and their settings

Expand Down Expand Up @@ -397,7 +404,6 @@ def enable_slep006():
UNSUPPORTED_ESTIMATORS = [
AdaBoostClassifier(),
AdaBoostRegressor(),
GraphicalLassoCV(),
RFE(ConsumingClassifier()),
RFECV(ConsumingClassifier()),
SelfTrainingClassifier(ConsumingClassifier()),
Expand Down