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

【Hackathon 5th No.110】为 Paddle 增强 sparse.matmul API #721

Merged
merged 11 commits into from
Nov 16, 2023
158 changes: 158 additions & 0 deletions rfcs/APIs/20231025_api_design_for_sparse_matmul.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# paddle.sparse.matmul 增强设计文档

|API名称 | paddle.sparse.matmul |
|---|---|
|提交作者<input type="checkbox" class="rowselector hidden"> | MayYouBeProsperous |
|提交时间<input type="checkbox" class="rowselector hidden"> | 2023-10-25 |
|版本号 | V1.0 |
|依赖飞桨版本<input type="checkbox" class="rowselector hidden"> | develop |
|文件名 | 20231025_api_design_for_sparse_matmul.md<br> |


# 一、概述
## 1、相关背景
深度学习中有许多模型使用到了稀疏矩阵的乘法算子。稀疏矩阵的乘法一般可支持 `COO*Dense`、`COO*COO`、`CSR*Dense`、`CSR*CSR` 四种计算模式,目前 Paddle 已支持 `COO*Dense`、`CSR*Dense` 两种。


## 2、功能目标
为稀疏 API `paddle.sparse.matmul` 完善 `COO*COO`、`CSR*CSR` 两种计算模式。

## 3、意义

完善稀疏API `paddle.sparse.matmul` 的功能,提升稀疏 tensor API 的完整度。

# 二、飞桨现状
目前 Paddle 已支持 `COO*Dense`、`CSR*Dense` 计算模式,不支持 `COO*COO`、`CSR*CSR` 计算模式。

# 三、业内方案调研
## PyTorch

Pytorch 在计算 `Sparse * Sparse` 时,会将两个相乘的矩阵统一为 `CSR` 模式,再调用 `CSR*CSR` 计算模式的 kernel, 计算得到 `CSR` 计算结果,最后将计算结果转换 `COO` 模式,API 调用方式如下:

```python
torch.sparse.mm
torch.mm
```

CPU 版本的 kernel 为 `sparse_matmul_kernel` ,其代码的位置在 `pytorch/aten/src/ATen/native/sparse/SparseMatMul.cpp`。

