我有在python中工作的代码,希望使用cython来加快计算速度。我复制的函数位于一个.pyx文件中,并从我的python代码中调用。V,C,train,I_k是二维numpy数组,而lambda_u,用户,隐藏是ints.我没有使用C或cython的经验。使代码更快的有效方法是什么。使用cython -a编译代码向我展示了代码是有缺陷的,但如何改进它。在for i in prange (user_size, nogil=True):中使用Constructing Python slice object not allowed without gil结果。
如何修改代码以获取cython的功能?
@cython.boundscheck(False)
@cython.wraparound(False)
def u_update(V, C, train, I_k, lambda_u, user, hidden):
cdef int user_size = user
cdef int hidden_dim = hidden
cdef np.ndarray U = np.empty((hidden_dim,user_size), float)
cdef int m = C.shape[1]
for i in range(user_size):
C_i = np.zeros((m, m), dtype=float)
for j in range(m):
C_i[j,j]=C[i,j]
U[:,i] = np.dot(np.linalg.inv(np.dot(V, np.dot(C_i,V.T)) + lambda_u*I_k), np.dot(V, np.dot(C_i,train[i,:].T)))
return U发布于 2016-09-28 16:04:59
您正在尝试通过跳入池的深处使用cython。你应该从一些小的东西开始,比如一些粗俗的例子。或者甚至尝试改进np.diag。
i = 0
C_i = np.zeros((m, m), dtype=float)
for j in range(m):
C_i[j,j]=C[i,j]v。
C_i = diag(C[i,:])您能提高这个简单表达式的速度吗?diag没有编译,但是它确实执行有效的索引分配。
res[:n-k].flat[i::n+1] = v但是cython的真正问题是这个表达式:
U[:,i] = np.dot(np.linalg.inv(np.dot(V, np.dot(C_i,V.T)) + lambda_u*I_k), np.dot(V, np.dot(C_i,train[i,:].T)))np.dot是编译的。cython不会将其转换为c代码,也不会将所有5 dots合并成一个表达式。它也不会触及inv。因此,充其量,cython将加快迭代包装程序的速度,但它仍然会调用这个Python表达式的m时间。
我猜这个表达式是可以被清除的。将内部dots替换为einsum可能会消除对C_i的需求。inv可能会使整个事情变得“矢量化”困难。但我得多研究一下。
但是,如果您想继续使用cython路由,则需要将该U表达式转换为简单的迭代代码,而无需调用dot和inv之类的numpy函数。
===================
我相信以下各点是相等的:
np.dot(C_i,V.T)
C[i,:,None]*V.T在:
np.dot(C_i,train[i,:].T) 如果train是2d,那么train[i,:]是1d,而.T什么也不做。
In [289]: np.dot(np.diag([1,2,3]),np.arange(3))
Out[289]: array([0, 2, 6])
In [290]: np.array([1,2,3])*np.arange(3)
Out[290]: array([0, 2, 6])如果我说得对,你不需要C_i。
======================
此外,这些计算可以移到循环之外,表达式类似于(未测试)
CV1 = C[:,:,None]*V.T # a 3d array
CV2 = C * train.T
for i in range(user_size):
U[:,i] = np.dot(np.linalg.inv(np.dot(V, CV1[i,...]) + lambda_u*I_k), np.dot(V, CV2[i,...]))进一步的步骤是将两个np.dot(V,CV...)移出循环。这可能需要np.matmul (@)或np.einsum。那我们就有了
for i...
I = np.linalg.inv(VCV1[i,...])
U[:,i] = np.dot(I+ lambda_u), VCV2[i,])甚至是
for i...
I[...i] = np.linalg.inv(...) # if inv can't be vectorized
U = np.einsum(..., I+lambda_u, VCV2)这是一个粗略的草图,细节需要制定出来。
发布于 2016-09-28 12:09:49
想到的第一件事是,您没有输入函数参数并指定数据类型和维数,如下所示:
def u_update(np.ndarray[np.float64, ndim=2]V, np.ndarray[np.float64, ndim=2]\
C, np.ndarray[np.float64, ndim=2] train, np.ndarray[np.float64, ndim=2] \
I_k, int lambda_u, int user, int hidden) :这将大大加快用2个索引索引的速度,就像在内部循环中一样。
最好也对数组U这样做,尽管您使用的是切片:
cdef np.ndarray[np.float64, ndim=2] U = np.empty((hidden_dim,user_size), np.float64)接下来,您将重新定义C_i,这是一个大的二维数组,每次外部循环的迭代都是这样。另外,您还没有为它提供任何类型信息,如果Cython要提供任何加速比,这是必须的。要解决这个问题:
cdef np.ndarray[np.float64, ndim=2] C_i = np.zeros((m, m), dtype=np.float64)
for i in range(user_size):
C_i.fill(0)在这里,我们已经定义了它一次(使用类型信息),并通过填充零来重用内存,而不是每次调用np.zeros()来创建一个新的数组。
此外,您可能只希望在完成调试后才关闭边界检查。
如果需要在U[:,i]=...步骤中加速,可以考虑使用Cython编写另一个函数来使用循环执行这些操作。
一定要阅读这个教程,它应该让您了解在Cython中使用Numpy数组时应该做些什么,以及不应该做什么,并了解通过这些简单的更改可以得到多大的加速。
https://stackoverflow.com/questions/39745881
复制相似问题