读取文件中的用户-物品交互数据,构建了两个字典:user_x_items和item_x_N。
user_x_items:键为用户ID,值为该用户交互过的物品ID集合。item_x_N:键为物品ID,值为该物品被交互的次数。
import math
import multiprocessing
import json
from collections import defaultdict
user_x_items = defaultdict(set)
item_x_N = defaultdict(int)
with open('train_user_items.json','r') as f:
train_user_x_items = json.load(f)
# 1. 处理数据得到user->itemlist已经item交互次数
for userid, items in train_user_x_items.items():
for movieid, rating, timestamp in items:
item_x_N[movieid] += 1
user_x_items[userid].add(movieid)
# 提前计算
for movieid, N in item_x_N.items():
item_x_N[movieid] = 1 / math.log(1 + N)
all_users = list(user_x_items.keys())
计算用户列表中第i个用户与其他用户的相似度。对于每个用户对(i,j),计算他们的共同交互物品,并根据物品的交互次数计算得分。得分计算公式为:score = sum(1 / log(1 + item_x_N[item])) / sqrt(ni * nj),其中ni和nj分别为用户i和用户j交互的物品数量。将计算出的相似度分数以元组(ui, uj, score)的形式存入列表sim中,并返回。
# 3. 遍历每个用户,与其他用户的相似度,使用多进程的方式计算,最后进行合并
def simscore(i):
sim = []
ui = all_users[i]
ui_items = user_x_items[ui]
ni = len(ui_items)
for j in range(i+1, len(all_users)):
score = 0
uj = all_users[j]
uj_items = user_x_items[uj]
union_items = ui_items & uj_items
for item in union_items:
score += item_x_N[item]
nj = len(uj_items)
if ni > 0 and nj > 0:
score = score / math.sqrt(ni * nj)
sim.append((ui, uj, score))
return sim
使用multiprocessing.Pool创建进程池,使用imap方法将用户索引列表映射到producer函数,实现多进程计算用户之间的相似度。将每个进程返回的相似度结果合并到字典uu_score中,键为用户ID,值为包含其他用户及其相似度分数的列表。
if __name__ == "__main__":
results = []
user_simscore = defaultdict(dict)
num_producers = 16
with multiprocessing.Pool(processes=num_producers) as pool:
# map会将所有的结果全部计算完成存到results中,很占用内存
# imap得到是一个迭代器,results是一个迭代器,for循环获取的时候会执行计算,占用空间小
results = pool.imap(simscore, list(range(len(all_users))))
for sim_scores in results:
for sim_score in sim_scores:
ui, uj, score = sim_score
user_simscore[ui][uj] = score
user_simscore[uj][ui] = score
topk = 10
# 4. 计算每个用户最相似的topk个用户和分数
for uid, scores in user_simscore.items():
scores = sorted(scores.items(), key = lambda x : x[1], reverse = True)
user_simscore[uid] = scores[:topk]
with open('user_simscore.json', 'w') as f:
json.dump(user_simscore, f)