-
-
Notifications
You must be signed in to change notification settings - Fork 25.8k
[MRG] Add common test and estimator tag for preserving float32 dtype in transformers #16290
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Overall sounds reasonable to me. I'm not sure it we should keep the default preserves_32bit_dtype
as False or True, but I think there should be no expectation for transformers to pass this test by default.
sklearn/utils/estimator_checks.py
Outdated
@@ -1339,6 +1343,35 @@ def check_estimators_dtypes(name, estimator_orig): | |||
getattr(estimator, method)(X_train) | |||
|
|||
|
|||
def check_estimators_preserve_dtypes(name, estimator_orig): | |||
|
|||
if not _safe_tags(estimator_orig, 'preserves_32bit_dtype'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually we don't implement in this way. We avoid to call the function if the tag is false. see above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it was done inside, so that we would have a list of estimators that fail and that needs fixing, as a temporary approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved it now, as one needs to change the default value of the tag anyways to true to check if an estimator actually fails the test and is not just is skipped.
And after the meta issue is resolved one doesn't need to change anything.
sklearn/utils/estimator_checks.py
Outdated
@@ -197,6 +198,9 @@ def _yield_transformer_checks(name, transformer): | |||
yield check_transformer_data_not_an_array | |||
# these don't actually fit the data, so don't raise errors | |||
yield check_transformer_general | |||
# it's not important to preserve types with Clustering | |||
if not isinstance(transformer, ClusterMixin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not isinstance(transformer, ClusterMixin): | |
if (not isinstance(transformer, ClusterMixin) and | |
_safe_tags(transformer, "preserves_32bit_dtype")): |
assert X_trans.dtype == dtype_out, \ | ||
('Estimator transform dtype: {} - orginal/expected dtype: {}' | ||
.format(X_trans.dtype, dtype_out.__name__)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the end maybe we should add the check comparing transforms in this PR, say using,
assert_allclose(X_trans_32, X_trans_64, rtol=1e-2)
with a high enough tolerance. Before we start modifying other estimators to pass this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why such a high tolerance ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, maybe that was too pessimistic. Maybve rtol=1e-4 then, what value would be good you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't know :/ Ideally it would be around 1e-6, but it's not realistic.
The output precision depends of the algorithm and we probably can't expect machine precision for all algorithms even with the best possible implementation.
I put 1e-5 in the tests of our wrappers of scipy blas but I'm fine with 1e-4
@@ -1339,6 +1344,31 @@ def check_estimators_dtypes(name, estimator_orig): | |||
getattr(estimator, method)(X_train) | |||
|
|||
|
|||
def check_estimators_preserve_dtypes(name, estimator_orig): | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please skip this test for all estimators that currently fail,
if name in [...]:
raise SkipTest('Known failure to preserve dtypes')
so we can merge this.
The XFAIL support from #16306 could be used once that PR is merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are already skipped here for those who fail. https://github.com/scikit-learn/scikit-learn/pull/16290/files#diff-a95fe0e40350c536a5e303e87ac979c4R194-R213
Also shouldn't more estimators have the preserves_32bit_dtype=True flag? Say |
doc/developers/develop.rst
Outdated
@@ -521,6 +521,9 @@ poor_score (default=``False``) | |||
are based on current estimators in sklearn and might be replaced by | |||
something more systematic. | |||
|
|||
preserves_32bit_dtype (default=``False``) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the common test is a bit broader than only preserving 32 bits. I think that we should go with the tag preserves_dtype
which should be an array of dtype which will be preserved. By default, it will be only 64 bits
so in short
preserves_32bit_dtype (default=``False``) | |
preserves_dtype (default=``['float64']``) |
and one could give ['float64', 'float'32', 'float16']
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ksslng Would you be able to finish the PR or shall I take over?
sklearn/utils/estimator_checks.py
Outdated
|
||
for dtype_in, dtype_out in [(np.float32, np.float32), | ||
(np.float64, np.float64), | ||
(np.float16, np.float64)]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StandardScaler will not pass this test because it preserving even 16 bits without casting which is fine.
Basically, for dtype in the preserves_dtype
list, we should ensure that the dtype is the same, otherwise, it should be casted to 64 bits.
sklearn/utils/estimator_checks.py
Outdated
# FIXME: should we check that the dtype of some attributes are the | ||
# same than dtype and check that the value of attributes | ||
# between 32bit and 64bit are close | ||
assert X_trans.dtype == dtype_out, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert X_trans.dtype == dtype_out, \ | |
assert X_trans.dtype.name == dtype_out, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be the changes with we use a string in the list of preserves_dtype
sklearn/utils/estimator_checks.py
Outdated
for dtype_in, dtype_out in [(np.float32, np.float32), | ||
(np.float64, np.float64), | ||
(np.float16, np.float64)]: | ||
X_cast = X.copy().astype(dtype_in) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will work directly with string name of a dtype.
I'll have time this weekend and I have a couple of changes locally and not pushed yet. I also added the test to compare the float32 and float64 output of transform, should I leave it in there, or make a different pr out of it? As the question, is if one want to actually guarantee it? |
great
It can be added here and we can check when is it failing as well |
So a couple of things to add:
So to the transformers that fails.
These are the ones that return not close enough transformations:
|
@ksslng, it would be great if you could find some time to sync your PR with upstream and check build failures. Then, maybe reviewers will show up again... :) |
Reference Issues/PRs
Part of #11000.
What does this implement/fix? Explain your changes.
It will make testing and implementing of #11000 easier. Also added a new tag
preserve_32bit_type
, which skips the test if false.Any other comments?
Verifying that values and results are similar as without conversion is needed to be added.
Also it's part of Paris sprint.