CPU kernel 实现了 [Sparse matrix multiplication package (SMMP)](https://doi.org/10.1007/BF02070824) 论文中的稀疏矩阵乘法算法。

GPU 版本的 kernel 为 `sparse_sparse_matmul_cuda_kernel` ,其代码的位置在 `pytorch/aten/src/ATen/native/sparse/cuda/SparseMatMul.cu`。

GPU kernel 使用了 cuda 实现计算,当 `cudaSparse` 库可用时,kernel 调用 `cusparseSpGEMM` 进行矩阵运算,否则会调用 `thrust` 进行矩阵运算。

## TensorFlow
TensorFlow 中的 `SparseTensor` 使用了 `COO` 模式存储稀疏矩阵,为了支持 `CSR` 模式的稀疏矩阵,专门设计了 `CSRSparseMatrix`。

TensorFlow 中没有 `COO*COO` 计算模式的直接实现。

`CSR*CSR` 计算模式的 API 调用方式如下:
```python
tensorflow.python.ops.linalg.sparse.sparse_matrix_sparse_mat_mul
```

算子实现代码的位置在 `tensorflow\core\kernels\sparse\sparse_mat_mul_op.cc`,包含 CPU 版本的 `CSRSparseMatMulCPUOp` 和 GPU 版本的 `CSRSparseMatMulGPUOp`。

`CSRSparseMatMulCPUOp` 使用了 `Eigen` 库实现计算;`CSRSparseMatMulGPUOp`使用了 `cudaSparse` 库的 `cusparseSpGEMM` 实现计算。


# 四、对比分析
PyTorch 同时支持 `CSR*CSR`、`COO*COO` 计算模式,其底层只实现了 `CSR*CSR` 模式的稀疏矩阵乘法计算,在计算 `COO*COO` 模式时,底层代码会进行稀疏矩阵模式的转换。

TensorFlow 只支持 `CSR*CSR` 计算模式。

PyTorch 和 TensorFlow 在使用 GPU 进行稀疏矩阵乘法计算时,都调用了 `cudaSparse` 库。

Paddle 中已经有部分的稀疏矩阵乘法 API,代码位置在 `paddle/phi/kernels/sparse/gpu/matmul_kernel.cu`,主要是通过`cudaSparse` 库完成计算。

# 五、设计思路与实现方案

## 命名与参数设计
Paddle 中已完成 `COO*COO`、`CSR*CSR` 计算模式的函数设计。
```yaml
- op: matmul
args : (Tensor x, Tensor y)
output : Tensor(out)
infer_meta :
func : MatmulInferMeta
param: [x, y, false, false]
kernel :
func : matmul_csr_dense {sparse_csr, dense -> dense},
matmul_csr_csr {sparse_csr, sparse_csr -> sparse_csr},
matmul_coo_dense {sparse_coo, dense -> dense},
matmul_coo_coo {sparse_coo, sparse_coo -> sparse_coo}
layout : x
backward: matmul_grad
```

## 底层OP设计
Paddle 中已完成 `COO*COO`、`CSR*CSR` 计算模式的 kernel 设计。
```cpp
template <typename T, typename Context>
void MatmulCooCooKernel(const Context& dev_ctx,
const SparseCooTensor& x,
const SparseCooTensor& y,
SparseCooTensor* out);

template <typename T, typename Context>
void MatmulCsrCsrKernel(const Context& dev_ctx,
const SparseCsrTensor& x,
const SparseCsrTensor& y,
SparseCsrTensor* out);
```
对应的反向 kernel。
```cpp
template <typename T, typename Context>
void MatmulCooCooGradKernel(const Context& dev_ctx,
const SparseCooTensor& x,
const SparseCooTensor& y,
const SparseCooTensor& dout,
SparseCooTensor* dx,
SparseCooTensor* dy);

template <typename T, typename Context>
void MatmulCsrCsrGradKernel(const Context& dev_ctx,
const SparseCsrTensor& x,
const SparseCsrTensor& y,
const SparseCsrTensor& dout,
SparseCsrTensor* dx,
SparseCsrTensor* dy);
```

## API实现方案
在 `paddle/phi/kernels/sparse/gpu/matmul_kernel.cu` 中实现 `MatmulCooCooKernel` 和 `MatmulCsrCsrKernel`;

在 `paddle/phi/kernels/sparse/gpu/matmul_grad_kernel.cu` 中实现 `MatmulCooCooGradKernel` 和 `MatmulCsrCsrGradKernel`。

API 主要通过调用 `cudaSparse` 库完成计算实现,目前暂不需要开发 CPU kernel。

`cudaSparse` 库的 `cusparseSpGEMM` 只支持 `CSR*CSR` 模式,在计算 `COO*COO` 模式时,需要进行 `COO` 和 `CSR` 模式之间的转换。
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
Contributor Author

Choose a reason for hiding this comment

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

Done.


反向算子计算方式如下:

$dx = dout * y'$

$dy = x' * dout$

# 六、测试和验收的考量

在 `test/legacy_test/test_sparse_matmul_op.py` 中补充对 `COO*COO`、`CSR*CSR` 计算模式的测试。参照 `TestMatmul` 类,测试 2 维和 3 维稀疏矩阵的乘法计算。

# 七、可行性分析和排期规划

## 排期规划
1. 10月25日~10月30日完成 `MatmulCooCooKernel` 和 `MatmulCsrCsrKernel` 的实现。
2. 10月31日~11月4日完成 `MatmulCooCooGradKernel` 和 `MatmulCsrCsrGradKernel` 的实现。
3. 11月5日~11月10日完成测试代码的开发,并编写文档。

# 八、影响面
拓展 `paddle.sparse.matmul` 模块。

# 名词解释

# 附件及参考资料

[The API reference guide for cuSPARSE](https://docs.nvidia.com/cuda/cusparse/)
[CSR Sparse Matrix](https://github.com/tensorflow/community/blob/master/rfcs/20200519-csr-sparse-matrix.md)