推荐算法实践:UserCF计算用户相似度

49 阅读2分钟

读取文件中的用户-物品交互数据,构建了两个字典:user_x_itemsitem_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),其中ninj分别为用户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)