我正在尝试在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}}。我想应该还有收获,因为我不知道我是否以最佳方式设置了数组。