首页 > 解决方案 > 解包稀疏矩阵性能调优

问题描述

我正在使用 ING 的数据科学家创建的sparse_dot_topn库来搜索大量公司名称(近 150 万条记录)中的近似重复项。该库的最新更新现在可以使用多个线程来计算两个矩阵之间的叉积(即余弦相似度)。我运行了一个快速基准测试,性能提升非常显着(取决于一个人可以在他的机器/远程服务器上使用多少个内核):

+-----------+--------------+
| # threads | time (%M:%S) |
+-----------+--------------+
| 32        | 03:43:12     |
+-----------+--------------+
| 16        | 05:16:97     |
+-----------+--------------+
| 8         | 08:11:69     |
+-----------+--------------+
| 4         | 13:32:72     |
+-----------+--------------+
| 2         | 24:02:28     |
+-----------+--------------+
| 1         | 47:11:30     |
+-----------+--------------+

为了轻松探索结果,我需要解压缩生成的稀疏矩阵。幸运的是,我找到了Chris van den Berg编写的以下辅助函数,它正是这样做的(此处链接到 Chris 的博客文章):

def get_matches_df(sparse_matrix, name_vector, top=100):
    non_zeros = sparse_matrix.nonzero()

    sparserows = non_zeros[0]
    sparsecols = non_zeros[1]

    if top:
        nr_matches = top
    else:
        nr_matches = sparsecols.size

    left_side = np.empty([nr_matches], dtype=object)
    right_side = np.empty([nr_matches], dtype=object)
    similairity = np.zeros(nr_matches)

    for index in range(0, nr_matches):
        left_side[index] = name_vector[sparserows[index]]
        right_side[index] = name_vector[sparsecols[index]]
        similairity[index] = sparse_matrix.data[index]

    return pd.DataFrame(
        {"left_side": left_side, "right_side": right_side, "similairity": similairity}
    )

上面的函数有一个可选参数,只查看前n 个值,但我必须在完整数据上运行它。我目前的问题是这需要很长时间才能完成(大约 1 小时)。

问:我想知道如何提高这里的性能(如果可能的话)?特别是因为我有很多核心没有用于这项工作。

在性能调整方面,我不是专家。我探索的一个选项是 Numba。@njit(parallel=True)我用 Numba装饰了这个函数,prange而不是range指定循环可以并行化,但这失败了。我的理解是 Numba 不能处理字符串值(即,在我的例子中是公司名称)。

任何有关提高性能的可能方法的帮助将不胜感激。

标签: pythonperformancenumpysparse-matrix

解决方案


如果没有一些示例,我无法确定这是您要查找的内容,但我认为这就是您想要的。我对top您的示例感到困惑,因为它只采用第一个结果而不是具有最大值的结果。

import pandas as pd
from scipy import sparse
import random
import string

arr = sparse.random(100,100,density=0.02).tocoo()
name_vec = pd.Series(''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) for _ in range(100))

pd.DataFrame({"left_side": name_vec[arr.row].tolist(), 
              "right_side": name_vec[arr.col].tolist(), 
              "similairity": arr.data})

在运行时方面,您可以通过避免系列 -> 列表 -> 系列步骤来清理更多。


推荐阅读