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

Skip to content

Fix: Feature Union: Checking if feautre union is fitted fails - #22953

Merged
adrinjalali merged 9 commits into
scikit-learn:mainfrom
randomgeek78:patch-2
Apr 5, 2022
Merged

Fix: Feature Union: Checking if feautre union is fitted fails#22953
adrinjalali merged 9 commits into
scikit-learn:mainfrom
randomgeek78:patch-2

Conversation

@randomgeek78

Copy link
Copy Markdown
Contributor

Currently, checking if the feature union is fitted fails. This is because it does not have any attributes of its own that end with '_' and it does not have sklearn_is_fitted implemented.

This PR delegates checking if a feature union was fitted to one of its sub-transformers.

Reference Issues/PRs

What does this implement/fix? Explain your changes.

Any other comments?

@thomasjpfan thomasjpfan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for the PR!

Comment thread sklearn/pipeline.py Outdated

def __sklearn_is_fitted__(self):
# Delegate whether feature union was fitted
check_is_fitted(self.transformer_list[0][1])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we need to check all the transformers to make sure the whole FeatureUnion is fitted.

Also transformer_list can be configured with "passthrough" and "drop" as the estimator, so those needs to be avoided in this check. (In other words, transformer_list[0][1] can be string.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @thomasjpfan. Let me know if my fix works. Tx

@thomasjpfan thomasjpfan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the update!

Comment thread sklearn/tests/test_pipeline.py Outdated


def test_feature_union_check_if_fitted():
class Estimator(BaseEstimator):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can use from sklearn.utils._testing import MinimalTransformer for a minimal transformer.

Comment thread sklearn/tests/test_pipeline.py Outdated
Comment on lines +1556 to +1568
union = clone(union)
with pytest.raises(NotFittedError):
check_is_fitted(union)

union.set_params(clf="drop")
check_is_fitted(union)

union.set_params(clf=Estimator())
with pytest.raises(NotFittedError):
check_is_fitted(union)

union.fit(iris.data, iris.target)
check_is_fitted(union)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we can remove these tests. It's fundamentally testing that clone works. In other words, if clone works and the tests above pass, then these should pass as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@thomasjpfan I just pushed out an update. Tx

@thomasjpfan thomasjpfan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor nits, otherwise LGTM

Comment thread doc/whats_new/v1.1.rst Outdated
Comment on lines +805 to +806
- |Fix| Checking if :class:`pipeline.FeatureUnion` was fitted was broken.
:pr:`22953` by :user:`randomgeek78 <randomgeek78>` provides a fix.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can be more explicit about the fix:

Suggested change
- |Fix| Checking if :class:`pipeline.FeatureUnion` was fitted was broken.
:pr:`22953` by :user:`randomgeek78 <randomgeek78>` provides a fix.
- |Fix| Defines `__sklearn_is_fitted__` in
:class:`pipeline.FeatureUnion` to return correct result
with :func:`utils.validation.check_is_fitted`.
:pr:`22953` by :user:`randomgeek78 <randomgeek78>`

check_is_fitted(pipeline)


def test_feature_union_check_if_fitted():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def test_feature_union_check_if_fitted():
def test_feature_union_check_if_fitted():
"""Check __sklearn_is_fitted__ is defined correctly."""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great feedback. Just pushed an update. Tx

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@thomasjpfan I think linting is currently broken due to a black dependency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@thomasjpfan This PR does not build because of this message: Could not find a version that satisfies the requirement black==22.1.0. Do you know anything about this? Thanks.

@randomgeek78

Copy link
Copy Markdown
Contributor Author

@adrinjalali @ogrisel Could you help review this PR? It requires a second approval. Thanks!

@adrinjalali adrinjalali left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we should also test how drop works here.

Comment thread sklearn/pipeline.py
Comment on lines +1237 to +1239
for _, transformer, _ in self._iter():
check_is_fitted(transformer)
return True

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the issue with this implementation is that we allow all transformers to be None in FeatureUnion. fit's code is:

        transformers = self._parallel_func(X, y, fit_params, _fit_one)
        if not transformers:
            # All transformers are None
            return self
        self._update_transformer_list(transformers)
        return self

wouldn't it be better here to add a private _fitted attribute in fit, and in this method to check for that?

If we want to also support pre-fitting transformers and then putting them in a FeatureUnion, we could do something like:

if getattr(self, '_fitted', None) or all_transformers_fitted:
    return True

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the current solution works because _iter does not yield dropped estimators:

for name, trans in self.transformer_list:
if trans == "drop":
continue
if trans == "passthrough":
trans = FunctionTransformer()
yield (name, trans, get_weight(name))

(Also FeatureUnion no longer supports None and uses `"drop" instead. )

@randomgeek78 randomgeek78 Apr 4, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@adrinjalali If all the transformers are None, it is stateless and is by definition fitted (imho). This is the approach that is also taken by FunctionTransformer:

    def __sklearn_is_fitted__(self):
        """Return True since FunctionTransfomer is stateless."""
        return True

Instead of all transformers being None, I am fine returning true even if all transformers are stateless (like FunctionTransformer). What do you think? If you feel strongly about introducing state to FeatureUnion through _fitted, I can push a PR with the required changes. Thanks for your feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Regarding drop, what would you like to test for it? As you know, transformers that are dropped are not yielded by _iter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@thomasjpfan Thanks. I didn't see your message while composing my responses.

@adrinjalali
adrinjalali merged commit 106ea32 into scikit-learn:main Apr 5, 2022
@randomgeek78
randomgeek78 deleted the patch-2 branch April 6, 2022 05:16
glemaitre pushed a commit to glemaitre/scikit-learn that referenced this pull request Apr 6, 2022
…learn#22953)

* Fix to checking if feature union was fitted

* Updated test and linting

* Added entry in whats new 1.1

* Expanded tests to all transformers and strings

* Expanded test

* Linting fix

* Updated tests

* Addressed additional feedback
jjerphan pushed a commit to jjerphan/scikit-learn that referenced this pull request Apr 29, 2022
…learn#22953)

* Fix to checking if feature union was fitted

* Updated test and linting

* Added entry in whats new 1.1

* Expanded tests to all transformers and strings

* Expanded test

* Linting fix

* Updated tests

* Addressed additional feedback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants