Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Add sparse sum and mean operator #159

Closed
wants to merge 4 commits into from
Closed

Conversation

kalyc
Copy link

@kalyc kalyc commented Aug 24, 2018

Summary

Add sparse support for sum, mean and dot operators

Related Issues

Missing sparse operators

PR Overview

  • [y] This PR requires new unit tests [y/n] (make sure tests are included)
  • [n] This PR requires to update the documentation [y/n] (make sure the docs are up-to-date)
  • [y] This PR is backwards compatible [y/n]
  • [n] This PR changes the current API [y/n]

@kalyc kalyc changed the title Add sparse sum and mean operator [WIP] Add sparse sum and mean operator Aug 24, 2018
@kalyc kalyc changed the title [WIP] Add sparse sum and mean operator Add sparse sum and mean operator Aug 24, 2018
Copy link

@roywei roywei left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution, few comments in-line.

  1. Remove skip if not mxnet backend. these tests can be tested with tf backend as well.

  2. Add assert is_sparse to make sure sparse tensor is used

  3. There is no mxnet sparse implementation in this PR, suggest to change title to Add sparse tests for sum and mean operator. Make it available for tf backend, and make sure tests pass for tf backend. After sparse implementation, we can use these tests to test mxnet backend.

@@ -1702,6 +1702,56 @@ def test_sparse_concat(self):
assert k_s_d.shape == k_d.shape
assert_allclose(k_s_d, k_d, atol=1e-05)

@pytest.mark.skipif((K.backend() != 'mxnet'),
reason='Testing only for MXNet backend')
Copy link

Choose a reason for hiding this comment

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

There is nothing specific for mxnet, we can test tensorflow backend as well.

Copy link
Author

Choose a reason for hiding this comment

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

the eval() implementation is different for mxnet - eval() converts the array into a numpy array - making it dense & fails the test. In Tensorflow the original class of the sparse tensor doesn't change - https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/sparse_ops.py#L724

Copy link

Choose a reason for hiding this comment

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

It's the same for both backends, eval() are supposed to convert to dense and return a numpy array. same in tensorflow backend. See tf_backend

Copy link
Author

Choose a reason for hiding this comment

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

With MXNet backend, when we try to evaluate the value of a sparse KerasSymbol - the dense value is returned and because of this is_sparse(eval(KerasSymbol)) is always False - we basically return the numpy array which is always dense.
In TF backend, a SparseTensor class has been used and because of this even when the tensor is converted to dense it always remains an instance of the SparseTensor class

Choose a reason for hiding this comment

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

Will this behavior not become an issue during training when weights are sparse tensor?

Copy link
Author

Choose a reason for hiding this comment

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

I dont think this should be an issue. We are just abstracting the _forward_pass() operation in eval() and then converting it to a numpy array.


W = np.random.random((5, 4))

k_s = K.eval(K.sum(K.variable(x_sparse), axis=0))
Copy link

Choose a reason for hiding this comment

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

add check for is_sparse like in the test sparse concat

k_s = k.concatenate([k.variable(x_sparse_1), k.variable(x_sparse_2)])
assert k.is_sparse(k_s)

Copy link

Choose a reason for hiding this comment

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

For mxnet backend, currently sparse tensor is not created in K.variable, so this is comparing the same results. Adding assert k.is_sparse() will ensure sparse tensor is used.

Copy link
Author

Choose a reason for hiding this comment

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

added implementation of sparse tensor as well now

assert k_s.shape == k_d.shape
assert_allclose(k_s, k_d, atol=1e-05)

