推荐模型之ItemCF

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

1.相关概念

这里的ItemCF指的是根据用户交互历史中的item数据进行协同过滤,挑选与目标item相似的item进行推荐。例如给定一段序列[1,2,3,4,5],推荐目标就是计算与item id为4的商品最相似的物品。 这里计算方式使用的是Cij/math.sqrt(N(i)* N(j)),Cij表示物品i和物品j共现的次数,N(i)和N(j)分别表示物品i和物品j出现的次数。

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 ItemCF


def item_CF(original_data,actual_data,data_dic,us_it,mode,topk=1):
    """
    :param original_data: [[1,2,3]···]
    :param actual_data: [[1],[2]···]
    :param data_dic: {1:2,3:4···}
    :param topk: 1
    :return:
    """
    pred_data=[[] for _ in range(len(original_data))]
    for l in range(len(pred_data)):
        pred_data[l].append(original_data[l][-1])
    C={}
    N={}

    for idx,(u,items) in enumerate(us_it.items()):
        if mode=="ItemCF":
            for i in items.keys(): # 对于每一个user
                N.setdefault(i,0)
                N[i]+=1
                for j in items.keys():
                    if i==j:
                        continue
                    C.setdefault(i,{})
                    C[i].setdefault(j,0)
                    C[i][j]+=1
        else:
            for i in items.keys(): # 对于每一个user
                N.setdefault(i,0)
                N[i]+=1
                for j in items.keys():
                    if i==j:
                        continue
                    C.setdefault(i,{})
                    C[i].setdefault(j,0)
                    C[i][j]+=1/math.log(1+len(items)*1.0)

        # print(C)
    itemSimBest={}
    for idx,(cur_item,related_items) in enumerate(C.items()):
        itemSimBest.setdefault(cur_item,{})
        for related_item,score in related_items.items():
            itemSimBest[cur_item].setdefault(related_item,0)
            itemSimBest[cur_item][related_item]+=score/math.sqrt(N[cur_item] * N[related_item])
    itemSimbest_f={}
    for it in itemSimBest.keys():
        itemSimbest_f[it]=sorted(itemSimBest[it].items(),key=lambda x:x[1],reverse=True)


    # print(itemSimbest_f)
    pred=[[] for _ in range(len(original_data))]

    for u_i in range(len(pred)):
        temp=[]
        if pred_data[u_i][0] in itemSimbest_f:
            for i_i in itemSimbest_f[pred_data[u_i][0]]:
                if i_i[0] not in original_data[u_i] and len(temp)<topk:
                    temp.append(i_i[0])
                else:
                    continue
            pred[u_i].extend(temp)
            while len(pred[u_i])<topk:
                t_=random.randint(1,len(actual_data))
                if t_ not in original_data[u_i]:
                    pred[u_i].append(t_)
                else:
                    continue
        else:
            i=0
            while i<topk:
                t_=random.randint(1,len(actual_data))
                if t_ not in original_data[u_i]:
                    pred[u_i].append(t_)
                    i+=1
                else:
                    continue
    return pred,actual_data

2.3 打印结果

def metric_all(tag):
    filename=os.listdir(os.getcwd()+"/data/")
    with open("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")
            for t in tag:
                pred, actual = item_CF(original_data, actual_data, data_dic, us_it, "ItemCF_IUF", t)
                rc_t=round(recall_at_k(actual,pred,t),4)
                ndcg_t=round(ndcg_k(actual,pred,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")

if __name__ == '__main__':
    tag = [1, 5, 10, 15, 20, 25, 30]
    metric_all(tag)

运行结果

Beauty.txt
HIT@1 : 0.0236 , NDCG@1 : 0.0236
HIT@5 : 0.0509 , NDCG@5 : 0.0379
HIT@10 : 0.067 , NDCG@10 : 0.0431
HIT@15 : 0.0773 , NDCG@15 : 0.0458
HIT@20 : 0.0855 , NDCG@20 : 0.0478
HIT@25 : 0.0923 , NDCG@25 : 0.0493
HIT@30 : 0.0983 , NDCG@30 : 0.0505

参考资料

[1] Amazon

[2] 代码github自取