我能做些什么来提高 sklearn 在 9000+ 数据上的 Jaccard 相似度得分性能

What can I do to improve sklearn's Jaccard similarity score performance on 9000+ data

我正在尝试在向量列表 x 上创建一个 table 的 Jaccard 相似度分数,该列表中的所有其他元素都超过 9000 行(因此导致大约 9000、9000 列表):

[[  2   2  67   2   5   3  62  68  27]
[  2   9  67   2   1   3  20  62 139]
[  2  17  67   2   0   6   6  62  73]
[  2  17  67   2   0   6  39  68  92]
[  0   0  67   0   0   3  62  62  13]
...

我是初学者,所以我试图用这样的代码来实现可耻的借口:

similarities_matrix = np.empty([len(x), len(x)])
for icounter, i in enumerate(x.as_matrix()):
    similarities_row = np.empty(len(x))
    for jcounter, j in enumerate(x.as_matrix()):
        similarities_row[jcounter] = jaccard_similarity_score(i, j)
    similarities_matrix[icounter] = similarities_row
pprint(similarities_matrix) 

但它 运行 慢得不可思议。 理想情况下,我希望我的代码在我的一生中 运行(最好在 5 分钟内)

目前,此代码 运行 每个元素大约需要一秒来计算相似度矩阵。

如果您不介意使用scipy,您可以使用函数pdist from scipy.spatial.distance. The value computed by sklearn.metrics.jaccard_similarity_score(u, v) is equivalent to 1 -scipy.spatial.distance.hamming(u, v)。例如,

In [71]: from sklearn.metrics import jaccard_similarity_score

In [72]: from scipy.spatial.distance import hamming

In [73]: u = [2, 1, 3, 5]

In [74]: v = [2, 1, 4, 5]

In [75]: jaccard_similarity_score(u, v)
Out[75]: 0.75

In [76]: 1 - hamming(u, v)
Out[76]: 0.75

'hamming'scipy.spatial.distance.pdist 提供的指标之一,因此您可以使用该函数计算所有成对距离。这里有一个小 x 用作示例:

In [77]: x = np.random.randint(0, 5, size=(8, 10))

In [78]: x
Out[78]: 
array([[4, 2, 2, 3, 1, 2, 0, 0, 4, 0],
       [3, 1, 4, 2, 3, 1, 2, 3, 4, 4],
       [1, 1, 0, 1, 0, 2, 0, 3, 3, 4],
       [2, 3, 3, 3, 1, 2, 3, 2, 1, 2],
       [3, 2, 3, 2, 0, 0, 4, 4, 3, 4],
       [3, 0, 1, 0, 4, 2, 0, 2, 1, 0],
       [4, 3, 2, 4, 1, 2, 3, 3, 2, 4],
       [3, 0, 4, 1, 3, 3, 3, 3, 1, 3]])

我将使用 squareformpdist 的输出转换为相似度的对称数组。

In [79]: from scipy.spatial.distance import pdist, squareform

In [80]: squareform(1 - pdist(x, metric='hamming'))
Out[80]: 
array([[ 1. ,  0.1,  0.2,  0.3,  0.1,  0.3,  0.4,  0. ],
       [ 0.1,  1. ,  0.3,  0. ,  0.3,  0.1,  0.2,  0.4],
       [ 0.2,  0.3,  1. ,  0.1,  0.3,  0.2,  0.3,  0.2],
       [ 0.3,  0. ,  0.1,  1. ,  0.1,  0.3,  0.4,  0.2],
       [ 0.1,  0.3,  0.3,  0.1,  1. ,  0.1,  0.1,  0.1],
       [ 0.3,  0.1,  0.2,  0.3,  0.1,  1. ,  0.1,  0.3],
       [ 0.4,  0.2,  0.3,  0.4,  0.1,  0.1,  1. ,  0.2],
       [ 0. ,  0.4,  0.2,  0.2,  0.1,  0.3,  0.2,  1. ]])

我将你的代码转换为这个函数:

def jaccard_sim_matrix(x):
    similarities_matrix = np.empty([len(x), len(x)])
    for icounter, i in enumerate(x):
        similarities_row = np.empty(len(x))
        for jcounter, j in enumerate(x):
            similarities_row[jcounter] = jaccard_similarity_score(i, j)
        similarities_matrix[icounter] = similarities_row
    return similarities_matrix 

所以我们可以验证 pdist 结果与您的计算结果相同。

In [81]: jaccard_sim_matrix(x)
Out[81]: 
array([[ 1. ,  0.1,  0.2,  0.3,  0.1,  0.3,  0.4,  0. ],
       [ 0.1,  1. ,  0.3,  0. ,  0.3,  0.1,  0.2,  0.4],
       [ 0.2,  0.3,  1. ,  0.1,  0.3,  0.2,  0.3,  0.2],
       [ 0.3,  0. ,  0.1,  1. ,  0.1,  0.3,  0.4,  0.2],
       [ 0.1,  0.3,  0.3,  0.1,  1. ,  0.1,  0.1,  0.1],
       [ 0.3,  0.1,  0.2,  0.3,  0.1,  1. ,  0.1,  0.3],
       [ 0.4,  0.2,  0.3,  0.4,  0.1,  0.1,  1. ,  0.2],
       [ 0. ,  0.4,  0.2,  0.2,  0.1,  0.3,  0.2,  1. ]])

在这里我将比较更大数组的时间:

In [82]: x = np.random.randint(0, 5, size=(500, 10))

In [83]: %timeit jaccard_sim_matrix(x)
14.9 s ± 192 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [84]: %timeit squareform(1 - pdist(x, metric='hamming'))
1.19 ms ± 2.23 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

最后,让我们为形状为 (9000, 10) 的输入计算时间:

In [94]: x = np.random.randint(0, 5, size=(9000, 10))

In [95]: %timeit squareform(1 - pdist(x, metric='hamming'))
1.34 s ± 9.13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

这只是 1.34 秒——绝对在一生中。