[英]How do I vectorize this code that produces a scipy sparse matrix?
讓我首先解釋一下我想做什么。 我正在嘗試基於存儲在mxn
稀疏矩陣X
的m
包(每個都有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
矩陣的替代方法是將i
, indices
和點積以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.