Repository navigation
[0-size Tensor No.318] Add 0-size Tensor support for paddle.Tensor.matmul API. - #72780
crashbussy wants to merge 15 commits into
Conversation
一个更鲁棒、支持广播规则、保留空维度(如 batch_size=0)的 MatmulKernel 实现版本 ,适用于多维张量的矩阵乘法(batched matmul)
matmul的0 size tensor输入的单测
|
你的PR提交成功,感谢你对开源项目的贡献! |
|
@wanghuancoder,想请问方便的时候能否帮忙审核下这个 PR?非常感谢你的review! |
|
@DanielSun11 请看下 |
|
@crashbussy 你可以看下CI日志,目前编译都没有过 |
|
@luotao1 总是跟不上develop分支的版本,develop不断的更新导致编译不通过。晚上人少的时候我同步完再试试。 |
|
@luotao1 求助,不知道为什么编译没有通过,编译报错是一些我没有动过的文件,不清楚报错与我的修改有何关联。 |
| } | ||
|
|
||
| template <typename T, typename Context> | ||
| void MatmulWithFlattenKernelImpl(const Context& dev_ctx, |
There was a problem hiding this comment.
MatmulWithFlattenKernelImpl 函数删了?在2216行有用到
|
在Tensor规范化第二期的活动中有关于0-size Tensor的测试,我正好看了关于matmul的问题,#70238 这个PR中修复的有一些问题 |
|
再次修改了一次,还是发现编译通不过。感谢@fangfangssj的细致讲解,这个修改难度确实大一些,目前还没有信心能找到对应解决办法,这个pr我就关闭了。我去完成一些添加单测的任务。 |




PR Category
Execute Infrastructure
PR Types
New features
Description
paddle.Tensor.matmul支持0-Size。
修改历程介绍如下:
在PaddleAPITest report/0size_tensor中检索paddle.Tensor.matmul的错误日志,发现[accuracy error]报错。分析可能是前向过程出错。!!!注意到(shapes (0, 100, 1, 40), (0, 100, 40) mismatch),也是就shape不匹配的问题
`2025-03-05 15:27:11.945992 test begin: paddle.Tensor.matmul(Tensor([0, 100, 1],"float64"), Tensor([0, 1, 40],"float64"), )
[accuracy error] paddle.Tensor.matmul(Tensor([0, 100, 1],"float64"), Tensor([0, 1, 40],"float64"), )
Not equal to tolerance rtol=0.01, atol=0.01
(shapes (0, 100, 1, 40), (0, 100, 40) mismatch)
x: array([], shape=(0, 100, 1, 40), dtype=float64)
y: array([], shape=(0, 100, 40), dtype=float64)
前向修复: a. 在Paddle代码中检索def matmul,发现matmul的核心实现调用的是_C_ops的matmul b. 以_C_ops的matmul在paddle/phi/ops/yaml中检索,发现matmul的InferMeta函数使用到一个: MatmulInferMeta- op: matmulargs : (Tensor x, Tensor y)
output : Tensor(out)
infer_meta :
func : MatmulInferMeta
param: [x, y, false, false]`
c. 在代码中检索MatmulInferMeta,并检查其dims(shape)的推导是否正确(在matmul中推导是正确因此不用修改)
d. 在paddle/phi/kernels中检索matmul,找全所有matmul的实现Kernel。发现共有五个涉及matmul的文件,分别为:
其中cc和cu文件均将前两个.h文件设为头文件,因此只用修改.h文件即可。而matmul_kernel.h和matmul_kernel_impl.h中不需要(不能)重复定义,故只修改了matmul_kernel_impl.h
注意,下面的文件仅以 paddle/phi/kernels/matmul_kernel.h为头文件,并不含有matmulkernel字段,所以不予以考虑
在paddle/phi/kernels/impl/matmul_kernel_impl.h中删除了原来的代码(因为有错误),加入以下代码,完成修复
` if (x.numel() == 0 || y.numel() == 0) {
auto x_dims = x.dims();
auto y_dims = y.dims();
}`
添加单测:
在test/legacy_test/test_matmul_op.py中添加0 size tensor输入的单测:
`import paddle
import numpy as np
所有测试用例
test_cases = [
# 格式: (x_shape, y_shape, expected_out_shape, dtype)
((0, 100, 1), (0, 1, 40), (0, 100, 40), "float64"),
((0, 100, 1), (0, 1, 4), (0, 100, 4), "float64"),
((0, 100, 1), (1, 1, 40), (0, 100, 40), "float64"),
((0, 100, 1), (1, 1, 4), (0, 100, 4), "float64"),
((0, 12, 197, 197), (0, 12, 197, 64), (0, 12, 197, 64), "float16"),
((0, 12, 197, 197), (0, 12, 197, 64), (0, 12, 197, 64), "float32"),
((1, 0, 1), (1, 1, 40), (1, 0, 40), "float64"),
((1, 0, 1), (1, 1, 4), (1, 0, 4), "float64"),
((1, 100, 1), (0, 1, 40), (0, 100, 40), "float64"),
((1, 100, 1), (0, 1, 4), (0, 100, 4), "float64"),
((1, 100, 1), (1, 1, 0), (1, 100, 0), "float64"),
((112, 0, 197, 197), (112, 0, 197, 64), (112, 0, 197, 64), "float16"),
((112, 0, 197, 197), (112, 0, 197, 64), (112, 0, 197, 64), "float32"),
((112, 12, 0, 197), (112, 12, 197, 64), (112, 12, 0, 64), "float16"),
((112, 12, 0, 197), (112, 12, 197, 64), (112, 12, 0, 64), "float32"),
((112, 12, 197, 197), (112, 12, 197, 0), (112, 12, 197, 0), "float16"),
((112, 12, 197, 197), (112, 12, 197, 0), (112, 12, 197, 0), "float32"),
]
def run_all_matmul_tests():
for idx, (x_shape, y_shape, expected_shape, dtype) in enumerate(test_cases):
print(f"\nTest {idx+1} begin: paddle.Tensor.matmul(Tensor({x_shape}), Tensor({y_shape}), dtype={dtype})")
try:
x = paddle.zeros(x_shape, dtype=dtype)
y = paddle.zeros(y_shape, dtype=dtype)
result = x.matmul(y)
if name == "main":
run_all_matmul_tests()`
备注:原来的代码存在错误,
std::vector<std::int64_t> out_dims(x_dims.size() - 1 + y_dims.size() - 1);对于二维 matmul 是对的,但对于多维(batched)matmul 来说,这会导致错误。
正确做法应是:batch 维度取广播后的结果;最后两维做矩阵乘法;所以输出 rank 应与广播后的 batch rank 相同 + 2(最后两个维度)
重写后支持任意 rank 的输入张量(>=2)
下面提供一个最简单的示例测试
`import paddle
测试数据
x = paddle.randn([0, 100, 1]) # shape: [0, 100, 1]
y = paddle.randn([1, 1, 4]) # shape: [1, 1, 4]
result = x.matmul(y)
print(result.shape) # 输出: [0, 100, 4], 而不是原来的[0, 100, 1, 4]`