-
Notifications
You must be signed in to change notification settings - Fork 273
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.36】 为 Paddle 新增 matrix_exp API #775
Conversation
- paddle.isfinite | ||
- paddle.linalg.solve | ||
- paddle.static.nn.cond | ||
- paddle.static.nn.while_loop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
需要控制流的原因是?这两个API支持静态图,并且有些bug,能否使用其它API代替
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个算法是个近似算法,计算的时候会有逻辑判断,参考一下 eigen 的实现:
https://eigen.tuxfamily.org/dox/unsupported/MatrixExponential_8h_source.html
template <typename MatrixType>
struct matrix_exp_computeUV<MatrixType, double>
{
typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
template <typename ArgType>
static void run(const ArgType& arg, MatrixType& U, MatrixType& V, int& squarings)
{
using std::frexp;
using std::pow;
const RealScalar l1norm = arg.cwiseAbs().colwise().sum().maxCoeff();
squarings = 0;
if (l1norm < 1.495585217958292e-002) {
matrix_exp_pade3(arg, U, V);
} else if (l1norm < 2.539398330063230e-001) {
matrix_exp_pade5(arg, U, V);
} else if (l1norm < 9.504178996162932e-001) {
matrix_exp_pade7(arg, U, V);
} else if (l1norm < 2.097847961257068e+000) {
matrix_exp_pade9(arg, U, V);
} else {
const RealScalar maxnorm = 5.371920351148152;
frexp(l1norm / maxnorm, &squarings);
if (squarings < 0) squarings = 0;
MatrixType A = arg.unaryExpr(MatrixExponentialScalingOp<RealScalar>(squarings));
matrix_exp_pade13(A, U, V);
}
}
};
这是其中一段 uv 的计算,会不断的比对 l1norm 和一些常数 ~
paddle 实现的话如果涉及到比对 tensor 和 数值,之前尝试过用 if 来实现,不过静态图好像会有问题?所以只能用 paddle.static.nn.cond
paddle.static.nn.while_loop
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个逻辑用python去组,确实会有问题,除非写到kernel中可以避免控制流。这个待定吧,先把前面两种算法性能测试验证完,如果选型是torch算法,可能实现为一个kernel更合适;如果选型是近似算法,可以按照这个思路实现一半,验证一下,如果静态图有问题,可以做下降级,先支持动态图版本(这个我去确认下)
|
||
使用 python 实现,不涉及底层 op。 | ||
|
||
## API实现方案 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
竞品调研中提到了pade近似及泰勒级数两种算法,这两种算法优劣需要对比,并且需要说明下Paddle实现基哪种方案
|
||
该 API 实现于 `python/paddle/tensor/linalg.py`. | ||
|
||
涉及到的 paddle api 主要有: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
可以再补充下基于这些实现该API伪代码,进一步降低实现上的风险
* 对于参数异常值输入,例如x的不合法值等,应该有友好的报错信息及异常反馈,需要有相关测试Case验证。 | ||
|
||
# 七、可行性分析和排期规划 | ||
技术可行性:参考同类项目和相似的 API,相关内容参考丰富,无重大难点; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
如果需要用到控制流,是一个比较大的风险点,目前飞桨控制流支持尚不完善,在快速迭代中
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个算法好像不用控制流没法做,tensorflow, eigen, scipy 里面的 pade 近似都是比对 l1norm 实现的 ~ 😂
Update 20231208
这个实现方案用的帕德近似,里面涉及到很多的数值对比,因此需要用到控制流 (if/while 静态图好像不能用?)~ 实际上,pytorch 的泰勒多项式算法,也会涉及到数值对比 ~ matrix exp 算法涉及到的数学内容较多,本人能力有限,也只能参考各个实现方法,然后转换为 paddle 能够实现的方式 ~ 🤣🤣🤣 @cxxly 请评审 ~ 谢谢! |
|
||
PyTorch 的实现基于上述论文,发表于 2019 年,参考资料较少。 | ||
|
||
基于此,本次实现基于 Pade 近似的方式,并且考虑到算子兼容情况,使用 python 的实现方式。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytorch目前是使用最为广泛的框架,并且也是飞桨主要竞品;需要进一步测试,验证下性能及精度差距。比如,可以用tf和torch对比下性能和精度。如果性能/精度差距较大,还是选择更好的算法。先不用担心实现的问题,如果实现难度较大,我们可以提升题目难度。
嗯,其实我自己也比较好奇,这几天就想测试一下的 ~ 刚抽出时间来测了一下: 我在本地测试的,cpu 是 i5-12400,ubutnu 22.04 ~ In [1]: import paddle
In [3]: import torch
In [4]: import tensorflow as tf
In [6]: import numpy as np
In [8]: import math
In [9]: m = np.array([[0, math.pi/3], [-math.pi/3, 0]])
In [10]: m_torch = torch.tensor(m)
In [11]: me_torch = torch.linalg.matrix_exp(m_torch)
In [12]: m_tf = tf.convert_to_tensor(m)
In [13]: me_tf = tf.linalg.expm(m_tf)
In [14]: import scipy
In [15]: me_scipy = scipy.linalg.expm(m)
In [18]: m_paddle = paddle.to_tensor(m)
In [23]: me_paddle = matrix_exp(m_paddle)
In [25]:
In [25]: %timeit me_torch = torch.linalg.matrix_exp(m_torch)
45 µs ± 695 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [26]: %timeit me_torch = torch.linalg.matrix_exp(m_torch)
45.6 µs ± 865 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [27]: %timeit me_tf = tf.linalg.expm(m_tf)
4.73 ms ± 45.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [28]: %timeit me_tf = tf.linalg.expm(m_tf)
4.7 ms ± 34.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [29]: %timeit me_scipy = scipy.linalg.expm(m)
30.1 µs ± 486 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [30]: %timeit me_scipy = scipy.linalg.expm(m)
30 µs ± 544 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [31]: %timeit me_paddle = matrix_exp(m_paddle)
1.15 ms ± 7.34 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
In [32]: %timeit me_paddle = matrix_exp(m_paddle)
1.14 ms ± 14.7 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
In [33]:
In [33]:
In [36]: s_a = np.random.rand(2, 3, 3)
In [37]: s_b = np.random.rand(2, 3, 4)
In [38]: s_a_torch = torch.tensor(s_a)
In [39]: s_b_torch = torch.tensor(s_b)
In [41]: %timeit torch.linalg.solve(s_a_torch, s_b_torch)
9.2 µs ± 65.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
In [42]: s_a_paddle = paddle.to_tensor(s_a)
In [43]: s_b_paddle = paddle.to_tensor(s_b)
In [45]: %timeit paddle.linalg.solve(s_a_paddle, s_b_paddle)
43 µs ± 1.55 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [46]:
In [46]: def max_diff(a, b):
...: return np.max(np.abs(a-b))
...:
In [47]: def max_rela(a, b):
...: diff = np.abs(a-b)
...: return np.max(diff/np.abs(a))
...:
In [48]: max_diff(me_scipy, me_paddle.numpy())
Out[48]: 1.1102230246251565e-16
In [50]: max_diff(me_scipy, me_torch.numpy())
Out[50]: 3.3306690738754696e-16
In [51]: max_diff(me_scipy, me_tf.numpy())
Out[51]: 1.1102230246251565e-16
In [52]: max_rela(me_scipy, me_paddle.numpy())
Out[52]: 1.2819751242557095e-16
In [53]: max_rela(me_scipy, me_torch.numpy())
Out[53]: 3.845925372767128e-16
In [54]: max_rela(me_scipy, me_tf.numpy())
Out[54]: 1.2819751242557095e-16
In [55]: max_diff(me_torch.numpy(), me_paddle.numpy())
Out[55]: 4.440892098500626e-16
In [56]: max_diff(me_torch.numpy(), me_scipy)
Out[56]: 3.3306690738754696e-16
In [57]: max_diff(me_torch.numpy(), me_tf.numpy())
Out[57]: 4.440892098500626e-16
In [58]: max_rela(me_torch.numpy(), me_paddle.numpy())
Out[58]: 5.12790049702284e-16
In [59]: max_rela(me_torch.numpy(), me_scipy)
Out[59]: 3.8459253727671296e-16
In [60]: max_rela(me_torch.numpy(), me_tf.numpy())
Out[60]: 5.12790049702284e-16
In [62]: tf.__version__
Out[62]: '2.13.1'
In [63]: torch.__version__
Out[63]: '2.1.1+cpu' 上面代码是直接从 ipython 中拷贝出来的,只把中间一些输入错误给删掉了,如有疑问可以在其他地方复现 ~ 测试分三部分:
这里可能误解我的意思了,我是想说,目前只有 pade 近似与泰勒多项式可以参考,我也想不出第三种算法,或者更快的算法,这是个数学问题,提升多少难度也做不到啊 ~~~ 🫣🫣🫣 所以,如果用 python 实现,可以参考 tensoflow/scipy,如果用 c++ 实现,可以参考 eigen,如果用 c++ 的泰勒多项式实现,可以参考 torch ~ 这里选择了 python 实现,因为 c++ 写起来太麻烦了 ~~~ 😂😂😂 另外,关于控制流目前不稳定的问题,我觉得还好,毕竟最后发布版本的时候总不能跟用户说这个控制流不能用吧 ~~~ 😂😂😂 @cxxly 请阅 ~ |
Update 20231214现在程序里面很多是兼顾静态图的地方,如 paddle.full ~ 如果单独调试动态图性能的话,刚才试了一下,能提升一倍吧: In [12]: %timeit me_paddle = matrix_exp(m_paddle)
533 µs ± 3.29 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each) |
好的,感谢补充测试数据。从当前结论上来看,pade近似和泰勒多项式算法自身性能和精度近似,主要性能diff在python与c++ bind overhead,这个个人认为可以接受,后续模型实际应用如果有较大差距,可以再下沉优化,成本也不高。 另外这个测试数据形状比较小,缺少GPU测试,需要再进一步验证以验证上述结论solid。 1)数据量较大情况(超过2000个元素) 2) GPU测试,可以用AI Studio环境,仅对比 tf与torch就行 。测试结论补充到设计文档。 至于控制流问题,只有部分场景(跨block微分)有些问题,可以实现一版,测试下静态图能否跑通。 我们还是首要追求工程质量,不放过每一个细节。如果可能延期,可以随时提出,会协助帮忙向活动产品运营同学申请。同时也会同步Review PR,如果上述结论没问题,会协助尽快合入,再次感谢。 |
aistudio 不能用 tf 和 torch ~~~ 🤣🤣🤣 稍后我在本地再测一下大数据量的情况,然后更新 rfc ~
赞!!! @cxxly PaddlePaddle/Paddle#59715 这个 pr 已经验证了,旧 ir 是可以跑通的,pir 研发那边说还没做,后面可以验证 ~~~ |
Update 20231215
这里简单摘抄: 性能:
结论:
分析主要原因:
精度:
结论:
另外,当输入增大时,精度误差同步增大。 这里与之前的测试主要区别:
另外,pytorch 的 gpu 版本不知道为啥装不上,也就没测试 ~~~ p.s. 从 48*48 增加到 8*32*32*32 测试的性能数据挺有意思的,tf 的 gpu 慢了 4 倍,paddle 的 gpu 慢了 10 倍 ~ torch 的 cpu 慢了 100 倍, paddle 的 cpu 慢了 50 倍,tf 的 cpu 慢了 10 倍 ~~~ @cxxly 请阅 ~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Update 20231218重新装了 torch 的 nightly version,补充测试数据: In [30]: torch.__version__
Out[30]: '2.3.0.dev20231217+cu121'
结论跟之前的一样:中小数据量,cpu 好于 gpu,大数据量 gpu 好于 cpu ~ 但是 torch 的 gpu 性能提升很小 ~ |
PR types
New features
PR changes
Docs
Description
【Hackathon 5th No.36】 为 Paddle 新增 matrix_exp API
参考 RFC: #674
做如下修改:
主要是因为这样相对简单稳妥 ~ 🤣🤣🤣
tensorflow、eigen、scipy 都是使用的 pade 近似,可以作为参考 ~
pytorch 是用的 2019 年较新发表论文的泰勒多项式算法,参考资料较少,底层算子也可能存在不兼容情况 ~
参考实现方式:PaddlePaddle/Paddle#59715
@luotao1 @cxxly @zade23
请评审 ~ 谢谢!