• Item协同过滤(基于Python实现)


            在众多召回策略里面,基于Item与基于User(可参考:https://www.cnblogs.com/SysoCjs/p/11466424.html)在实现上非常相似。所以这里使用了跟基于User协同过滤的数据u.data。

    u.data数据格式(user_id, item_id, rating, timestamp)

    实现原理:
            区别于User,先根据User已经购买过,或者评价过的Items,基于算法,对其他Items做一个相似度计算,来获取基于该User的Items的相似Items,这样每个User_Item都可以得到一堆相似Items,根据相似度进行排序,每个User_Item取出topN个Items,然后将所有的User_item的各自topN个Items,共同组成一个大的Item_List,Item_list中的每个item的相似度乘上对应的User_item的权重,得到新的权重值,根据新的权重值排序,取topK个Items,作为最终的Items候选集。

    具体实现:
            因为是使用Python来实现,所以还是选择最简单的算法:杰卡尔德算法。在User协同过滤中:通过两个用户共同拥有的物品集合数量,除以两个用户的物品的平均数量。这里,需要倒过来:通过两个item共同用户数量,除以两个item各自的用户的平均数量。所以要做的是,想方设法获取这三个参数值:共同用户数量、两个item各自的用户数量。

    一、创建源数据的结构

            timestamp列数据在寻找相似用户里面,意义不大,可以不使用;对于pyspark程序,u.data数据是没有结构的,所以第一时间是读取u.data,并定义数据的结构,可以将数据的结构定义为:

    dict{user_id:{item_id:rating}}
    import pandas as pd
     
    def generate_train_data(nrows=10):
        # 处理训练数据 -> dict{user_id:{item_id:rating}}
        df = pd.read_csv('../row_data/u.data',
                         sep='	',
                         nrows=nrows,
                         names=['user_id', 'item_id', 'rating', 'timestamp'])
     
        # d为每个用户的商品及对应打分的列表
        d = dict()
        for _, row in df.iterrows():
            # 类型转换
            user_id = str(row['user_id'])
            item_id = str(row['item_id'])
            rating = row['rating']
            if user_id not in d.keys():
                d[user_id] = {item_id: rating}
            else:
                d[user_id][item_id] = rating
        return d

    二、统计杰卡尔德的参数

            基于Item的协同过滤,在遍历train_data的时候,需要同时统计每个item的User数量,以及item与item之间共同的User数量,因为,相对于基于User的协同过滤,item的User数量不可以直接通过len(train_data[i])来获取,需要另外定义一个类型为dict的N来存储,N的数据结构为{item: userNum}。

    N = dict()  # N{item:userNum},统计item的user数量

    在遍历train_data的同时,可以将N和C都收集得到:

    N = dict()  # N{item:userNum},统计item的user数量
    def item_sim(train_data):
        C = dict()  # C{item:{iten:simUserNum}}
        for u,items in train_data.items():
            for i in items.keys():
                # 对于每一个u的items,N[i]只有一个
                if N.get(i,-1)==-1:
                    N[i] = 0
                N[i] += 1
                if C.get(i,-1)==-1:
                    C[i] = dict()
                for j in items.keys():
                    if i==j:
                        continue
                    if C[i].get(j,-1)==-1:
                        C[i][j] = 0
                    C[i][j] += 1
        return C

            这个时候,共同用户数量存储在C里面,而每个item对应的用户数量存储在N里面,那么接下来可以借用杰卡尔德公式计算Item-Item相似度矩阵。

    # 计算相似度矩阵,杰卡尔德算法
    def item_item(C):
        for i, related_items in C.items():
            for j, cij in related_items.items():
                C[i][j] = 2 * cij/(N[i]+N[j]*1.0)
        return C

            最终结果还是保存在C里面,此时C的数据结构变成:

    C = dict()  # C{item:{iten:sim_score}}

    二、过滤,统计推荐候选集

            最终是要得到Item候选集,所以先定义一个字典,用于存储item,及其对应的打分:

    rank = dict() # rank{item:score}

            Item候选集仍然是指代推荐给指定User的Items,一般会根据业务需求,将该User购买过或评价过的Item进行过滤,这时,需要取出该User对应的r购买过或评价过的Item列表:

    Ru = train_data[user_id]

            过滤掉User购买过或评价过的Item后,需要计算候选集中每个Item的打分,通过Item在该User的rating,乘上其相似Item的sim_score,得到最终打分score:

    def recommendation(train_data, user_id, C, k):
        rank = dict() # rank{item:score}
        # 用户user_id有很多个已经购买的item,每个item都有好多个相似item,
        # 每个item取k个相似item
        Ru = train_data[user_id]
        for i, rating in Ru.items():    # 相对于user-based,item-based的一个用户对应于多个item,所以大循环,要遍历item
            for j, sim_score in sorted(C[i].items(), key=lambda x:x[1],reverse=True)[0:k]:
                # 相对于user-based,每个user还要遍历各自的item,item-based则不需要,需要它的矩阵就是item-iten
                # 过滤这个user已经打分过的item
                if j in Ru:
                    continue
                if rank.get(j,-1)==-1:
                    rank[j] = 0
                rank[j] += sim_score*rating
        return rank

    三、根据指定User,得出其推荐Item候选集

    if __name__ == '__main__':
        train_data = dict()
        with open(train_data_path,'r') as ft:
            train_data = eval(ft.read())
     
        C = dict()
        with open(sim_item_path, 'r') as fs:
            C = eval(fs.read())
     
        user_id = '196'
        k = 5
        rank = recommendation(user_id, C, train_data, k)
        print(sorted(rank.items(), key=lambda x: x[1], reverse=True)[0:k])
  • 相关阅读:
    程序员面试金典题解
    Leetcode 解题报告
    常用小算法
    BestCoder 百度之星2016
    jQuery 删除或是清空某个HTML元素。
    dataTable 默认排序设定
    jquery tableExport 插件导出excel (无乱码) 比较简单的表格
    php 根据周数获取当周的开始日期与最后日期
    thinkphp5使用load和use引入第三方类
    判断checkbox是否选中
  • 原文地址:https://www.cnblogs.com/SysoCjs/p/11466767.html
Copyright © 2020-2023  润新知