-
-
Notifications
You must be signed in to change notification settings - Fork 25.8k
[MRG+2] LogisticRegression convert to float64 (newton-cg) #8835
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
@GaelVaroquaux Actually fixing |
sklearn/linear_model/logistic.py
Outdated
@@ -1281,9 +1287,9 @@ def fit(self, X, y, sample_weight=None): | |||
self.n_iter_ = np.asarray(n_iter_, dtype=np.int32)[:, 0] | |||
|
|||
if self.multi_class == 'multinomial': | |||
self.coef_ = fold_coefs_[0][0] | |||
self.coef_ = fold_coefs_[0][0].astype(np.float32) |
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.
my bad it should be _dtype
You can execute the PEP8 check locally:
That should be useful in the future. |
Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg
sklearn/linear_model/logistic.py
Outdated
_dtype = np.float64 | ||
if self.solver in ['newton-cg'] \ | ||
and isinstance(X, np.ndarray) and X.dtype in [np.float32]: | ||
_dtype = np.float32 |
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.
check_X_y can take a list of acceptable dtypes as a dtype argument. I think that using this feature would be a better way of writing this code. The code would be something like
if self.solver in ['newtown-cg']: _dtype = [np.float64, np.float32]
sklearn/linear_model/logistic.py
Outdated
else: | ||
self.coef_ = np.asarray(fold_coefs_) | ||
self.coef_ = np.asarray(fold_coefs_, dtype=_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.
Is the conversion necessary here? In other word, if we get the code right, doesn't coefs_ get returned in the right dtype?
I suspect that the problem isn't really solved: if you look a bit further in the code, you will see that inside 'logistic_regression_path', check_X_y is called again with the np.float64 dtype. And there might be other instances of this problem. |
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.
However, when logistic_regression_path is called, check_input=False, therefore X.dtype remains np.float32.
Good catch!
Still, coefs starts as an empty list and end up being a np.float64.
I'll try to figure this out today.
Right, that might be where the problem needs to be fixed.
|
This reverts commit 4ac33e8.
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 for the PR!
@@ -1203,7 +1205,12 @@ def fit(self, X, y, sample_weight=None): | |||
raise ValueError("Tolerance for stopping criteria must be " | |||
"positive; got (tol=%r)" % self.tol) | |||
|
|||
X, y = check_X_y(X, y, accept_sparse='csr', dtype=np.float64, | |||
if self.solver in ['newton-cg']: | |||
_dtype = [np.float64, np.float32] |
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.
Sorry if I am missing something, but why?
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.
The idea is that previously check_X_y
was converting X
and y
into np.float64
. This is fine, if the user passes a list
as X
, but if a user passes a np.float32
willingly converting it to np.float64
penalizes them in memory and speed.
Therefore, we are trying to keep the data in np.float32
if the user provides the data in such type.
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.
The fact that @raghavrv asks a question tells us that a short comment explaining the logic should probably be useful here.
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.
|
||
for solver in ['newton-cg']: | ||
for multi_class in ['ovr', 'multinomial']: | ||
|
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.
can you remove this new line
|
||
def test_dtype_missmatch_to_profile(): | ||
# Test that np.float32 input data is not cast to np.float64 when possible | ||
|
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.
and this newline too
sklearn/utils/class_weight.py
Outdated
@@ -41,12 +41,17 @@ def compute_class_weight(class_weight, classes, y): | |||
# Import error caused by circular imports. | |||
from ..preprocessing import LabelEncoder | |||
|
|||
if y.dtype == np.float32: | |||
_dtype = np.float32 |
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 not _dtype=y.dtype
...
is it so you can have y.dtype
to be int
and weight
to be of float
?
|
||
# Check accuracy consistency | ||
lr_64 = LogisticRegression(solver=solver, multi_class=multi_class) | ||
lr_64.fit(X, Y1) |
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.
Can you ensure (maybe using astype
?) X, Y1
are of float64
before this test? (If in future it is changed, this test will still pass)
def test_dtype_match(): | ||
# Test that np.float32 input data is not cast to np.float64 when possible | ||
|
||
X_ = np.array(X).astype(np.float32) |
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.
X_32 = ... astype(32)
X_64 = ... astype(64)
assert_almost_equal(lr_32.coef_, lr_64.coef_.astype(np.float32)) | ||
|
||
|
||
def test_dtype_missmatch_to_profile(): |
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 test can be removed...
@@ -608,10 +610,10 @@ def logistic_regression_path(X, y, pos_class=None, Cs=10, fit_intercept=True, | |||
# and check length | |||
# Otherwise set them to 1 for all examples | |||
if sample_weight is not None: | |||
sample_weight = np.array(sample_weight, dtype=np.float64, order='C') | |||
sample_weight = np.array(sample_weight, dtype=X.dtype, order='C') |
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.
Should it be y.dtype
?
cc: @agramfort
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.
we were discussing with @glemaitre (and @GaelVaroquaux) to force X.dtype
and y.dtype
to be the same.
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.
Yes, I think the idea should be that the dtype of X conditions the dtype of the computation.
We should be an RFC about this, and include it in the docs.
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.
see #8976
|
||
# Check accuracy consistency | ||
lr_64 = LogisticRegression(solver=solver, multi_class=multi_class) | ||
lr_64.fit(X_64, y_64) |
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 add:
assert_equal(lr_64.coef_.dtype, X_64.dtype)
otherwise this test passes when we transform everything to 32 bits
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.
LGTM
+1 for MRG if travis is happy |
I would convert Y to dtype on the top
+1
|
Before we merge, this warrants a whats_new entry. |
appveyor is not happy :( |
It looks like some of the appveyor and travis unhappiness may have been
caused by a github outage.
…On 7 June 2017 at 05:57, Alexandre Gramfort ***@***.***> wrote:
appveyor is not happy :(
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#8835 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAEz66rWVqgrn_OUR50p17qnX4jHHQDUks5sBa9CgaJpZM4NSM_O>
.
|
https://status.github.com/
and otherwise due to backlog?
…On 7 June 2017 at 10:38, Joel Nothman ***@***.***> wrote:
It looks like some of the appveyor and travis unhappiness may have been
caused by a github outage.
On 7 June 2017 at 05:57, Alexandre Gramfort ***@***.***>
wrote:
> appveyor is not happy :(
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <#8835 (comment)>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAEz66rWVqgrn_OUR50p17qnX4jHHQDUks5sBa9CgaJpZM4NSM_O>
> .
>
|
all green merging |
Whats_new entry would have been good :) |
here you go:
8baaa0e
|
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
…rn#8835) * Add a test to ensure not changing the input's data type Test that np.float32 input data is not cast to np.float64 when using LR + newton-cg * [WIP] Force X to remain float32. (self.coef_ remains float64 even if X is not) * [WIP] ensure self.coef_ same type as X * keep the np.float32 when multi_class='multinomial' * Avoid hardcoded type for multinomial * pass flake8 * Ensure that the results in 32bits are the same as in 64 * Address Gael's comments for multi_class=='ovr' * Add multi_class=='multinominal' to test * Add support for multi_class=='multinominal' * prefer float64 to float32 * Force X and y to have the same type * Revert "Add support for multi_class=='multinominal'" This reverts commit 4ac33e8. * remvert more stuff * clean up some commmented code * allow class_weight to take advantage of float32 * Add a test where X.dtype is different of y.dtype * Address @raghavrv comments * address the rest of @raghavrv's comments * Revert class_weight * Avoid copying if dtype matches * Address alex comment to the cast from inside _multinomial_loss_grad * address alex comment * add sparsity test * Addressed Tom comment of checking that we keep the 64 aswell
Reference Issue
Fixes #8769
What does this implement/fix? Explain your changes.
Avoids logistic regression to aggressively cast the data to
np.float64
whennp.float32
is supplied.Any other comments?
(only for the
newton-cg
case)