Performance 高效的柱状相关系数计算
原始问题 我将大小为n的p行与大小为n×m的矩阵O的每一列相关联。我编写了以下代码:Performance 高效的柱状相关系数计算,performance,numpy,correlation,pearson-correlation,Performance,Numpy,Correlation,Pearson Correlation,原始问题 我将大小为n的p行与大小为n×m的矩阵O的每一列相关联。我编写了以下代码: 将numpy导入为np def ColumnWiseCorrcoef(O,P): n=P.size DO=O-(np.和(O,0)/np.双(n)) DP=P-(np.和(P)/np.双(n)) 返回np.dot(DP,DO)/np.sqrt(np.sum(DO**2,0)*np.sum(DP**2)) 它比单纯的方法更有效: def columnwiseCorcoefnaive(O,P): 返回np.cor
将numpy导入为np
def ColumnWiseCorrcoef(O,P):
n=P.size
DO=O-(np.和(O,0)/np.双(n))
DP=P-(np.和(P)/np.双(n))
返回np.dot(DP,DO)/np.sqrt(np.sum(DO**2,0)*np.sum(DP**2))
它比单纯的方法更有效:
def columnwiseCorcoefnaive(O,P):
返回np.corrcof(P,O.T)[0,1:O[0].size+1]
以下是我在Intel core上使用numpy-1.7.1-MKL的计时:
O=np.重塑(np.随机.兰德(100000),(1000100))
P=np.rand.rand(1000)
%timeit-n 1000 A=列式相关系数(O,P)
1000圈,最好3圈:每圈787美元
%timeit-n 1000 B=列型相关系数(O,P)
1000个回路,最佳3个:每个回路2.96毫秒
现在的问题是:你能为这个问题推荐一个更快的代码版本吗?再挤出20%就好了
2017年5月更新
过了一段时间,我回到这个问题上来,重新运行并扩展了任务和测试
所有的脚本和计时都可以在上找到。我们可以介绍
np.einsum
;但是,您的差异可能取决于您的编译以及它是否使用SSE2。替换求和操作的额外einsum
调用可能看起来无关,但是numpy ufuncs直到numpy 1.8才使用SSE2,而einsum
使用SSE2,我们可以避免一些if
语句
在带有intel mkl blas的opteron内核上运行此命令时,我会得到一个奇怪的结果,因为我预计dot
调用会占用大部分时间
def newColumnWiseCorrcoef(O,P):
n=P.size
DO=O-(名词性宾语('ij->j',O)/名词性双宾语(n))
P-=(np.einsum('i->',P)/np.double(n))
tmp=np.einsum('ij,ij->j',DO,DO)
tmp*=np.einsum('i,i->',P,P)#Dot或vdot实际上变化不大。
返回np.dot(P,DO)/np.sqrt(tmp)
O=np.重塑(np.随机.兰德(100000),(1000100))
P=np.rand.rand(1000)
old=列式相关系数(O,P)
new=newColumnWiseCorrcoef(O,P)
np.allclose(旧、新)
真的
%timeit ColumnWiseCorrcoef(O,P)
100圈,最佳3圈:每圈1.52ms
%timeit newColumnWiseCorrcoef(O,P)
1000个回路,最好3个:每个回路518us
再次在带有“英特尔mkl”的英特尔系统上运行此功能,我得到了更合理/更期望的结果:
%timeit ColumnWiseCorrcoef(O,P)
1000个回路,最好为3个:每个回路524µs
%timeit newColumnWiseCorrcoef(O,P)
1000个回路,最好为3个:每个回路354µs
再次在intel机器上使用更大的功能:
O=np.random.rand(1E5,1E3)
P=np.random.rand(1E5)
%timeit ColumnWiseCorrcoef(O,P)
1圈,最好3圈:每圈1.33秒
%timeit newColumnWiseCorrcoef(O,P)
1个回路,最佳3个:每个回路791毫秒
可以更改O
和P
吗?此外,这些方法严重依赖于链接到numpy的BLAS库,如果您还没有,我建议您使用某种优化的BLAS。@Ophion,P
可以更改,O
不能更改。我使用的是从中链接到英特尔MKL的numpy。现在无法测试它,但如果您尝试np.einsum('ij,ij->j',DO,DO)
而不是np.sum(DO**2,0)
,并且np.dot(DP,DP)
而不是np.sum(DP****.2)
,则是tmp=。。。;tmp*=…
应该节省时间吗?我见过其他人也这么做,我不认为这比tmp=…*代码>。我发现这个解决方案非常有用,我想知道是否有一种方法可以扩展它来计算回归残差,而不会在速度方面牺牲太多。