如何使用python通过余弦相似度有效地检索前K相似文档?

Posted

技术标签:

【中文标题】如何使用python通过余弦相似度有效地检索前K相似文档?【英文标题】:How to efficiently retrieve top K-similar document by cosine similarity using python? 【发布时间】:2016-03-30 13:20:31 【问题描述】:

我正在处理十万 (100,000) 个文档(平均文档长度约为 500 个术语)。对于每个文档,我想通过余弦相似度获得前 k 个(例如 k = 5)相似文档。那么如何高效地通过Python做到这一点。

这是我所做的:

    对每个文档,做文本分割,去除停用词,统计词频(tf) 所以我们得到 tf 矩阵,大约 100,000 个文档 * 600000 个术语 做 1 - pairwise_distances(tf_matrix, metric = "cosine") 对于每个文档,获取前 k 个相似文档。

我在 i5-2.5GHz 上运行我的代码,12 小时过去了,但它仍然可以工作。所以我想知道如何优化我的代码或过程。

这是我的想法:

    对每个文档,做特征选择,只保留 tf > 1 的词 先进行聚类,然后计算每个聚类内的余弦相似度 因为我只需要前 k 个相似的文档,我需要计算所有成对的余弦相似度吗? python GPU 编程还是并行编程?

那么,你有什么好主意吗?

非常感谢。


我知道有一个similar question,但这不是我想要的。


更新1

感谢@orange,经过分析,我发现第2步是瓶颈!下面是示例代码:

def construct_dt_matrix():
    dt_matrix = pd.DataFrame(columns=['docid'])
    docid = 0
    for f in files:
        # text segmentation for f
        # remove stop words
        # word count store in cleaned_dict = 'word': tf
        dt_matrix.loc[docid] = [0] * dt_matrix.shape[1] # add one row, init all 0
        dt_matrix.set_value(docid, 'docid', docid)
        for key, value in cleaned_dict.items():
            if key not in dt_matrix.columns.values:
                dt_matrix[key] = 0 # add one column, init all 0
            dt_matrix.set_value(docid, key, value) # bottleneck
        docid += 1

因此,瓶颈是向 pandas 添加新的行和列。有什么想法吗?

【问题讨论】:

您是否在较小的数据集上尝试过,并且可能使用分析器来查找和优化代码中的热点?看看 RunSnakeRun。 @orange 感谢您的建议,我找到了瓶颈并更新了描述。有什么想法吗? self.dt_matrix.set_value(docid, key, value) 看起来像一个错误。这会一遍又一遍地设置相同的值(索引docid,在迭代cleaned_dict 和列key 后递增)。 也许阅读一些关于 Pandas 的教程。您对它的理解可能并不准确(他们中的许多人解释了它的工作原理以及为什么它很快,我认为这是必需的)。 对不起,代码是从一个类中提取的,我已经删除了self。循环是正确的,我首先添加一个全为 0 的新行,然后对于每个键,用 value 填充 key 列。像这样添加行和列可能效率低下。无论如何,谢谢。 【参考方案1】:

Pandas DataFrames(和底层的 numpy)只有在你一次分配数据数组时才真正快。 set_value 需要调用矩阵中的每个单元格! 您可以使用dt_matrix = pd.DataFrame(cleaned_dict),并且您有一个带有一个函数调用的 DataFrame(忽略 Pandas 内部调用)。

试试吧:

dt_matrix = pd.DataFrame()

for docid, f in enumerate(files):
    dt_matrix_file = pd.DataFrame(cleaned_dict)
    dt_matrix_file['docid'] = docid
    dt_matrix = dt_matrix.append(dt_matrix_file)

这应该快几个数量级。

如果您要求 NaN 单元格为零,则可以执行 dt_matrix.fillna(0)(同样,一次调用而不是可能的 n * m)。

【讨论】:

首先谢谢。我试过DataFrame.append(),确实比set_value快,但没那么快。受您的启发,我们可以先获取所有列名,然后将新行添加到 DataFrame。可能append需要join,所以还需要一些时间。 不仅append 让它更快,还有DataFrame 的创建。并避免迭代字典。

以上是关于如何使用python通过余弦相似度有效地检索前K相似文档?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地计算数百万个字符串之间的余弦相似度

R语言使用lsa包计算余弦相似度(Cosine Similarity)实战:两个向量的余弦相似度矩阵的余弦相度

余弦相似度的应用

在 python 中使用余弦相似度返回与查询文档相比最相似的文档

余弦计算相似度理解以及计算

对 TF-IDF 特征向量中的特定特征进行加权,用于 k-means 聚类和余弦相似度