Python 如何有效地计算scipy.csr稀疏矩阵中非零索引的成对交集?

Python 如何有效地计算scipy.csr稀疏矩阵中非零索引的成对交集?,python,numpy,pytorch,sparse-matrix,Python,Numpy,Pytorch,Sparse Matrix,我有一个scipy.sparse.csr矩阵X,它是nxp。对于X中的每一行,我想计算非零元素索引与X中每一行的交点,并将它们存储在新的张量中,甚至可能存储在字典中。例如,X是: X = [ [0., 1.5, 4.7], [4., 0., 0.], [0., 0., 2.6] ] 我希望输出是 intersect = [ [[1,2], [], [2]], [[], [0], []], [[2], [], [2]] ] intersect[i,j]是表示X的第i行和第j行的非零元素的索引

我有一个scipy.sparse.csr矩阵X,它是nxp。对于X中的每一行,我想计算非零元素索引与X中每一行的交点,并将它们存储在新的张量中,甚至可能存储在字典中。例如,X是:

X = [
[0., 1.5, 4.7],
[4., 0., 0.],
[0., 0., 2.6]
]
我希望输出是

intersect = 
[
[[1,2], [], [2]],
[[], [0], []],
[[2], [], [2]]
]
intersect[i,j]是表示X的第i行和第j行的非零元素的索引的交点的数据列,即X[i],X[j]

目前我做这件事的方式是通过循环,我想把它矢量化,因为它会更快,而且计算是并行完成的

# current code
n = X.shape[0]
intersection_dict = {}
for i in range(n):
    for j in range(n):
        indices = np.intersect1d(X[i].indices, X[j].indices)
        intersection_dict[(i,j)] = indices
我的n相当大,所以在n^2上循环非常差。我只是想不出一个方法来矢量化这个操作。有人对如何解决这个问题有什么想法吗

编辑: 很明显,我应该解释我试图解决的问题,所以它就在这里

我正在解决一个优化问题,有一个方程
W=X诊断(θ)X'
。我想在更新θ的条目直到收敛时快速找到W。此外,我正在使用pytorch更新参数,其中稀疏操作不像scipy中那样广泛

其中:

X : n x p sparse data matrix (n documents, p features)
theta : p x 1 parameter vector I want to learn and will be updating
X' : p x n transpose of sparse data matrix

note p >> n
我想到了两种快速解决这个问题的方法

  • 缓存的稀疏外积(请参阅)
  • W_ij=X_i*theta*X_j
    (X的i行、θ和j行的元素乘积。由于
    X_i,X_j
    是稀疏的,我在想,如果我取非零指数的交集,那么我可以做
    X_i[交集指数]的简单密集元素乘积(pytorch不支持稀疏元素乘积)*θ[交叉点索引]X_j[交叉点索引]
  • 我希望尽可能多地向量化这些计算,而不是循环,因为我的n通常是数千,p是1100万


    我正在尝试方法2而不是方法1,以解决Pytorch中缺少稀疏支持的问题。主要是在更新θ的条目时,我不希望执行稀疏密集或稀疏稀疏操作。我希望执行密集密集操作。

    第一个简单的解决方案是注意输出矩阵是对称的:

    n = X.shape[0]
    intersection_dict = {}
    for i in range(n):
        for j in range(i,n): #note the edit here
            indices = np.intersect1d(X[i].indices, X[j].indices)
            intersection_dict[(i,j)] = indices
    

    这将使您的计算量减少不到2倍一个简单的解决方案是注意输出矩阵是对称的:

    n = X.shape[0]
    intersection_dict = {}
    for i in range(n):
        for j in range(i,n): #note the edit here
            indices = np.intersect1d(X[i].indices, X[j].indices)
            intersection_dict[(i,j)] = indices
    

    这将使您的计算量减少不到2倍

    您正在查看的优化需要存储不同的
    nxn
    矩阵。如果您确实想尝试,我可能会使用scipy的C扩展中内置于稀疏矩阵中的所有功能

    import numpy as np
    from scipy import sparse
    
    arr = sparse.random(100,10000, format="csr", density=0.01)
    
    xxt = arr @ arr.T
    p_comps = [arr[:, i] @ arr.T[i, :] for i in range(arr.shape[1])]
    
    def calc_weights(xxt, thetas, p_comps):
        xxt = xxt.copy()
        xxt.data = np.zeros(xxt.data.shape, dtype=xxt.dtype)
        for i, t in enumerate(thetas):
            xxt += (p_comps[i] * t)
        return xxt
    
    W = calc_weights(xxt, np.ones(10000), p_comps)
    
    >>>(xxt.A == W.A).all()
    True
    

    这不太可能在python中很好地实现。在C中实现这一点,或者使用嵌套循环编写一些对元素进行操作的东西,并且可以使用numba编译JIT。您正在查看的优化需要存储
    p
    不同的
    nxn
    矩阵。如果您真的想试试,我可能会使用scipy的C扩展中稀疏矩阵内置的所有功能

    import numpy as np
    from scipy import sparse
    
    arr = sparse.random(100,10000, format="csr", density=0.01)
    
    xxt = arr @ arr.T
    p_comps = [arr[:, i] @ arr.T[i, :] for i in range(arr.shape[1])]
    
    def calc_weights(xxt, thetas, p_comps):
        xxt = xxt.copy()
        xxt.data = np.zeros(xxt.data.shape, dtype=xxt.dtype)
        for i, t in enumerate(thetas):
            xxt += (p_comps[i] * t)
        return xxt
    
    W = calc_weights(xxt, np.ones(10000), p_comps)
    
    >>>(xxt.A == W.A).all()
    True
    

    这不太可能在python中很好地实现。在C中实现这一点,或者使用嵌套循环编写一些对元素进行操作的东西,并且可以使用numba编译JIT。如果您解释您试图解决的实际问题,而不是寻求帮助,可能会更好实现一些看起来根本不是什么好主意的东西。因为你的
    交集
    由不同长度(0,1,2)的列表组成,我对“矢量化”没有多大希望操作,即使使用常规的numpy数组。您可以通过使用
    indptr
    “row”属性直接在稀疏矩阵上迭代,尽管
    lil
    格式的数据和行属性可能更容易可视化。这是tf idf问题,对吗?您能解决较小的类似问题吗?点击次数超过100次。可能是这样的如果你解释你试图解决的实际问题,而不是寻求帮助来实现一个看起来根本不是很好的主意,那就更好了。因为你的
    intersect
    由不同长度(0,1,2)的列表组成,我对“矢量化”没有多大希望操作,即使使用常规的numpy数组。您可以通过使用
    indptr
    “row”属性直接在稀疏矩阵上迭代,尽管
    lil
    格式的数据和行属性可能更容易可视化。这是tf idf问题,对吗?您能解决较小的类似问题吗?点击次数超过100次。是的,因此存储ter products是解决此问题的典型方法唯一的问题是,我的p非常大,存储稀疏的outerproduct缓存对我来说不太好,因为pytorch稀疏支持不是最好的。我最终使用了一种计算量较小的启发式方法,谢谢。是的,所以存储外部产品是解决此问题的典型方法问题唯一的问题是,我的p非常大,存储稀疏outerproduct缓存对我来说不太合适,因为pytorch稀疏支持不是最好的。我最终使用了一种计算量较小的启发式方法,谢谢。