@pytest.mark.skipif((K.backend() != 'mxnet'),
Copy link

Choose a reason for hiding this comment

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

same as test_add, There is nothing specific for mxnet, we can test tensorflow backend as well.

Copy link
Author

Choose a reason for hiding this comment

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

see comment above

x_sparse = sparse.csr_matrix((x_d, (x_r, x_c)), shape=(4, 5))
x_dense = x_sparse.toarray()

k_s = K.eval(K.mean(K.variable(x_sparse), axis=0))
Copy link

Choose a reason for hiding this comment

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

Same as above, add assert is_sparse

Copy link
Author

Choose a reason for hiding this comment

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

added

x_sparse = sparse.csr_matrix((x_d, (x_r, x_c)), shape=(4, 5))
x_dense = x_sparse.toarray()

k_s = K.eval(K.mean(K.variable(x_sparse)))
Copy link

Choose a reason for hiding this comment

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

Same as above, add assert is_sparse

Copy link
Author

Choose a reason for hiding this comment

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

added

assert k_s.shape == k_d.shape
assert_allclose(k_s, k_d, atol=1e-05)

@pytest.mark.skipif((K.backend() != 'mxnet'),
Copy link

Choose a reason for hiding this comment

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

same as test_add, There is nothing specific for mxnet, we can test tensorflow backend as well.

Copy link
Author

Choose a reason for hiding this comment

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

same as above

Copy link

@roywei roywei left a comment

Choose a reason for hiding this comment

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

Thanks for the update! Really cool work!
Seems that this is a general solution, not specific to sum and mean ops, could you also enable other sparse tests to verify? for example test_sparse_dot and test_sparse_concat. In addition to adding new test cases, with this implementation, we should enable previously disabled unit tests in Keras that tests sparse.

Copy link

@sandeep-krishnamurthy sandeep-krishnamurthy left a comment

Choose a reason for hiding this comment

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

Thanks @kalyc . Great work! Looking forward for first end-to-end sparse model :-)

return tensor.toarray()
elif isinstance(tensor, mx.sym.Symbol):
return tensor.stype('default')
elif isinstance(tensor, KerasSymbol):

Choose a reason for hiding this comment

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

else ?

Copy link
Author

Choose a reason for hiding this comment

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

else returning tensor the way it is

@@ -4149,10 +4174,10 @@ def dfs_get_bind_values(node_start):
return bind_values


def _keras_variable(name, shape, dtype, is_vector=False, **kwargs):
def _keras_variable(name, shape, dtype, stype, is_vector=False, **kwargs):

Choose a reason for hiding this comment

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

Should we set default to 'default'?

Copy link
Author

Choose a reason for hiding this comment

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

yep will do

@@ -1702,6 +1702,56 @@ def test_sparse_concat(self):
assert k_s_d.shape == k_d.shape
assert_allclose(k_s_d, k_d, atol=1e-05)

@pytest.mark.skipif((K.backend() != 'mxnet'),
reason='Testing only for MXNet backend')

Choose a reason for hiding this comment

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

Will this behavior not become an issue during training when weights are sparse tensor?

@@ -1702,6 +1702,107 @@ def test_sparse_concat(self):
assert k_s_d.shape == k_d.shape
assert_allclose(k_s_d, k_d, atol=1e-05)

@pytest.mark.skipif((K.backend() != 'mxnet'),
reason='Testing only for MXNet backend')
def test_sparse_sum(self):

Choose a reason for hiding this comment

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

I think it will be good idea to move all sparse tests to a separate file. may be - tests/keras/backend/sparse_test.py ?
It will be easier later for merge/rebase etc. and also cleanly separates the tests, that can be eventually contributed back to Keras tests

Copy link
Author

Choose a reason for hiding this comment

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

sounds good - will do

@kalyc
Copy link
Author

kalyc commented Aug 28, 2018

PR continued here - #162
Was unable to update this one.

Have enabled sparse dot operator test.
Sparse concat operator would require addition of one more check - will open another PR for it.

@sandeep-krishnamurthy
Copy link

@kalyc - Please feel free to close this PR as we running through another PR :-)

@kalyc
Copy link
Author

kalyc commented Sep 4, 2018

Closing this PR in place of this one - #162

@kalyc kalyc closed this Sep 4, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants