推荐模型之UserCF

261 阅读2分钟
参与拿奖:本文已参与「新人创作礼」活动,一起开启掘金创作之路
ps:代码文末自取

1.相关概念

这里的UserCF指的是根据用户数据进行协同过滤,挑选与目标user相似的user中没有交互过的item进行推荐。例如给定三段序列u1:[1,2,3,4,5],u2:[2,3,4,5,9],u3:[1,2,7], 这里计算方式使用的是Cij/math.sqrt(N(i)* N(j)),Cij表示user i 和user j item交集的个数,N(i)和N(j)分别表示user i和 user j历史交互的item数目。根据上述公式计算可以得出u1与u2最相似,u3与u1最相似。因此推荐目标就是为u1推荐9,为u2推荐1,为u3推荐[3,4,5]中的一个。

2.代码

2.1 数据预处理

这里使用的是Amazon[1]数据集为例。需要处理的数据,每一行的内容是user_id item_ids,内容如下图所示:

image.png

import numpy as np
import pandas as pd
import os

"""根据流行度对用户进行推荐,不需要训练集、验证集,区分真实数据与原始数据"""
"""根据协同过滤对用户进行推荐,不需要训练集、验证集,区分真实数据与原始数据"""
def dataProcess(filename,topk):
    """
    :param:
    filename:需要处理的数据文件名
    topk:最好的k个推荐
    :return:
     original_data: 原始数据(去掉最后一个item的数据)
     data_dic:原始数据形成的数据字典
     actual_data:每个user的最后一个item数据
    """
    original_data=[]
    train_data=[]
    data_dic={}
    actual_data=[]
    us_it={}
    with open(os.getcwd()+filename) as f:
        d_f=f.readlines()
    for d in d_f:
        temp=list(map(int,d.split(" ")))
        us_it.setdefault(temp[0],{}) # 为每一个user 分配一个字典
        for it_ in temp[1:-1*topk]:
            us_it[temp[0]][it_]=1
        train_data.append(temp[1:-1*topk])
        original_data.extend(temp[1:-1*topk])
        actual_data.append(temp[-topk:])

    for i_ in original_data:
        data_dic[i_]=data_dic.get(i_,0)+1

    return train_data,actual_data,data_dic,us_it



if __name__ == '__main__':
    filename="/data/Sports_and_Outdoors.txt"
    train,test,_,us_it=dataProcess(filename,1)
    print(us_it)
复制代码

image.png

2.2 UserCF

def User_CF(original_data, actual_data,us_it,t):
    U_U={}
    U_I={}
    for u_,itemsets in enumerate(original_data):
        U_U.setdefault(u_,{})
        for u_n,itemsets in enumerate(original_data):
            if u_==u_n:
                U_U[u_][u_n]=-1
                continue
            U_U[u_][u_n]=cal_sim(u_,u_n,original_data)
        temp_userset=sorted(U_U[u_].keys(),key=lambda x:U_U[u_][x],reverse=True)
        temp_itemset=[]
        # print(temp_userset)
        for u_u in temp_userset: # 4 2 3 -> 3 1 2
            # print(u_u)
            if len(set(temp_itemset))<t:
                temp_itemset.extend(list(set(original_data[u_u])-set(original_data[u_])))
            else:
                break
        temp_dic={}
        for i_t_t in temp_itemset:
            if i_t_t not in temp_dic:
                temp_dic[i_t_t]=1
            else:
                temp_dic[i_t_t]+=1

        U_I.setdefault(u_,[])
        temp_itemset=sorted(list(set(temp_itemset)),key=lambda x:temp_dic[x],reverse=True)[:t]
        U_I[u_].extend(temp_itemset)

    return list(U_I.values()),actual_data

2.3 打印结果

def metric_all(tag):
    filename=os.listdir(os.getcwd()+"/data/")
    with open("UserCF_result.txt","w+") as ff:
        for f in filename:
            if f[-4:] !=".txt":
                continue
            print(f)
            # 做next_item predict
            original_data, actual_data, data_dic,us_it=dataProcess("/data/"+f,topk=1)
            ff.write(f+"\n")
            pred, actual = User_CF(original_data[:10000], actual_data[:10000], us_it, 100)
            for t in tag:
                pred_t=[pred[i][:t] for i in range(len(pred))]
                rc_t=round(recall_at_k(actual,pred_t,t),4)
                ndcg_t=round(ndcg_k(actual,pred_t,t),4)
                print('HIT@{0} : {1} , NDCG@{0} : {2}'.format(t,rc_t,ndcg_t))
                ff.write('HIT@{0} : {1} , NDCG@{0} : {2}'.format(t,rc_t,ndcg_t))
                ff.write("\n")

运行结果

Beauty.txt
HIT@1 : 0.0052 , NDCG@1 : 0.0052
HIT@5 : 0.0157 , NDCG@5 : 0.0107
HIT@10 : 0.0205 , NDCG@10 : 0.0122
HIT@15 : 0.0249 , NDCG@15 : 0.0134
HIT@20 : 0.0279 , NDCG@20 : 0.0141
HIT@25 : 0.0316 , NDCG@25 : 0.0149
HIT@30 : 0.0345 , NDCG@30 : 0.0155

其结果相较于ItemCF[3]较差,但结果好于PopRec[4]

参考资料

[1] Amazon

[2] 代码github自取

[3] ItemCF

[4] PopRec