Python-使用scipy加速余弦相似度

时间:2019-03-22 23:12:12

标签: python pandas scipy

以下问题来自于我之前提出的一个问题:Python - How to speed up cosine similarity with counting arrays

使用提出的解决方案时,我面临着一个很大的复杂性问题,基本上,我的实现花费大量时间来构建余弦相似度矩阵。在我正在使用的代码下面:

import numpy as np
import pandas as pd
import networkx as nx
from scipy import spatial

def compute_other(user_1, user_2):
    uniq = list(set(user_1[0] + user_2[0]))

    duniq = {k:0 for k in uniq}    

    u1 = create_vector(duniq, list(user_1[0]))
    u2 = create_vector(duniq, list(user_2[0]))

    return 1 - spatial.distance.cosine(u1, u2)

# START
distances = spatial.distance.cdist(df[['ARTIST']], df[['ARTIST']], metric=compute_other)

idx_to_remove = np.triu_indices(len(distances))
distances[idx_to_remove] = 0

df_dist = pd.DataFrame(distances, index = df.index, columns = df.index)
edges = df_dist.stack().to_dict()
edges = {k: v for k, v in edges.items() if v > 0}

print('NET inference')
net = nx.Graph()
net.add_nodes_from(df.index)
net.add_edges_from(edges)     

我要注意的第一件事是,我计算了完整的矩阵并删除了其中的一半,因此只计算其中的一半会很酷 我需要(这将是x2)。

表示df的结构:

ARTIST
"(75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 75751, 15053)"
"(55852, 55852, 17727, 17727, 2182)"
"(11446, 11446, 11446, 11446, 11446, 11446, 11446, 11446)"
"(54795,)"
"(22873, 22873, 22873, 22873)"
"(5634, 5634)"
"(311, 18672)"
"(1740, 1740, 1740, 1740, 1746, 15048, 15048, 1740)"
"(1788, 1983, 1788, 1748, 723, 100744, 723, 226, 1583, 12188, 51325, 1748, 75401, 1171)"
"(59173, 59173)"
"(2673, 2673, 2673, 2673, 2673, 2673, 2673, 5634, 5634, 5634)"
"(2251, 4229, 14207, 1744, 16366, 1218)"
"(19703, 1171, 1171)"
"(12877,)"
"(1243, 8249, 2061, 1243, 13343, 9868, 574509, 892, 1080, 1243, 3868, 2061, 4655)"
"(1229,)"
"(3868, 60112, 11084)"
"(15869, 15869, 15869, 15869)"
"(4067, 4067, 4067, 4067, 4067, 4067)"
"(1171, 1171, 1171, 1171)"
"(1245, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1195, 1193, 1193, 1193, 1193, 1193, 1193)"
"(723, 723)"  

dataset已完成,可以与我发布的代码一起使用。只需将其作为带有熊猫的普通csv阅读并应用该功能即可:

import ast
import pandas as pd

df = pd.read_csv('Stack.csv')
df['ARTIST'] = df['ARTIST'].apply(lambda x : ast.literal_eval(x))

该代码几乎在166中执行。我在8核处理器上并行执行8个进程,每个进程在不同的数据集上计算相同的函数。老实说,我不知道这是否已经是最优化的版本,但是,正如我之前解释的那样,将计算的一半删除也是非常有用的(从16683)。

编辑:在create_vector函数下面:

def create_vector(duniq, l):
    dx = duniq.copy()
    dx.update(Counter(l)) # Count the values
    return list(dx.values()) # Return a list

1 个答案:

答案 0 :(得分:1)

我试图对此进行修改,但是我在两行中遇到了编译错误:  u1 = create_vector(duniq,list(user_1 [0]))  u2 = create_vector(duniq,list(user_2 [0]))

create_vector()是您构建但未发布的def吗?

我怀疑在您的df上使用遮罩可能会通过消除正在执行的覆盖来提高性能 距离[idx_to_remove] = 0 并应减少的迭代次数 “ edges = {k:对于k,v,如果v> 0},则在edges.items()中使用v””

如果您可以发布create_vector()的来源或def本身,那么我想测试一个遮罩。这是一个有趣的问题。

嗨,Guido。很抱歉,花了这么长时间,但这真是难以克服! 在尝试了几种不同的方法(甚至花费了更长的时间)之后,我想出了以下方法来代替您的create_vector()和compute_other()函数:

def compute_other2(user_1, user_2):
    uniq = set(user_1[0] + user_2[0]) #create list of unique list of items in user _1 and user_2   
    u1 = [user_1[0].count(ui) for ui in uniq]
    u2 = [user_2[0].count(ui) for ui in uniq]
    return 1 - spatial.distance.cosine(u1, u2)

我的性能提高了20%,比我期望的要少,但是有一些。 注意:我仍在使用“ spatial.distance.cdist”运行您的代码。我确实看到您通过切换到“ spatial.distance.pdist”获得了50%的收益。我不确定您是如何使用它的(我怀疑是矢量数学)超出了我的范围。也许您可以将此新的compute_other()函数与spatial.distance.pdist一起使用,并获得更多收益。

P.S。如果您尝试此操作,请验证结果。我对照您的原始代码检查了我的代码,它对我来说似乎是正确的。