我正在尝试为学校项目构建推荐算法。在某些时候,我有这个功能,这对性能至关重要。它返回与用户具有最高相似度的5个项目。我做了三种不同的方式。
(1),我认为它是最“pythonic”的,但是最慢的方法,完成大约需要800ms。
(2)虽然增加了排序操作,但是更加丑陋但却出乎意料地更快(600毫秒)。
然后,我就像在旧式C ++(3)中那样做,这是最快的,但不是很多(550ms):
import math
import numpy as np
import pickle
import bisect
shrink=10
return_itemcount=5
target_users_list=[1,3,4]
already_rated_set={ 1: {1,2}, 3: {1}, 4: {5}}
user_features_dict={1: np.array([5,6]), 3: np.array([6,7]), 4: np.array([11])}
item_features_dict={1: np.array([6,7]), 2: np.array([7,8]), 3: np.array([6,11])}
items_set=set(item_features_dict.keys())
#(1)
def get_item_similarfeatures1(user):
res=[]
for item in items_set-already_rated_set[user]:
bisect.insort(res,[useritem_similarity(user,item),item])
return [x[1] if x[0]>0 else -1 for x in res[(-return_itemcount-1):]]
#(2)
def get_item_similarfeatures2(user):
res={}
for item in items_set-already_rated_set[user]:
sim=useritem_similarity(user,item)
if sim>0:
res[item]=sim
res=sorted(res.items(), key=lambda x: x[1],reverse=True)[0:5]
return list(map(lambda x: x[0],res))
#(3)
def get_item_similarfeatures3(user):
res=[(-1,0)]*return_itemcount
threshold=0
for item in (items_set-already_rated_set[user]):
sim=useritem_similarity(user,item)
if sim>threshold:
for idx, _ in enumerate(res):
if _[1]<sim:
res.insert(idx,(item,sim))
del res[return_itemcount]
threshold=res[return_itemcount-1][1]
break
return list(map(lambda x: x[0],res))
def useritem_similarity(user,item):
if ( user not in user_features_dict): return 0
features_user=user_features_dict[user]
features_item=item_features_dict[item]
return np.in1d(features_user,features_item).sum()/np.sqrt(features_user.size*features_item.size+shrink)
feature_recommendations_dict={ user : get_item_similarfeatures1(user) for user in target_users_list}
feature_recommendations_dict
我想了解的是:
编辑:评论中ali_m已对此进行了解答,我已根据gboffi的建议更新了代码:
def get_item_similarfeatures1(user):
res=[[0,-1]]*return_itemcount
for item in items_withfeatures_set-already_rated_set[user]:
sim=useritem_similarity(user,item)
if (sim>res[0][0] or res[return_itemcount-1][0]==0):
bisect.insort(res,[sim,item])
del res[0]
return [x[1] if x[0]>0 else -1 for x in res[-return_itemcount-1:]]
但这仍然比(3)慢一些(580ms)。
useritem_similarity
是否值得?它会产生更好的表现吗?答案 0 :(得分:0)
我有一个建议,不能在评论的限制范围内制定。
我认为你过度了,你可以保持最好的5个结果,比如我已经构建的这个例子
In [1]: import bisect as bs
In [2]: from random import random, seed
In [3]: seed(8888)
In [4]: l = [random() for _ in range(100)]
In [5]: r = [0 for i in range(5)]
In [6]: for x in l:
...: if x>r[0]:
...: bs.insort(r, x)
...: r.pop(0)
...:
In [7]: print(r) ; print(sorted(l)[-5:])
[0.9734802621456982, 0.9764741228388244, 0.9806769811156987, 0.9820424903993659, 0.9884636482746705]
[0.9734802621456982, 0.9764741228388244, 0.9806769811156987, 0.9820424903993659, 0.9884636482746705]
In [8]:
当然,您应该将这个简单的示例应用于您更复杂的用例,但避免移动大量数据(插入和/或排序)可能会有很大帮助。
另一种可能性是heapq
module
In [1]: from random import random, seed
In [2]: from heapq import heappushpop, heapreplace
In [3]: seed(8888)
In [4]: l = [random() for _ in range(100)]
In [5]: r = [0 for i in range(5)]
In [6]: for x in l:
...: if x>r[0]: heapreplace(r, x)
In [7]: r
Out[7]:
[0.9734802621456982,
0.9764741228388244,
0.9806769811156987,
0.9820424903993659,
0.9884636482746705]
In [8]:
继续heapq
模块提供的可能性,
In [10]: from heapq import nlargest
In [11]: nlargest(5, l)
Out[11]:
[0.9884636482746705,
0.9820424903993659,
0.9806769811156987,
0.9764741228388244,
0.9734802621456982]
(顺序颠倒过来)。
虽然heapreplace
的时间略微,但始终优于insort
时间,nlargest
明显更快。
我没有引用时间因为你必须对你的数据的不同可能性进行基准测试,请记住,存储所有结果并最终排序的方法可能比上面的更快,具体取决于您正在处理的数据量,正如我在评论中提到的那样。
答案 1 :(得分:0)
由于您已经拥有多个执行所需功能的算法,因此您现在可以对它们进行基准测试/计时,从而找到性能最佳的算法。
然后,您将分析获胜代码以找出其瓶颈所在。
然后,您将使用我提到的here技术/工具来消除这些瓶颈。
我详细说明了上述每一项内容,包括在my talk at PyCon中查看每项工具。