2017-08-29 90 views
0

我试图来计算语料库文本文档的所有可能的组合之间的余弦相似度得分。我正在使用scikit-learn的cosine_similarity函数来执行此操作。由于我的语料库非常庞大(3000万份文档),因此语料库中文档之间的可能组合数量太多,无法存储为数据框。因此,在将它们存储在数据框中供将来使用之前,我想使用阈值过滤相似性分数,因为它们正在创建。虽然我这样做了,但我也希望将这些文档中的每个文档的相应ID分配给数据框的索引和列名称。因此,对于数据框中的数据值,每个值应具有索引(行)和列名称,这些文档ID是值为余弦相似性分值的文档ID。过滤余弦相似度得分为大熊猫数据帧

similarity_values = pd.DataFrame(cosine_similarity(tfidf_matrix), index = IDs, columns= IDs) 

这段代码运行良好,没有过滤部分。 IDs是一个列表变量,它具有与tfidf矩阵相对应的所有文档ID。

similarity_values = pd.DataFrame(cosine_similarity(tfidf_matrix)>0.65, index = IDs, columns= IDs) 

此修改有助于筛选,但相似性分数转换为布尔(真/假)值。我怎样才能保持实际的余弦相似度分数而不是布尔真/假值。

+0

cosine_similarity将输出一个方阵并且它可能的是在单个列中的一个值是> 0.65和其它的是以下。那么在那种情况下,您希望该列出现在数据框中? –

+0

@VivekKumar好问题。我希望数据框的所有值都堆叠在一起。即数据帧中的每一行应该只有一个相似度分数和相应的文档ID。 'similar_values = similarity_values.stack()。reset_index()。rename(columns = {'level_0':'ID1','level_1':'ID2',0:'Score'})' – Minu

回答

0

3E7 x 3E7是荒谬矩阵大小。在低性能笔记本电脑/台式机上实现这一目标的唯一方法是使用生成器来减少内存占用,并将问题细分为效率问题。

以下函数使用的发电机厂,并使用双for遍历组块的笛卡尔乘积。我们预先计算语料库中每个tfidf的规范。

这不是设计为较小的数据这样做同样的任务最快的解决方案。这是为了在一台适度的机器的内存中完成这项任务。

from scipy.sparse import coo_matrix 
import numpy as np 

def f(t, c, p=-1, v=False): 
    n = (t ** 2).sum(1) ** .5 
    g = lambda: ((x, t[x:x+c]) for x in range(0, t.shape[0], c)) 
    h = lambda a, b, i, j: a.dot(b.T)/n[i:i+c, None]/n[j:j+c] 
    d = lambda s: (s * (1 - np.eye(s.shape[0]))) 

    for i, a in g(): 
     for j, b in g(): 
      s = h(a, b, i, j) 
      if i == j: 
       s = d(s) 
      i_, j_ = np.where(s > p) 
      if v: 
       print('\r', 'i = {:0000000d}; j = {:0000000d}'.format(i, j), end='') 
      yield s[i_, j_], i_ + i, j_ + j 

有了,我们通过计算它们自己在每个分块和跟踪的相似之处比我们的阈值提取余弦相似。

最后,我们通过足够的相似用的相似的坐标以一个稀疏矩阵构造,并将结果指定到名称m。如果您需要矩阵表示,请使用m.toarray()

values, *ij = zip(*f(tfidf_matrix, 5000, .8, v=True)) 

values = np.concatenate(values) 
ij = list(map(np.concatenate, ij)) 

m = coo_matrix((values, ij)) 

请注意,我零对角线。否则,如果我们使用阈值-1,这将产生与sklearn.metrics.pairwise中的cosine_similarity完全相同的内容。

相同的烦躁的验证

from sklearn.metrics.pairwise import cosine_similarity 

tfidf_matrix = np.random.randint(10, size=(1000, 100)) 
s = cosine_similarity(tfidf_matrix) 

values, *ij = zip(*f(tfidf_matrix, 5000, -1, v=True)) 

values = np.concatenate(values) 
ij = list(map(np.concatenate, ij)) 

m = coo_matrix((values, ij)) 

# This should be equal the 1000. The number of 1's in the diagonal. 
(s - m.toarray()).sum() 

1000.0 
+0

请详细解释一下你的功能正在做什么,每个变量的含义是什么? – Minu

+0

对不起,不。这需要很长时间才能完成。我根本没有时间去详细解释一切。你会注意到没有人花时间。那是因为它是一笔巨大的时间投资。我会为您提供这一点,我将矩阵细分为区块,只保存超过阈值的位置和值。除此之外,将其作为一项任务来了解代码的每个部分的功能。此外,它可能会帮助您理解我们自愿回答问题的时间。我们不想浪费时间。你应该尽量不要这样做。 – piRSquared