首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >加速cython代码

加速cython代码
EN

Stack Overflow用户
提问于 2016-09-28 11:15:25
回答 2查看 1K关注 0票数 3

我有在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的功能?

代码语言:javascript
复制
 @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
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-28 16:04:59

您正在尝试通过跳入池的深处使用cython。你应该从一些小的东西开始,比如一些粗俗的例子。或者甚至尝试改进np.diag

代码语言:javascript
复制
    i = 0
    C_i = np.zeros((m, m), dtype=float)
    for j in range(m):
        C_i[j,j]=C[i,j]

v。

代码语言:javascript
复制
    C_i = diag(C[i,:])

您能提高这个简单表达式的速度吗?diag没有编译,但是它确实执行有效的索引分配。

代码语言:javascript
复制
 res[:n-k].flat[i::n+1] = v

但是cython的真正问题是这个表达式:

代码语言:javascript
复制
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表达式转换为简单的迭代代码,而无需调用dotinv之类的numpy函数。

===================

我相信以下各点是相等的:

代码语言:javascript
复制
np.dot(C_i,V.T)
C[i,:,None]*V.T

在:

代码语言:javascript
复制
np.dot(C_i,train[i,:].T) 

如果train是2d,那么train[i,:]是1d,而.T什么也不做。

代码语言:javascript
复制
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

======================

此外,这些计算可以移到循环之外,表达式类似于(未测试)

代码语言:javascript
复制
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。那我们就有了

代码语言:javascript
复制
for i...
    I = np.linalg.inv(VCV1[i,...])  
    U[:,i] = np.dot(I+ lambda_u), VCV2[i,])

甚至是

代码语言:javascript
复制
for i...
     I[...i] = np.linalg.inv(...) # if inv can't be vectorized
U = np.einsum(..., I+lambda_u, VCV2)

这是一个粗略的草图,细节需要制定出来。

票数 3
EN

Stack Overflow用户

发布于 2016-09-28 12:09:49

想到的第一件事是,您没有输入函数参数并指定数据类型和维数,如下所示:

代码语言:javascript
复制
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这样做,尽管您使用的是切片:

代码语言:javascript
复制
cdef np.ndarray[np.float64, ndim=2] U = np.empty((hidden_dim,user_size), np.float64)

接下来,您将重新定义C_i,这是一个大的二维数组,每次外部循环的迭代都是这样。另外,您还没有为它提供任何类型信息,如果Cython要提供任何加速比,这是必须的。要解决这个问题:

代码语言:javascript
复制
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数组时应该做些什么,以及不应该做什么,并了解通过这些简单的更改可以得到多大的加速。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39745881

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档