[WIP] Common test for equivalence between sparse and dense matrices.#13246
[WIP] Common test for equivalence between sparse and dense matrices.#13246wdevazelhes wants to merge 127 commits intoscikit-learn:mainfrom
Conversation
# Conflicts: # sklearn/utils/estimator_checks.py
|
I tried to integrate the dictionary of values to overwrite the default, as in this @lesteve comment: #7590 (comment) |
|
Right now here are the tests that fail (without for now looking at whether it's a real fail, or if it's because the input takes different paths between sparse and non sparse):
|
|
for linear models it's likely that sparse data are not centered to output
will differ.
only coordinate_descent will implicitly center sparse data.
… |
|
Setting |
…learn into tests_sparse
sklearn/neighbors/regression.py
Outdated
| """ | ||
| if issparse(X) and self.metric == 'precomputed': | ||
| raise ValueError( | ||
| raise TypeError( |
There was a problem hiding this comment.
@agramfort should I leave this for another PR or is it ok to change it here ?
There was a problem hiding this comment.
@agramfort related to our discussion, in fact here they do call check_array but the sparse error is raised by this "if", which has a ValueError, so I guess this is the right fix (and I'll let it in this PR for this one as you said)
sklearn/utils/estimator_checks.py
Outdated
| assert_equal(probs.shape, (X.shape[0], 4)) | ||
| except TypeError as e: | ||
| if 'sparse' not in repr(e): | ||
| if 'sparse' not in str.lower(repr(e)): |
There was a problem hiding this comment.
I put this because the estimator that used kernel='precomputed' raised something like "Sparse..." which is an OK message
sklearn/utils/estimator_checks.py
Outdated
| @ignore_warnings(category=(DeprecationWarning, FutureWarning)) | ||
| def check_estimator_sparse_dense(name, estimator_orig): | ||
| rng = np.random.RandomState(42) | ||
| rng = np.random.RandomState(52) |
There was a problem hiding this comment.
@agramfort I had the same problem of random seed here for AdaBoostClassifier: (tested at the end of test_check_estimator)
this random seed fixed it
…se same n_samples as n_features created other bugs, and fix pep8 errors
|
The new seed (52) introduced a weird bug for AffinityPropagation that predicts two really different arrays (one with zeros everywhere and one with -1 everywhere (putting back the seed to 0 fixes the pb). I'll try to investigate that Err message: Testing started at 09:57 ...
/home/will/anaconda3/envs/sprint/bin/python /snap/pycharm-community/112/helpers/pycharm/_jb_pytest_runner.py --target test_common.py::test_estimators -- -k test_estimators[AffinityPropagation-check_estimator_sparse_dense]
Launching pytest with arguments -k test_estimators[AffinityPropagation-check_estimator_sparse_dense] test_common.py::test_estimators in /home/will/Code/sklearn-forks/wdevazelhes/scikit-learn/sklearn/tests
============================= test session starts ==============================
platform linux -- Python 3.7.2, pytest-4.3.0, py-1.7.0, pluggy-0.8.1
rootdir: /home/will/Code/sklearn-forks/wdevazelhes/scikit-learn, inifile: setup.cfg
plugins: sugar-0.9.2collected 5385 items / 5384 deselected / 1 selected
test_common.py FEstimator AffinityPropagation doesn't seem to fail gracefully on sparse data: it should raise a TypeError if sparse input is explicitly not supported.
sklearn/tests/test_common.py:101 (test_estimators[AffinityPropagation-check_estimator_sparse_dense])
estimator = AffinityPropagation(affinity='euclidean', convergence_iter=15, copy=True,
damping=0.5, max_iter=5, preference=None, verbose=False)
check = <function check_estimator_sparse_dense at 0x7fa5ee03e048>
@pytest.mark.parametrize(
"estimator, check",
_generate_checks_per_estimator(_yield_all_checks,
_tested_estimators()),
ids=_rename_partial
)
def test_estimators(estimator, check):
# Common tests for estimator instances
with ignore_warnings(category=(DeprecationWarning, ConvergenceWarning,
UserWarning, FutureWarning)):
set_checking_parameters(estimator)
name = estimator.__class__.__name__
> check(name, estimator)
test_common.py:114:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../utils/testing.py:350: in wrapper
return fn(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
name = 'AffinityPropagation'
estimator_orig = AffinityPropagation(affinity='euclidean', convergence_iter=15, copy=True,
damping=0.5, max_iter=5, preference=None, verbose=False)
@ignore_warnings(category=(DeprecationWarning, FutureWarning))
def check_estimator_sparse_dense(name, estimator_orig):
rng = np.random.RandomState(52)
X = rng.rand(40, 10)
X[X < .8] = 0
X_csr = sparse.csr_matrix(X)
y = (4 * rng.rand(40)).astype(np.int)
estimator = clone(estimator_orig)
estimator_sp = clone(estimator_orig)
for sparse_format in ['csr', 'csc', 'dok', 'lil', 'coo', 'dia', 'bsr']:
X_sp = X_csr.asformat(sparse_format)
# catch deprecation warnings
with ignore_warnings(category=DeprecationWarning):
if name in ['Scaler', 'StandardScaler']:
estimator.set_params(with_mean=False)
estimator_sp.set_params(with_mean=False)
dense_vs_sparse_additional_params = defaultdict(dict,
{'Ridge': {'solver': 'cholesky'}})
params = dense_vs_sparse_additional_params[
estimator.__class__.__name__]
estimator.set_params(**params)
estimator_sp.set_params(**params)
set_random_state(estimator)
set_random_state(estimator_sp)
try:
with ignore_warnings(category=DeprecationWarning):
estimator_sp.fit(X_sp, y)
estimator.fit(X, y)
if hasattr(estimator, "predict"):
pred = estimator.predict(X)
pred_sp = estimator_sp.predict(X_sp)
> assert_array_almost_equal(pred, pred_sp, 2)
E AssertionError:
E Arrays are not almost equal to 2 decimals
E
E (mismatch 100.0%)
E x: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
E y: array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
E -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
E -1, -1, -1, -1, -1, -1])
../utils/estimator_checks.py:2367: AssertionError
[100%]
=================================== FAILURES ===================================
______ test_estimators[AffinityPropagation-check_estimator_sparse_dense] _______
estimator = AffinityPropagation(affinity='euclidean', convergence_iter=15, copy=True,
damping=0.5, max_iter=5, preference=None, verbose=False)
check = <function check_estimator_sparse_dense at 0x7fa5ee03e048>
@pytest.mark.parametrize(
"estimator, check",
_generate_checks_per_estimator(_yield_all_checks,
_tested_estimators()),
ids=_rename_partial
)
def test_estimators(estimator, check):
# Common tests for estimator instances
with ignore_warnings(category=(DeprecationWarning, ConvergenceWarning,
UserWarning, FutureWarning)):
set_checking_parameters(estimator)
name = estimator.__class__.__name__
> check(name, estimator)
test_common.py:114:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../utils/testing.py:350: in wrapper
return fn(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
name = 'AffinityPropagation'
estimator_orig = AffinityPropagation(affinity='euclidean', convergence_iter=15, copy=True,
damping=0.5, max_iter=5, preference=None, verbose=False)
@ignore_warnings(category=(DeprecationWarning, FutureWarning))
def check_estimator_sparse_dense(name, estimator_orig):
rng = np.random.RandomState(52)
X = rng.rand(40, 10)
X[X < .8] = 0
X_csr = sparse.csr_matrix(X)
y = (4 * rng.rand(40)).astype(np.int)
estimator = clone(estimator_orig)
estimator_sp = clone(estimator_orig)
for sparse_format in ['csr', 'csc', 'dok', 'lil', 'coo', 'dia', 'bsr']:
X_sp = X_csr.asformat(sparse_format)
# catch deprecation warnings
with ignore_warnings(category=DeprecationWarning):
if name in ['Scaler', 'StandardScaler']:
estimator.set_params(with_mean=False)
estimator_sp.set_params(with_mean=False)
dense_vs_sparse_additional_params = defaultdict(dict,
{'Ridge': {'solver': 'cholesky'}})
params = dense_vs_sparse_additional_params[
estimator.__class__.__name__]
estimator.set_params(**params)
estimator_sp.set_params(**params)
set_random_state(estimator)
set_random_state(estimator_sp)
try:
with ignore_warnings(category=DeprecationWarning):
estimator_sp.fit(X_sp, y)
estimator.fit(X, y)
if hasattr(estimator, "predict"):
pred = estimator.predict(X)
pred_sp = estimator_sp.predict(X_sp)
> assert_array_almost_equal(pred, pred_sp, 2)
E AssertionError:
E Arrays are not almost equal to 2 decimals
E
E (mismatch 100.0%)
E x: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
E y: array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
E -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
E -1, -1, -1, -1, -1, -1]) |
|
This issue: #17554, is causing the following test not to pass: |
sklearn/utils/estimator_checks.py
Outdated
| tags = estimator_orig._get_tags() | ||
| centers = 2 if tags["binary_only"] else None | ||
| X, y = make_blobs(random_state=rng, cluster_std=0.5, centers=centers) | ||
| X, y = make_blobs(n_samples=10, n_features=2, random_state=rng, |
There was a problem hiding this comment.
I simplified the data here similarly to before, because there were some numerical uncertainties with the Nystrom method (particularly with almost zero elements: the relative error between two numbers can be quite high but both number can be of order 1e-15 for instance, so they are clearly supposed to be zeros). With a simpler dataset that will lead to less operations (because less samples, less features, and less nonzero decimals), the uncertainties disappeared on my machine
|
Hi @wdevazelhes , do you mind synchronizing with upstream? Checks are failing looking for the master branch. Thanks! |
|
Hi @cmarmo , thanks, I just merged with master. |
|
removing the milestone for now. |
|
Is this PR ready for review? |
|
@haiatn this one would deserve a big refresher to be now considered for merge. I don't know how much we should invest on this one at this point. |
I do like the idea of testing this, but if you think so, maybe we should close this and leave the issue open for whoever that would like to start fresh |
|
you can see how difficult it would be to rebase this PR on current main
branch. Feel free to push directly to this PR
or open a new one.
… Message ID: ***@***.***>
|
|
I think this can be closed, the original issue is still open and relevant |
Fixes #1572
Follow-up of #7590
TODO:
Try to be more robust by maybe testing only the score (if possible), with a certain tolerance, and for certain random seeds (or if testing the predict also maybe test that like 90% of predictions are the same ?)After discussion with @agramfort , it would be better to test on a realistic dataset which would avoid weird instable behaviours
[MRG] Implement fitting intercept withCython code for PolynomialFeatures should use int64s for indices. #17554 )sparse_cgsolver in Ridge regression #13336check_estimator_sparse_denseby the one (more exhaustive) incheck_estimator_sparse_datathat it tests well multiclass and multioutputs estimators->see if it's necessary here to check with multioutput sparse datasets