Skip to content
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

Gram matrix support for sparse input #1296

Merged
merged 34 commits into from
Apr 25, 2023

Conversation

mfoerste4
Copy link
Collaborator

@mfoerste4 mfoerste4 commented Feb 22, 2023

This PR adds sparse input support (CSR) for GramMatrix kernel computation. This is a requirement to enable SVM support for sparse input in cuML issue 2197.

It also adds row norm computation for CSR which is utilized for expanded L2 norm computation within RBF kernels.

Although this branch introduces a new API it is still backwards compatible with the old GramMatrix API (which is marked as deprecated).

CC @cjnolet @tfeher

@mfoerste4 mfoerste4 requested review from a team as code owners February 22, 2023 20:50
@rapids-bot
Copy link

rapids-bot bot commented Feb 22, 2023

Pull requests from external contributors require approval from a rapidsai organization member with write or admin permissions before CI can begin.

@tfeher tfeher added 5 - DO NOT MERGE Hold off on merging; see PR for details feature request New feature or request non-breaking Non-breaking change breaking Breaking change and removed non-breaking Non-breaking change labels Mar 8, 2023
Copy link
Contributor

@tfeher tfeher left a comment

Choose a reason for hiding this comment

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

Hi Malte, thanks for the PR! I have looked at the changes of the Gram matrices. Overall it looks nice, below you will find my comments.

I have added the "braking" label to remind us that we have code in cuML that is affected by the interface changes (even though it is the detail namespace, so technically it should not be breaking).

I will share my comments about the new norm method separately.

