numpy 矩阵技巧 - 逆矩阵乘法之和

12 投票
1 回答
1955 浏览
提问于 2025-04-17 15:18

我正在尝试做以下事情,并重复直到结果稳定:

这里每个 Xi 是一个 n x p 的矩阵,而总共有 r 个这样的矩阵,组成一个 r x n x p 的数组,叫做 samplesU 是一个 n x n 的矩阵,V 是一个 p x p 的矩阵。(我在计算一个 矩阵正态分布 的最大似然估计。) 这些矩阵的大小都可能比较大;我预计 r = 200n = 1000p = 1000

我现在的代码是:

V = np.einsum('aji,jk,akl->il', samples, np.linalg.inv(U) / (r*n), samples)
U = np.einsum('aij,jk,alk->il', samples, np.linalg.inv(V) / (r*p), samples)

这个方法还不错,但实际上你不应该去计算逆矩阵并用它来乘其他东西。如果能利用 UV 是对称且正定的特性,那就更好了。 我希望在迭代中能直接计算 UV 的 Cholesky 分解,但因为有求和的关系,我不知道该怎么做。

我可以通过做类似下面的事情来避免计算逆矩阵:

V = sum(np.dot(x.T, scipy.linalg.solve(A, x)) for x in samples)

(或者其他利用正定特性的方式),但这样就会出现 Python 循环,这样会让 numpy 的性能下降。

我也想过重新调整 samples 的形状,这样我就可以用 solve 为每个 x 计算 A^-1 x,而不需要 Python 循环,但这样会产生一个很大的辅助数组,浪费内存。

有没有什么线性代数或 numpy 的技巧,可以让我同时满足这三点:不显式计算逆矩阵、不使用 Python 循环、也不产生大的辅助数组?还是说我最好的办法是把带有 Python 循环的部分用更快的语言实现,然后调用它?(直接移植到 Cython 可能有帮助,但仍然会涉及很多 Python 方法调用;不过也许直接实现相关的 blas/lapack 例程不会太麻烦。)

(实际上,我最后并不需要 UV 矩阵,只需要它们的行列式,或者说它们的 Kronecker 积的行列式。所以如果有人有聪明的主意,可以减少工作量并得到行列式,那就太感谢了。)

1 个回答

8

在有人提出更好的答案之前,如果我是你,我就让那些小精灵哭吧...

r, n, p = 200, 400, 400

X = np.random.rand(r, n, p)
U = np.random.rand(n, n)

In [2]: %timeit np.sum(np.dot(x.T, np.linalg.solve(U, x)) for x in X)
1 loops, best of 3: 9.43 s per loop

In [3]: %timeit np.dot(X[0].T, np.linalg.solve(U, X[0]))
10 loops, best of 3: 45.2 ms per loop

这里说的是,使用Python写循环来把所有结果加起来,花费的时间比解决每一个系统所需的时间多出390毫秒,这个时间是解决200个系统所需时间的200倍。如果循环和加总的过程是免费的,你也只能提高不到5%的效率。可能调用Python函数也会有一些额外的时间开销,但无论你用什么编程语言,解决方程的实际时间都还是会占大头。

撰写回答