Python 计算~1m厄米矩阵的谱范数:`numpy.linalg.norm`太慢

Python 计算~1m厄米矩阵的谱范数:`numpy.linalg.norm`太慢,python,numpy,cython,linear-algebra,numba,Python,Numpy,Cython,Linear Algebra,Numba,我想计算N 8x8厄米矩阵的谱范数,其中N接近1E6。以这100万个随机复8x8矩阵为例: import numpy as np array = np.random.rand(8,8,1e6) + 1j*np.random.rand(8,8,1e6) 目前,使用numpy.linalg.norm,我花了将近10秒的时间: np.linalg.norm(array, ord=2, axis=(0,1)) 我尝试使用下面的Cython代码,但这只给了我微不足道的性能改进: import nu

我想计算N 8x8厄米矩阵的谱范数,其中N接近1E6。以这100万个随机复8x8矩阵为例:

import numpy as np

array = np.random.rand(8,8,1e6)  + 1j*np.random.rand(8,8,1e6)
目前,使用
numpy.linalg.norm
,我花了将近10秒的时间:

np.linalg.norm(array, ord=2, axis=(0,1))
我尝试使用下面的Cython代码,但这只给了我微不足道的性能改进:

import numpy as np
cimport numpy as np
cimport cython

np.import_array()

DTYPE = np.complex64

@cython.boundscheck(False)
@cython.wraparound(False)
def function(np.ndarray[np.complex64_t, ndim=3] Array):
    assert Array.dtype == DTYPE
    cdef int shape0 = Array.shape[2]
    cdef np.ndarray[np.float32_t, ndim=1] normarray = np.zeros(shape0, dtype=np.float32)
    normarray = np.linalg.norm(Array, ord=2, axis=(0, 1))
    return normarray
我还尝试了numba和其他一些scipy函数(例如
scipy.linalg.svdvals
)来计算这些矩阵的奇异值。一切还是太慢了

难道不可能让它更快吗?numpy是否已经优化到使用Cython或numba无法获得速度增益的程度?还是我的代码效率很低,而且我在做一些根本错误的事情

我注意到,在进行计算时,只有两个CPU内核的利用率为100%。考虑到这一点,我查看了前面的这些问题:

  • (没有帮助)

和其他几个,但不幸的是我仍然没有一个解决方案


我考虑将我的数组分割成更小的块,并并行处理(可能在GPU上使用CUDA)。在numpy/Python中有什么方法可以做到这一点吗?我还不知道我的代码中的瓶颈在哪里,也就是说,它是CPU还是内存限制,或者可能是其他什么。

深入研究
np.linalg.norm
的代码,我推断,对于这些参数,它是在N维上寻找矩阵奇异值的最大值

首先生成一个小样本数组。使
N
成为第一个维度,以消除
rollaxis
操作:

In [268]: N=10; A1 = np.random.rand(N,8,8)+1j*np.random.rand(N,8,8)

In [269]: np.linalg.norm(A1,ord=2,axis=(1,2))
Out[269]: 
array([ 5.87718306,  5.54662999,  6.15018125,  5.869058  ,  5.80882818,
        5.86060462,  6.04997992,  5.85681085,  5.71243196,  5.58533323])
In [270]: np.amax(np.linalg.svd(A1,compute_uv=0),axis=-1)
Out[270]: 
array([ 5.87718306,  5.54662999,  6.15018125,  5.869058  ,  5.80882818,
        5.86060462,  6.04997992,  5.85681085,  5.71243196,  5.58533323])
等效操作:

In [268]: N=10; A1 = np.random.rand(N,8,8)+1j*np.random.rand(N,8,8)

In [269]: np.linalg.norm(A1,ord=2,axis=(1,2))
Out[269]: 
array([ 5.87718306,  5.54662999,  6.15018125,  5.869058  ,  5.80882818,
        5.86060462,  6.04997992,  5.85681085,  5.71243196,  5.58533323])
In [270]: np.amax(np.linalg.svd(A1,compute_uv=0),axis=-1)
Out[270]: 
array([ 5.87718306,  5.54662999,  6.15018125,  5.869058  ,  5.80882818,
        5.86060462,  6.04997992,  5.85681085,  5.71243196,  5.58533323])
相同的值,相同的时间:

In [271]: timeit np.linalg.norm(A1,ord=2,axis=(1,2))
1000 loops, best of 3: 398 µs per loop
In [272]: timeit np.amax(np.linalg.svd(A1,compute_uv=0),axis=-1)
1000 loops, best of 3: 389 µs per loop
大部分时间都花在
svd
上,它生成一个(N,8)数组:

因此,如果您想加速
标准
,您必须进一步研究如何加速此
svd
svd
使用
np.linalg.\u umath\u linalg
函数-这是一个
。因此
文件-编译

c
代码位于

看起来这是你能得到的最快的。没有Python级别的循环。任何循环都存在于
c
代码中,或者它调用的
lapack
函数中。

np.linalg.norm(A,ord=2)
通过使用奇异值求最大奇异值来计算谱范数。但是,由于8x8子矩阵是厄米特矩阵,因此其最大奇异值将等于其绝对特征值的最大值():

厄米矩阵上的特征分解比奇异值分解快得多:

In [1]: %%timeit A = random_symmetric(N, k)
np.linalg.norm(A, ord=2, axis=(1, 2))
   ....: 
1 loops, best of 3: 1.54 s per loop

In [2]: %%timeit A = random_symmetric(N, k)
np.abs(np.linalg.eigvalsh(A)).max(1)
   ....: 
1 loops, best of 3: 757 ms per loop

如果您只是简单地使用Cython或numba来调用numpy函数,那么您将看不到Cython或numba对性能的任何好处。Cython和numba不了解numpy的内部工作原理,也无法对numpy函数进行任何优化-您必须在数组上编写自己的低级循环来计算范数。此计算与
N
成线性比例。简单地生成数组所需的时间是范数所需时间的1/4。在我比较旧的机器上,1e6太大,甚至无法生成阵列。所以速度问题的很大一部分是数据的剪切大小。谢谢你的回答,我想我无法打败numpy,除非在一个非常低级的循环中重写所有内容。谢谢,我会看看这个。我仍然想知道为什么Numpy不使用多个cpu核心。我假设,对于这个特定的任务,Numpy就是无法做到这一点?执行svd时,
np.linalg.svd
是否使用多个内核取决于您链接到的LAPACK共享库。例如,如果您链接到OpenBLAS,那么您可能会看到它使用多个核心。然而,并行化将在每个8x8子矩阵内进行,而不是在N个矩阵的向量上进行,因此不太可能在性能方面产生巨大差异。厄米矩阵的谱范数是特征值绝对值的最大值,矩阵是否正定。这确实几乎是所需计算时间的一半,谢谢。与此无关:为什么要编辑原始问题?我编辑你的问题的主要原因是想让标题和标签更清楚地反映问题的本质,而不是你试图解决的问题。因此,对于寻求类似问题解决方案的其他人来说,Q&A在理想情况下应该是有用的资源。如果我在谷歌上搜索一种快速的方法来计算大量矩阵的谱范数,那么我将有更好的机会找到当前标题下的这个问题,而不是以前提到Cython和Numba的问题。不管怎样,很高兴我能帮上忙。