将python程序转换为cython以加速的问题

时间:2018-04-26 17:49:07

标签: python arrays algorithm cython sparse-matrix

我正在尝试在Cython中编写一个快速算法。该算法相当简单,并在此处进行了描述(https://arxiv.org/pdf/1411.4357.pdf - 第17页定理8之前的段落)。这个想法相对简单:假设数据是稀疏的,它可以作为A形式的输入给出具有n行和d列的矩阵h。现在为了利用稀疏性,我们需要两个函数s,它们随机均匀地将n行映射到s桶中(这是算法的参数)和符号函数(i,j,A_ij),它只是±1概率相等。然后,对于每个三元组(h(i),j, s(h(i))*A_ij),算法必须输出 import numpy as np import numpy.random as npr from scipy.sparse import coo_matrix def countSketch_faster(input_matrix, sketch_size, seed=None): ''' input_matrix: sparse - coo_matrix type sketch_size: int seed=None : random seed ''' observed_rows = set([]) sign_hashes = [] hash_table = {} sketched_data = [] hashed_rows = [] for row_id, col_id, Aij in zip(input_matrix.row, input_matrix.col, input_matrix.data): if row_id in observed_rows: sketched_data.append(hash_table[row_id][1]*Aij) hashed_rows.append(hash_row_val) else: hash_row_val, sign_val = np.random.choice(sketch_size), np.random.choice([-1.0,1.0]) hash_table[row_id] = (hash_row_val, sign_val) #hash-sign pair sketched_data.append(sign_val*Aij) hashed_rows.append(hash_row_val) observed_rows.add(row_id) hashed_rows = np.asarray(hashed_rows) sketched_data = np.asarray(sketched_data) row_hashes = np.asarray(row_hashes) S = coo_matrix((sketched_data, (hashed_rows, input_matrix.col))).tocsr() return S 并将这些作为数组返回,以作为另一个稀疏的输入。

问题在于我无法获得加速。如上面的参考文献所示,这应该非常快并且优于实现S并乘以A的矩阵乘法。

Python方法(大约11ms):

cdef

转换为Cython: 分析带注释的输出突出显示大多数蟒蛇重的行是那些调用numpy的行。我尝试dtype所有变量,并为任何numpy调用指定zip。此外,我已删除%%cython --annotate cimport cython import numpy.random as npr import numpy as np from scipy.sparse import coo_matrix def countSketch_cython(input_matrix, sketch_size, seed=None): ''' input_matrix: sparse - coo_matrix type sketch_size: int seed=None : random seed ''' cdef Py_ssize_t idx cdef int row_id cdef float data_id cdef float sketch_data_val cdef int hash_row_value cdef float sign_value hash_table = {} sketched_data = np.zeros_like(input_matrix.data,dtype=np.float64) hashed_rows = np.zeros_like(sketched_data,dtype=np.float64) observed_rows = set([]) for idx in range(input_matrix.row.shape[0]): row_id = input_matrix.row[idx] data_id = input_matrix.data[idx] if row_id in observed_rows: hash_row_value = hash_table[row_id][0] sign_value = hash_table[row_id][1] sketched_data[row_id] = sign_value*data_id hashed_rows[idx] = hash_row_value else: hash_row_val = np.random.randint(low=0,high=sketch_size+1) sign_val = np.random.choice([-1.0,1.0]) hash_table[row_id] = (hash_row_val, sign_val) #hash-sign pair sketched_data[idx] = sign_val*data_id hashed_rows[idx] = hash_row_value observed_rows.add(row_id) S = coo_matrix((sketched_data, (hashed_rows, input_matrix.col))) return S` 命令并尝试使循环更简单,更像C。所有这些实际上都产生了不利影响,而且运行速度非常慢,我不确定为什么。这似乎是一个非常简单的算法,所以如果有人可以帮助我将运行时间缩小到非常小的程度,我将非常感激。

np.random

更新:我已经设法通过删除一些较慢的行来加速代码。这些是对#@cython.boundscheck(False) # these don't contribute much in this 的调用,其中C随机数生成器更快,给出输入中的长度,因此不需要计算,也不需要在返回之前转换为稀疏矩阵(为了本实验的目的)我感兴趣的是转换的速度有多快,而不是下游使用的转换细节。

%% cython - 注释     cimport cython     将numpy.random导入为npr     导入numpy为np     来自libc.stdlib cimport rand

    cdef Py_ssize_t idx
    cdef int row_id
    cdef float data_id
    cdef float sketch_data_val
    cdef int hash_row_value
    cdef float sign_value
    cdef int arr_lengths = input_matrix_length

    # These two lines are still annotated most boldly
    cdef double[:,:] sketched_data = 
np.zeros((arr_lengths,1),dtype=np.float64)
    cdef double[:,:] hashed_rows = np.zeros((arr_lengths,1))

    hash_table = {}

    observed_rows = set([])

    for idx in range(arr_lengths):
        row_id = input_matrix.row[idx]
        data_id = input_matrix.data[idx]

        if row_id in observed_rows:
            hash_row_value = hash_table[row_id][0]
            sign_value = hash_table[row_id][1]
            sketched_data[row_id] = sign_value*data_id
            hashed_rows[idx] = hash_row_value
        else:
            hash_row_val = rand()%(sketch_size) 
            #np.random.randint(low=0,high=sketch_size+1)
            sign_val = 2*rand()%(2) - 1
            #np.random.choice([-1.0,1.0])
            hash_table[row_id] = (hash_row_val, sign_val) #hash-sign pair
            sketched_data[idx] = sign_val*data_id
            hashed_rows[idx] = hash_row_value
            observed_rows.add(row_id)


    #S = coo_matrix((sketched_data, (hashed_rows, input_matrix.col)), dtype=np.float64) 
return hashed_rows, sketched_data

示例     #@cython.wraparound(假)
     def countSketch(input_matrix,input_matrix_length,sketch_size,seed = None):          “””          input_matrix:sparse - coo_matrix类型          input_matrix_length - 输入矩阵中的行数          sketch_size:int          种子=无:随机种子         '''

A = scipy.sparse.random(1000, 50, density=0.1)

在随机稀疏矩阵508 µs ± 17.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)上,现在使用%timeit countSketch(A, A.shape[0],100)实现{{1}}。我想应该还有收获,因为我不知道我是否以最佳方式设置了数组。

0 个答案:

没有答案