Skip to content

Commit

Permalink
Copy attributes from the original class to the proxy (#6306)
Browse files Browse the repository at this point in the history
This makes the proxy look and feel more like the original class.

This copies several attributes, not just `__name__` and makes sure the signature of the constructor looks like that of the scikit-learn class. This is useful for people who inspect it during coding (like some editors) or `ipython`'s `LogisticRegression?`.

~~For some reason the test in `test_basic_estimator.py` fails because the proxy doesn't have a `__name__` attribute. Trying to work out why, if someone can spot the mistake let me know~~

This is an alternative/extended version of #6305

Authors:
  - Tim Head (https://github.com/betatim)

Approvers:
  - Simon Adorf (https://github.com/csadorf)

URL: #6306
  • Loading branch information
betatim authored Feb 10, 2025
1 parent 2c47a01 commit aa8da6b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 4 deletions.
22 changes: 21 additions & 1 deletion python/cuml/cuml/experimental/accel/estimator_proxy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2024, NVIDIA CORPORATION.
# Copyright (c) 2024-2025, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -277,6 +277,26 @@ def __reduce__(self):
),
)

# Help make the proxy class look more like the original class
for attr in (
"__module__",
"__name__",
"__qualname__",
"__doc__",
"__annotate__",
"__type_params__",
):
try:
value = getattr(original_class_a, attr)
except AttributeError:
pass
else:
setattr(ProxyEstimator, attr, value)

ProxyEstimator.__init__.__signature__ = inspect.signature(
original_class_a.__init__
)

logger.debug(
f"Created proxy estimator: ({module_b}, {original_class_name}, {ProxyEstimator})"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2024, NVIDIA CORPORATION.
# Copyright (c) 2024-2025, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -140,3 +140,32 @@ def test_k_neighbors_regressor():
for metric in ["euclidean", "manhattan"]:
knr = KNeighborsRegressor().fit(X, y)
knr.predict(X)


def test_proxy_facade():
# Check that the proxy estimator pretends to look like the
# class it is proxying

# A random estimator, shouldn't matter which one as all are proxied
# the same way.
# We need an instance to get access to the `_cpu_model_class`
# but we want to compare to the PCA class
pca = PCA()
for attr in (
"__module__",
"__name__",
"__qualname__",
"__doc__",
"__annotate__",
"__type_params__",
):
# if the original class has this attribute, the proxy should
# have it as well and the values should match
try:
original_value = getattr(pca._cpu_model_class, attr)
except AttributeError:
pass
else:
proxy_value = getattr(PCA, attr)

assert original_value == proxy_value
13 changes: 11 additions & 2 deletions python/cuml/cuml/tests/experimental/accel/test_pipeline.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2024, NVIDIA CORPORATION.
# Copyright (c) 2024-2025, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,7 +31,7 @@
KNeighborsClassifier,
KNeighborsRegressor,
)
from sklearn.pipeline import Pipeline
from sklearn.pipeline import Pipeline, make_pipeline
from sklearn.datasets import make_classification, make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, mean_squared_error
Expand Down Expand Up @@ -145,3 +145,12 @@ def test_umap_with_logistic_regression(classification_data):
# Fit and predict
pipeline.fit(X_train, y_train)
pipeline.predict(X_test)


def test_automatic_step_naming():
# The automatically generated names of estimators should be the
# same with and without accelerator.
pipeline = make_pipeline(PCA(), LogisticRegression())

assert "pca" in pipeline.named_steps
assert "logisticregression" in pipeline.named_steps

0 comments on commit aa8da6b

Please sign in to comment.