簡體   English   中英

如何將產生稀疏矩陣的代碼矢量化?

[英]How do I vectorize this code that produces a scipy sparse matrix?

讓我首先解釋一下我想做什么。 我正在嘗試基於存儲在mxn稀疏矩陣Xm包(每個都有n功能)構建推薦系統。 為此,我嘗試運行kNN以獲取軟件包的k最接近的匹配項。 我想建立一個mxm稀疏矩陣K ,如果X[j]是kNN為X[i]返回的包,則K[i, j]是行X[i]X[j]的點積,否則為0 。

這是我編寫的代碼:

X = ...
knn = NearestNeighbors(n_neighbors=self.n_neighbors, metric='l2')
knn.fit(X)
knn_indices = knn.kneighbors(X, return_distance=False)

m, k = X.shape[0], self.n_neighbors
K = lil_matrix((m, m))

for i, indices in enumerate(knn_indices):
    xi = X.getrow(i)
    for j in indices:
        xj = X.getrow(j)
        K[i, j] = xi.dot(xj.T)[0, 0]

我試圖弄清楚如何使其更有效。 在我的方案中, m為〜120萬, n為〜50000, k為500,因此性能非常重要。

我填充K的最后一部分是程序的瓶頸。 getrow似乎很差; 根據scipy docs的說法,它會復制行,因此getrow調用每次被調用時最多可以復制5萬個元素。 另外,在最內層的循環中,我無法弄清楚如何為dot取標量,而不是創建一個全新的1x1稀疏矩陣。

如何避免這些問題並加速/向量化此代碼的最后一部分? 謝謝。

In [21]: from scipy import sparse
In [22]: M = sparse.random(10,10,.2,'csr')
In [23]: M
Out[23]: 
<10x10 sparse matrix of type '<class 'numpy.float64'>'
    with 20 stored elements in Compressed Sparse Row format>

看一個MA ,我選擇了這個小的knn_indices數組進行測試:

In [45]: knn = np.array([[4],[2],[],[1,3]])

您的雙循環:

In [46]: for i, indices in enumerate(knn):
    ...:     xi = M[i,:]
    ...:     for j in indices:
    ...:         xj = M[j,:]
    ...:         print((xi*xj.T).A)
    ...:         
[[0.35494592]]
[[0.]]
[[0.08112133]]
[[0.56905781]]

內循環可以壓縮:

In [47]: for i, indices in enumerate(knn):
    ...:     xi = M[i,:]
    ...:     xj = M[indices,:]
    ...:     print((xi*xj.T).A)
    ...:         
[[0.35494592]]
[[0.]]
[]
[[0.08112133 0.56905781]]

並分配:

In [49]: k = sparse.lil_matrix((4,5))
In [50]: for i, indices in enumerate(knn):
    ...:     xi = M[i,:]
    ...:     for j in indices:
    ...:         xj = M[j,:]
    ...:         k[i,j] = (xi*xj.T)[0,0]
    ...:         
    ...:         
In [51]: k.A
Out[51]: 
array([[0.        , 0.        , 0.        , 0.        , 0.35494592],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.08112133, 0.        , 0.56905781, 0.        ]])

第二個循環

 k[i,indices] = (xi*xj.T)

做同樣的事情。

也可以對i循環執行某些操作,但這至少是一個開始。

knn不需要數組。 內部列表長度不同,無論如何它都是對象dtype。 最好將其保留為列表。

填充此lil矩陣的替代方法是將iindices和點積以coo樣式數組進行累積。

In [64]: r,c,d = [],[],[]
In [65]: for i, indices in enumerate(knn):
    ...:     xi = M[i,:]
    ...:     xj = M[indices,:]
    ...:     t = (xi*xj.T).data
    ...:     if len(t)>0:
    ...:         r.extend([i]*len(indices))
    ...:         c.extend(indices)
    ...:         d.extend(t)
    ...:         
In [66]: r,c,d
Out[66]: 
([0, 3, 3],
 [4, 1, 3],
 [0.3549459176547072, 0.08112132851228658, 0.5690578146292733])
In [67]: sparse.coo_matrix((d,(r,c))).A
Out[67]: 
array([[0.        , 0.        , 0.        , 0.        , 0.35494592],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.08112133, 0.        , 0.56905781, 0.        ]])

在我的測試案例中,第二行沒有任何非零值,因此需要在循環中進行額外的測試。 我不知道這是否比lil方法要快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM