【问题标题】:Scipy Cosine Similarity is very slow using Pandas Dataframes使用 Pandas Dataframes 的 Scipy 余弦相似度非常慢
【发布时间】:2018-11-01 15:58:43
【问题描述】:

几天前,我通过做一个项目开始学习 Python。 我有两个数据框是我从我的数据中准备的,它们是用户数据框和项目数据框。 User DF 有 17k 个不同的用户,Item DF 有 1,500 张专辑。我正在使用协同过滤来获取两个数据帧之间的余弦相似度 如下

from scipy.spatial.distance import cosine
for i in range(0,len(user_normalized.index)-1):
 for j in range(0,len(item_matrix.index)-1):
  item_matrix_cpy.at[j, 'cosine'] = 1 -cosine(item_matrix_same_shape.iloc[j], user_normalized.iloc[i])  

我观察到我的循环逻辑执行速度非常慢并且一直运行到完成。我为少数用户(

【问题讨论】:

    标签: python pandas dataframe cosine-similarity collaborative-filtering


    【解决方案1】:

    您可以使用sklearn.metrics.pairwise.cosine_similarity,它将 2 个矩阵作为输入。这种方法比单独计算每一对要高效得多。

    from sklearn.metrics.pairwise import cosine_similarity
    cosine_similarity(
        item_matrix_same_shape.values,
        user_normalized.values
    )
    

    这是sklearn's implementation 888 行:

    • 它只使用numpy矩阵运算,没有任何python for循环
    • 它只通过normalize 函数为每个样本(用户、专辑)计算一次范数。使用您的代码,每个专辑向量的范数会计算 17K 次!

    【讨论】:

      【解决方案2】:

      让我们做一些计算余弦相似度的快速基准测试,以便我们对其性能有一个基本的了解

      timeit.timeit('cosine_similarity(x,y)',setup='from sklearn.metrics.pairwise import cosine_similarity; import numpy as np; x,y = np.random.random(100),np.random.random(100)',number=20000)
      4.5380048290098784
      
      timeit.timeit('cosine(x,y)',setup='from scipy.spatial.distance import cosine; import numpy as np; x,y = np.random.random(100),np.random.random(100)',number=20000)
      0.6468068649992347
      

      所以看起来scipy's cosine 的性能优于sklearn,所以我们可能不会从切换实现中受益......继续。

      如果您想对包含 17k 个条目的矩阵执行成对余弦相似度,则大致相当于 17k^2 评估 == 289,000,000 这已经很多了,尤其是如果您的向量也是高维的,则更是如此.

      让我们看看在您输入的thousandth 上完成余弦相似性的速度有多快,所以289,000 评估。

      timeit.timeit('cosine(x,y)',setup='from scipy.spatial.distance import cosine; import numpy as np; x,y = np.random.random(100),np.random.random(100)',number=int((17000**2)/1000))
      9.857225538988132
      

      大约需要 10 秒。由于这会随着案例的数量线性增长......您可以预计花费 10,000 秒,或者不到 3 小时来执行大约 3 亿次余弦相似度运算(在我的例子中,是长度为 100 的向量)。

      所以余弦相似度计算只需要花费一定的时间。可以潜在改进代码的一种方法是,您可以将余弦相似性存储在新数据帧或新系列中,然后使用索引将其连接到原始数据帧,而不是在循环的每次迭代中添加到数据帧使用at 方法(可能很慢)。

      如果所有这些都有意义,那么您的计算负载就足够高,以至于一台小型笔记本电脑开始感到虚弱。你接下来做什么取决于你的用例——这需要做一次吗?只需运行它并忘记它。

      需要偶尔但不要太频繁?使用 python 的multiprocessing 模块(重要的是不要使用multithreading,在这种情况下它不起作用)并行化你的实现(如果你有四个内核,一次使用所有内核)。

      需要经常运行吗?您可能需要更多硬件,或研究云计算解决方案 - 您会惊讶于妥善管理的云计算设置的经济实惠程度。

      【讨论】:

      • 非常感谢您的详细分析。真的很感谢你的时间。要回答你关于频率的问题 - 我需要这段代码偶尔执行一次,但不是经常执行。那么在我的情况下,有什么方法可以让我的余弦计算快速吗?
      • @NedunuriRajesh 查看 Dask 的数据帧,它们为数据帧的并行处理提供支持。您可以通过并行化来加速您的解决方案,并且不将计算出的余弦相似度存储在同一数据帧中,而是将其存储在新的数据帧/系列中(如果需要,稍后再连接它们)。
      猜你喜欢
      • 2020-08-12
      • 2020-01-28
      • 1970-01-01
      • 2012-11-20
      • 2019-05-21
      • 2018-06-16
      • 2017-04-04
      • 2011-01-01
      相关资源
      最近更新 更多