基于用户的协同过滤算法主要包含以下两个步骤:
A. 搜集用户和物品的历史信息,计算用户u和其他用户的相似度,找到和目标用户Ui兴趣相似的用户集合N(u)
B.找到这个集合中用户喜欢的,且目标用户还没有听说过的物品推荐给目标用户。
适用性
由于需计算用户相似度矩阵,基于用户的协同过滤算法适用于用户较少的场合; 由于时效性较强,该方法适用于用户个性化兴趣不太明显的领域。
file = open("E:\PycharmWorks\ML\CollaborativeFiltering\ml-latest\data.csv",'r', encoding='UTF-8')#记得读取文件时加‘r’, encoding='UTF-8'
##读取data.csv中每行中除了名字的数据
data = {}##存放每位用户评论的电影和评分
for line in file.readlines()[1:100]:
#注意这里不是readline()
line = line.strip().split(',')
#如果字典中没有某位用户,则使用用户ID来创建这位用户
if not line[0] in data.keys():
data[line[0]] = {line[3]:line[1]}
#否则直接添加以该用户ID为key字典中
else:
data[line[0]][line[3]] = line[1]
#print(data)
"""计算任何两位用户之间的相似度,由于每位用户评论的电影不完全一样,所以兽先要找到两位用户共同评论过的电影
然后计算两者之间的欧式距离,最后算出两者之间的相似度
"""
from math import *
def Euclidean(user1,user2):
#取出两位用户评论过的电影和评分
user1_data=data[user1]
user2_data=data[user2]
distance = 0
#找到两位用户都评论过的电影,并计算欧式距离
for key in user1_data.keys():
if key in user2_data.keys():
#注意,distance越大表示两者越相似
distance += pow(float(user1_data[key])-float(user2_data[key]),2)
return 1/(1+sqrt(distance))#这里返回值越小,相似度越大
#计算某个用户与其他用户的相似度
def top10_simliar(userID):
res = []
for userid in data.keys():
#排除与自己计算相似度
if not userid == userID:
simliar = Euclidean(userID,userid)
res.append((userid,simliar))
res.sort(key=lambda val:val[1])
return res[:4]
RES = top10_simliar('1')
print(RES)
#根据用户推荐电影给其他人
def recommend(user):
#相似度最高的用户
top_sim_user = top10_simliar(user)[0][0]
#相似度最高的用户的观影记录
items = data[top_sim_user]
recommendations = []
#筛选出该用户未观看的电影并添加到列表中
for item in items.keys():
if item not in data[user].keys():
recommendations.append((item,items[item]))
recommendations.sort(key=lambda val:val[1],reverse=True)#按照评分排序
#返回评分最高的10部电影
return recommendations[:10]
Recommendations = recommend('1')
print(Recommendations)
我们查找与自己品味相近的用户,并根据他们喜欢的item,而自己没有看过的,就进行推荐,这样做太过随意了。这样可能会有一些问题:
- 评论者还未对item做过评论,而这些影片也许是被推荐的用户所喜欢的;
- 有些用户热衷于某一item,而其他用户并不看好。
为了解决上面这些问题,我们可以选择加权平均给item打分,根据分数的先后排名,推荐给用户
#伪代码
import numpy as np
#利用所有他人评分的加权平均,为某人提供建议
def getRecommendations(frame,person,similarity=sim_pearson,TopN=5):
columns = list(frame.columns)
#与用户最匹配的n个用户,以及匹配度
topn_sim_user=topMatches(frame,name=person,similarity=similarity,n=TopN)
#用户没有过相关行为(观看影片,购买物品等)的物品
no_sim_action=list(set(frame[columns[1]]) ^ set(frame[frame[columns[0]]==person][columns[1]]))#差集
no_action = frame.pivot_table(values=columns[2],index=columns[0],columns=columns[1],aggfunc=np.sum).loc[topn_sim_user.index,no_sim_action]
no_action['Similarity'] = topn_sim_user
print(no_action)
no_action = no_action[no_action['Similarity']>=0]
#item评分加权和,利用相似度进行加权平均
w_sum = no_action.drop('Similarity',axis=1).mul(no_action['Similarity'],axis=0).sum()#mul对应位置相乘
print(w_sum)
#item相似性和
sim_sum = no_action.notna().drop('Similarity',axis=1).mul(no_action['Similarity'],axis=0).sum()
print(no_action.notna().drop('Similarity',axis=1).mul(no_action['Similarity'],axis=0))
print(sim_sum)
#计算加权平均
rankings = (w_sum/sim_sum).sort_values(ascending=False)[:TopN]
return rankings
print(getRecommendations(frame=criticsdf,person='Toby'))
print(getRecommendations(frame=criticsdf,person='Toby',similarity=sim_distance))