让我先解释一下我想做什么。我正在尝试构建一个基于m
个包的推荐系统,每个包都有n
个功能,存储在m x n
稀疏矩阵X
中。为此,我尝试运行kNN以获得包的k
最接近的匹配。我想构建一个m x m
稀疏矩阵K
,其中K[i, j]
是行X[i]
和X[j]
的点积,如果X[j]
是返回的包按kNN表示X[i]
,否则为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文档,它会复制该行,因此getrow
调用每次调用时都可以复制多达50k个元素。此外,在最里面的循环中,我无法弄清楚如何找回dot
的标量,而不是创建一个全新的1x1
稀疏矩阵。
如何避免这些问题并加速/矢量化此代码的最后部分?感谢。
答案 0 :(得分:1)
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>
查看M.A
,我选择了这个小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
方法更快。