cpp/include/raft/distance/detail/kernels/gram_matrix.cuh Outdated Show resolved Hide resolved
cpp/include/raft/distance/detail/kernels/gram_matrix.cuh Outdated Show resolved Hide resolved
cpp/include/raft/distance/detail/kernels/gram_matrix.cuh Outdated Show resolved Hide resolved
cpp/include/raft/distance/detail/kernels/gram_matrix.cuh Outdated Show resolved Hide resolved
cpp/test/sparse/gram.cu Show resolved Hide resolved
cpp/test/sparse/gram.cu Outdated Show resolved Hide resolved
cpp/test/sparse/gram.cu Outdated Show resolved Hide resolved
template <typename math_t>
class DenseMatrix : public Matrix<math_t> {
public:
DenseMatrix(math_t* data, int rows, int cols, bool row_major = false, int ld_in = 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we would like to use mdspan for representing the dense matrix. Could you list the pain points that would motivate keeping a pointer based API still around?

Copy link
Collaborator Author

@mfoerste4 mfoerste4 Mar 13, 2023

Choose a reason for hiding this comment

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

I thought about storing the dense data ptr as raft::device_aligned_matrix_view<math_t, std::uint32_t, layout_left_padded<math_t>>, which would combine the the information of data/n_rows/n_cols/is_row_major/lld.
This would require getters for all members as they need to be extracted from layout which is fine.

The reasons I did not choose to do it though are

  • Python interface in cuML for what this is wrapper was originally build for starts with raw pointer(s) from (Sparse)CumlArray, which is why a simple container containing these pointers was an easy and sufficient approach. Using mdspan would add additional code here in order to construct the layouts.
  • only raw data pointers need to be extracted and forwarded to third party libs (like cublas/cusparse), no element access needed that could benefit from mdspan/layout
  • matrix container for sparse would still have raw data pointers and individual members for rows/cols/nnz which feels inconsistent

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @tfeher, rather than introducing more abstractions for this, we can unify the dense and sparse APIs using overloads of the existing abstractions (with templates if needed).

We didn’t have abstractions like the mdspan available when we were building cuml so we used pointers for everything. Now that we have the proper abstractions and infrastructure in place, we should use them.

please note that the sparse API has been merged. We should be able to add overloads for both dense and sparse without having an explicit class to unify them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@cjnolet, @tfeher , I have updated the PR to utilize both device_mdspan and device_csr_matrix_view as input for GramMatrixBase. I have not yet adapted the cuML branch as I would like to hear your feedback first.

I had to explicitly define combinations of inputs for the evaluate method as it is virtual and needs to be overridden by the deriving kernels. I did not find an elegant way to describe this via templates which results in some duplicated code. If you have a better suggestion on how to design this avoiding code duplication without introducing other pain I'd be happy to hear about it.

Note that lots of the code-lines is marked as deprecated and only exist to be backwards compatible to cuML. We can remove it as soon as the cuML PR is merged as well.

Copy link
Member

@cjnolet cjnolet Mar 22, 2023

Choose a reason for hiding this comment

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

My apologies @tfeher and @mfoerste4, I meant to revisit this discussion myself and things have been pretty crazy the past couple of weeks. Please understand that my scrutiny over adding new types is only because

  1. I want to make sure that we're not exploding the number of types that a user needs work with, and
  2. making sure that we aren't creating new types each time we encounter a new pattern, unless said types can be at least moderately reusable across the codebase.

It sounds like the goal here is to have a trivial unified base class just so we can accept a single type and dispatch to the specific types. So far, the cython APIs in RAFT have gotten pretty tedious to code- and that's just for host/device mdspan. Adding sparse to the mid and accepting that on host and device is going to make things even more tedious. I definiely understand the reasoning here.

Currently, the SimpleMat contains linear algebra implementations. If the goal here is just to have a unified base class that can accept read-only inputs, could we maybe just create a very simple class called something like read_only_matrix_view which we extend for csr_read_only_matrix_view and dense_read_only_matrix_view?

Copy link
Collaborator Author

@mfoerste4 mfoerste4 Mar 22, 2023

Choose a reason for hiding this comment

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

No need to apologize. As far as I am concerned we can keep the GramMatrix raft interface as is with 3 different APIs for Dense, Sparse & Mixed input. This would keep the Raft API clean and we would not need another wrapper definition - at least not in raft.

As for cuML, in addition to the read-only views we also need a data/structure-owning matrix wrapper that allows for resizing internal data structures (ResizableCsrMatrix). As this is very specific for this use-case,
I would propose to re-use the old simple unified wrapper (with resizable addon) within cuML. It has simple constructors to be created from (Sparse)CumlArray within cython, and can be modified to internally contain/provide either mdspan or csr_matrix_views in order to be used when calling into raft.

Copy link
Member

@cjnolet cjnolet Mar 22, 2023

Choose a reason for hiding this comment

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

As for cuML, in addition to the read-only views we also need a data/structure-owning matrix wrapper that allows for resizing internal data structures (ResizableCsrMatrix). As this is very specific for this use-case,
I would propose to re-use the old simple unified wrapper (with resizable addon) within cuML.

It sounds like what you need there is indeed a structure-owning CSR matrix. Why not use the one that's already in RAFT? That's actually not a very specific use-case at all and that structure/pattern is used heavily through the sparse APIs. Again, it looks like we're duplicating types, which means increased maintenance burden.

Eventually, all of RAFT"s sparse APIs (and cuml) will be using those sparse types but these are baby steps. The purpose of those structure-owning types is to allow you to own the underlying data arrays and initialize the sparsity (resize) once you know it.

Copy link
Collaborator Author

@mfoerste4 mfoerste4 Mar 22, 2023

Choose a reason for hiding this comment

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

Why not use the one that's already in RAFT?

You are right, it seems that the data-owning csr_matrix would be a perfect fit once we switch to mdspan/csr_matrix_(view) representation within the unified wrapper.

Copy link
Collaborator Author

@mfoerste4 mfoerste4 Mar 24, 2023

Choose a reason for hiding this comment

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

As far as I am concerned we can keep the GramMatrix raft interface as is with 3 different APIs for Dense, Sparse & Mixed input.

@cjnolet, are you ok with the current API of the GramMatrix? Further discussion on the cuML APIs can take place in the cuML PR.

@mfoerste4
Copy link
Collaborator Author

Hi Malte, thanks for the PR! I have looked at the changes of the Gram matrices. Overall it looks nice, below you will find my comments.

I have added the "braking" label to remind us that we have code in cuML that is affected by the interface changes (even though it is the detail namespace, so technically it should not be breaking).

I will share my comments about the new norm method separately.

Thanks @tfeher for reviewing. I have applied your suggestions pushed an update.

Copy link
Contributor

@tfeher tfeher left a comment

Choose a reason for hiding this comment

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

Thanks Malte for updating the PR! Here are my second batch of comments (related to csr norm).

We need to find a way not to break cuML with this PR. Can we just keep the old implementation as on overload, and remove it once the corresponding cuML PR is accepted?

cpp/include/raft/sparse/linalg/norm.cuh Outdated Show resolved Hide resolved
cpp/test/sparse/normalize.cu Show resolved Hide resolved
cpp/include/raft/sparse/linalg/norm.cuh Outdated Show resolved Hide resolved
cpp/include/raft/sparse/linalg/norm.cuh Outdated Show resolved Hide resolved
@mfoerste4
Copy link
Collaborator Author

Thanks Malte for updating the PR! Here are my second batch of comments (related to csr norm).

We need to find a way not to break cuML with this PR. Can we just keep the old implementation as on overload, and remove it once the corresponding cuML PR is accepted?

I could add the old interface, but it would be a bit more intrusive as

  • we need the operator() AND evaluate interface (the latter is called directly by prediction)
  • we need a second set of constructors to allow instantiation with cublas_handle instead of raft handle
    Should I proceed anyways?

@mfoerste4
Copy link
Collaborator Author

Thanks Malte for updating the PR! Here are my second batch of comments (related to csr norm).
We need to find a way not to break cuML with this PR. Can we just keep the old implementation as on overload, and remove it once the corresponding cuML PR is accepted?

I could add the old interface, but it would be a bit more intrusive as

* we need the operator() AND evaluate interface (the latter is called directly by prediction)

* we need a second set of constructors to allow instantiation with cublas_handle instead of raft handle
  Should I proceed anyways?

@tfeher , I added the old interface for backwards compatibility and marked it as deprecated. in addition to that I followed your suggestion to modify the new API to pass the handle as runtime to the operator/evaluate function instead.

@mfoerste4 mfoerste4 requested a review from cjnolet March 30, 2023 22:23
@cjnolet cjnolet changed the base branch from branch-23.04 to branch-23.06 April 12, 2023 17:51
@cjnolet
Copy link
Member

cjnolet commented Apr 12, 2023

@mfoerste4 just a note- I bumped this to 23.06. I'll re-review so we can get this in quickly.

Copy link
Member

@cjnolet cjnolet 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 updates, @mfoerste4. The new norm and sparse::spmm APIs look great. I think this is almost there.

cpp/include/raft/distance/kernels/kernel_matrices.cuh Outdated Show resolved Hide resolved
typename NZType,
typename LayoutPolicyY,
typename LayoutPolicyZ>
void spmm(raft::device_resources const& handle,
Copy link
Member

Choose a reason for hiding this comment

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

Tagging @divyegala for awareness thoughts since he's starting on exposing the sparse APIs w/ our new core vocabulary.

Copy link
Member

@cjnolet cjnolet left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks Malte! Before we merge this, we should probably open a blank PR in cuml that pins the RAFT fork/tag in cpp/cmake/thirdparty/get_raft.cmake to your branch just to make sure we didn't overlook anything that might break cuml when we merge this.

@mfoerste4
Copy link
Collaborator Author

mfoerste4 commented Apr 20, 2023

LGTM. Thanks Malte! Before we merge this, we should probably open a blank PR in cuml that pins the RAFT fork/tag in cpp/cmake/thirdparty/get_raft.cmake to your branch just to make sure we didn't overlook anything that might break cuml when we merge this.

Thanks @cjnolet. I have created a test PR here.
UPDATE: cuml tests are green despite test_simpl_set.py which is unrelated

@cjnolet
Copy link
Member

cjnolet commented Apr 24, 2023

@mfoerste4 a few recent updates to the sparse API broke your changes here. I can make these updates and push to your banch if it's easier (since I know what changed).

@mfoerste4
Copy link
Collaborator Author

@mfoerste4 a few recent updates to the sparse API broke your changes here. I can make these updates and push to your banch if it's easier (since I know what changed).

@cjnolet , that would be great, thanks. I have been trying to keep it green but did not confirm after the last merge.

@cjnolet
Copy link
Member

cjnolet commented Apr 24, 2023

/merge

@rapids-bot rapids-bot bot merged commit 1defccc into rapidsai:branch-23.06 Apr 25, 2023
ahendriksen pushed a commit to ahendriksen/raft that referenced this pull request Apr 27, 2023
This PR adds sparse input support (CSR) for GramMatrix kernel computation. This is a requirement to enable SVM support for sparse input in [cuML issue 2197](rapidsai/cuml#2197).

It also adds row norm computation for CSR which is utilized for expanded L2 norm computation within RBF kernels.

Although this branch introduces a new API it is still backwards compatible with the old GramMatrix API (which is marked as deprecated).

CC @cjnolet @tfeher

Authors:
  - Malte Förster (https://github.com/mfoerste4)
  - Corey J. Nolet (https://github.com/cjnolet)

Approvers:
  - Tamas Bela Feher (https://github.com/tfeher)
  - Corey J. Nolet (https://github.com/cjnolet)

URL: rapidsai#1296
rapids-bot bot pushed a commit to rapidsai/cuml that referenced this pull request Jun 1, 2023
This PR adds support for sparse input to SVR and SVC. 'fit' as well as 'predict' can be called with sparse data compatible/convertible to SparseCumlArray. Support vectors in the model might also be stored as sparse data and can be retrieved as such.
This PR requires rapidsai/raft#1296 to provide sparse kernel computations.
Corresponding issue: #2197

Authors:
  - Malte Förster (https://github.com/mfoerste4)
  - Tamas Bela Feher (https://github.com/tfeher)

Approvers:
  - Tamas Bela Feher (https://github.com/tfeher)
  - Corey J. Nolet (https://github.com/cjnolet)

URL: #5273
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CMake cpp feature request New feature or request non-breaking Non-breaking change
Projects
Development

Successfully merging this pull request may close these issues.

4 participants