使用稀疏矩阵优化大型numpy数组乘法

时间:2018-05-03 19:06:10

标签: python numpy matrix sparse-matrix

我正在对以下表格进行统计计算:

enter image description here

,其中

  • d (数据)是一个大小为[i = 30,100x100]的矩阵
  • m (型号)是一个大小为[i = 30,100x100]的矩阵
  • C -1 (协方差)是100 2 乘100 2 对称矩阵

d C -1 是常量,除 C -1 是对称的。 m 更改,但始终稀疏。计算的输出只是一个浮点数。

我需要在蒙特卡罗模拟中多次执行此计算,因此速度至关重要。使用 m 的稀疏数组乘法技术可以大大加快天然矩阵点积的速度。但是,下面慢速函数的每次迭代仍然需要大约0.1秒才能运行。绝大多数时间(> 98%)用于矩阵乘法,而不是模型生成函数(generate_model)。如果可能的话,我想加快一个数量级。

代码和输出粘贴在下面。

工作的事情包括:

  • 升级到英特尔MKL线性代数例程(加速几个百分点,非常小)
  • 使用numpy.linalg.multi_dot
  • 利用 C -1 这一事实是对称的(这在原则上不起作用,见this mathoverflow question

有点的工作包括:

  • 预先计算 C -1 d ,加速度提高约40%

如何加快此代码的速度?我们非常欢迎依赖cythonnumba等软件包的解决方案以及"标准" scipy / numpy解决方案。提前谢谢!

from __future__ import division
import numpy as np
import scipy.sparse
import sys 
import timeit

def generate_model(n, size, hw = 8): 
    #model for the data--squares at random locations
    output = np.zeros((n, size, size))
    for i in range(n):
        randx = np.random.randint(hw, size-hw)
        randy = np.random.randint(hw, size-hw)
        output[i,(randx-hw):(randx+hw), (randy-hw):(randy+hw)]=np.random.random((hw*2, hw*2))
    return output

def slow_function(datacube, invcovmatrix, size):
    model = generate_model(30, size) 
    output = 0 
    for i in range(model.shape[0]):
        data = datacube[i,:,:].flatten()
        mu = model[i,:,:].flatten()
        sparsemu = scipy.sparse.csr_matrix(mu)
        output += -0.5* (
                  np.float(-2.0*sparsemu.dot(invcovmatrix).dot(data)) +
                  np.float(sparsemu.dot(sparsemu.dot(invcovmatrix).T))
                  )   
    return output

def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

if __name__ == "__main__":
    size = 100 
    invcovmat    = np.random.random((size**2, size**2))
    #make symmetric for consistency
    invcovmat    = (invcovmat+invcovmat.T)/2
    datacube     = np.random.random((30, size, size))
    #TIMING
    wrapped = wrapper(slow_function, datacube, invcovmat, size)
    times = []
    for i in range(20):
        print i
        times.append(timeit.timeit(wrapped, number = 1)) 
    times.sort()
    print '\n', np.mean(times[0:3]), ' s/iteration; best of 3'

输出:

0.10408163070678711  s/iteration; best of 3

0 个答案:

没有答案