Python推荐系统学习笔记(2)基于协同过滤的个性化推荐算法实战---ItemCF算法(上)

一、相关概念:

1、关于协同过滤:       

       协同过滤(Collaborative Filtering Recommendation)技术是推荐系统中应用最早和最为成功的技术之一。协同过滤简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐用户感兴趣的信息,个人通过合作的机制给予信息相当程度的回应(如评分)并记录下来以达到过滤的目的进而帮助别人筛选信息,回应不一定局限于特别感兴趣的,特别不感兴趣信息的纪录也相当重要。它一般采用最近邻技术,利用用户的历史喜好信息计算用户之间的距离,然后利用目标用户的最近邻居用户对商品评价的加权评价值来预测目标用户对特定商品的喜好程度,系统从而根据这一喜好程度来对目标用户进行推荐。协同过滤最大优点是对推荐对象没有特殊的要求,能处理非结构化的复杂对象,如音乐、电影。

      协同过滤分为用户协同过滤(User Collaborative Filtering ,UserCF)以及物品协同过滤(Item Collaborative Filtering ,ItemCF)两种形式,简而言之就是:

    (1)UserCF:根据用户的相似性,推荐与目标用户相似的用户所喜好的物品。适用于实时新闻,突发信息推荐。

    (2)ItemCF  :根据物品的相似性,推荐目标用户所喜好的物品所相似的物品。适用于图书,电子商务,电影推荐。

      本文使用ItemCF算法,以电影评价数据为例构建推荐系统。

2、ItemCF相似性度量公式:

    (1)基于评分的相似性度量公式:

            

        sim(i,j) 代表物品 i 和 j 的相似度,U 代表用户的集合,Ru,i 代表用户 u 对物品 i 的评分(Ru,j 同理),上划线 R 代表物品 i 的评分的平均值。

      (2)基于行为(喜好)的相似性度量公式:

          

        S i,j 代表物品 i 和 j 的相似度;u(i) 和 u(j) 代表含有物品 或者物品 j 行为的用户集合;分子表示既行为过 i 又行为过物品 的用户个数的绝对值;分母代表行为过物品 以及行为过物品 j 的用户的数量的乘积的平方根。

       在两种相似性度量公式中,基于评分的相似性度量公式更适用于含有用户评分数据的情景下,基于行为(喜好)的相似性度量公式多用于头条推荐以及减少计算量的情景下。

       此外还有其它计算公式:(x和y分别代表每个物品的来自不同用户的评分)

      (1)欧几里得(欧氏)距离:

            

     (2)皮尔逊相关系数:

             

     (3)Cosine(余弦)相似度:

             

    

3、评分预测公式:

      对物品进行推荐的过程,实际上就是对物品的评分进行预测,将较大预测评分的物品推荐给用户的过程,借助相似性度量结果,可以进行物品评分的预测。

(1)基于评分的ItemCF的评分预测公式:

              

       Pu,i 代表用户 u 对物品 i 的预测评分,N 代表所有与物品 i 相似的物品个体,Ru,N 代表用户 u 对物品的评分,Si,N 代表物品 i 与物品 N 的相似度。

(2)基于喜好的ItemCF的行为得分(喜好程度)预测公式:

                

      是 用户 行为过的物品;Pu,j 代表用户 u 对物品 j 的喜好程度;Sij 是物品i的相似度; rui 是 用户 u 对物品 i 的行为得分度量。

4、ItemCF的优势:

(1)计算性能高,通常用户数量远大于物品数量。

(2)可预先计算保留,物品并不善变。

5、ItemCF存在的问题:

      物品冷启动问题:当平台中物品数据较少或缺失时,无法精确计算物品相似度,解决办法:

    (1)文本分析,通过分析物品的介绍文本,计算相似度。

    (2)主题模型,通过主题模型分析物品文本主题得出主题相似度。

    (3)打标签,对物品打标签求得相似度。

    (4)推荐排行榜单。

二、ItemCF推荐实战:

      本文使用PyCharm为代码编写平台。

1、数据集准备:

      本实例使用MovieLens 数据集(下载地址:http://files.grouplens.org/datasets/movielens/ml-latest-small.zip,或者https://download.csdn.net/download/smart3s/10946693)中的ratings.csv(用户ID对电影ID的评分)以及movies.csv(电影类别明细)。如下:

               

                                  ratings.csv                                                                    movies.csv

2、项目结构:

        

       data文件夹用于存储电影评分数据,production文件夹用于存放推荐代码,util文件夹用于存放用于读取数据的工具文件。

3、reader.py:用于读取用户的点击序列(即每个用户对那些电影进行过评分)以及电影信息(id,名称,类别)。

import os

#获得用户的点击序列
def get_user_click(rating_file):
    #如果路径不存在,返回空数据
    if not os.path.exists(rating_file):
        return {}
    #打开文件
    fp=open(rating_file)
    num=0
    #用于传回的数据
    user_click={}
    #循环数据
    for line in fp:
        #第一行是表头,需要跳过处理
        if num==0:
            num+=1
            continue
        #根据逗号提取每个项目
        item=line.strip().split(',')
        if len(item)<4:
            continue
        [userid,itemid,rating,timestamp]=item
        if float(rating)<3.0:  #如果评分低于3分,则视为该用户不喜欢该电影
            continue
        #将单一用户的点击序列添加至返回数据
        if userid not in user_click:
            user_click[userid]=[]
        user_click[userid].append(itemid)
    fp.close()
    return user_click

#获取电影信息数据
def get_item_info(item_file):
    #若路径不存在则返回空
    if not os.path.exists(item_file):
        return {}
    num=0
    item_info={}
    fp=open(item_file,'r', encoding='UTF-8')
    for line in fp:
        #第一行是表头,需要跳过处理
        if num==0:
            num+=1
            continue
        #根据逗号提取每个项目
        item=line.strip().split(',')
        if len(item)<3: #若单行小于三项过滤(去除问题行)
            continue

        if len(item)==3:
            [itemid,title,genres]=item
        #这个elif语句是由于,有的电影名称中含有逗号,因此造成项数过多,需要另行处理
        elif len(item)>3:
            itemid=item[0]
            genres=item[-1] #获取最后一项
            title=",".join(item[1:-1]) #第一个到最后一个的拼接成为电影名称
        #将电影信息数据返回
        if itemid not in item_info:
            item_info[itemid]=[title,genres]
    fp.close()
    return item_info

4、ItemCF.py: 核心算法文件

(1)模块准备:

import sys
sys.path.append("../util")
import util.reader as reader#导入reader
import math
import operator

(2)主方法:

def main_flow():
    #获取用户的点击序列数据
    user_click=reader.get_user_click("../data/ratings.csv")
    #获取电影信息数据
    item_info=reader.get_item_info("../data/movies.csv")
    #计算各个物品之间的相似度
    sim_info=cal_item_sim(user_click)
    #计算每个用户的推荐(与喜好电影相似度较高的)电影
    recom_result=cal_recom_result(sim_info,user_click)
    #根据用户id推荐电影,此处为“1”
    debug_recom_result(recom_result, item_info,"1")

(3)cal_item_sim方法:计算各个电影间的相似度。

#计算各个电影间的相似度
def cal_item_sim(user_click):
    #相似度数据
    co_appear={}
    #用于统计每个电影的行为用户数量
    item_user_click_time={}
    #循环点击序列数据,user是每用户的id,itemlist是每个用户的点击序列
    for user,itemlist in user_click.items():
        #循环每个用户的点击序列的索引
        for index_i in range(0,len(itemlist)):
            #计算每个item的被用户点击数量
            itemid_i=itemlist[index_i]
            item_user_click_time.setdefault(itemid_i,0) #setdefault方法可以对不存在的键做初值设定(初始化)
            item_user_click_time[itemid_i]+=1

            #计算每个电影id和其他电影id共同出现在一个用户的点击序列中的数值
            for index_j in range(index_i+1,len(itemlist)):
                itemid_j=itemlist[index_j]

                #计算所有电影id中,两两id的共同出现次数
                co_appear.setdefault(itemid_i,{})
                co_appear[itemid_i].setdefault(itemid_j,0)
                co_appear[itemid_i][itemid_j]+=base_contribute_score()#贡献度,默认为1

                co_appear.setdefault(itemid_j,{})
                co_appear[itemid_j].setdefault(itemid_i,0)
                co_appear[itemid_j][itemid_i]+=base_contribute_score()

    #计算相似度
    item_sim_score={}
    item_sim_score_sorted={}
    for itemid_i,relate_item in co_appear.items():
        for itemid_j,co_time in relate_item.items():
            #相似度计算公式
            sim_score=co_time/math.sqrt(item_user_click_time[itemid_i]*item_user_click_time[itemid_j])
            #存储相似度
            item_sim_score.setdefault(itemid_i,{})
            item_sim_score[itemid_i][itemid_j]=sim_score
    #对相似度进行排序
    for itemid in item_sim_score:
        item_sim_score_sorted[itemid]=sorted(item_sim_score[itemid].items(),key=operator.itemgetter(1),reverse=True)
    
    return item_sim_score_sorted

#基础贡献度得分
def base_contribute_score():
    return 1

(4)cal_recom_result方法:计算各个用户的推荐结果

#计算推荐结果的方法
def cal_recom_result(sim_info,user_click):
    #选取用户的前三个点击电影作为电影样本,找寻它们的相似电影进行推荐
    recent_click_num=3
    #取电影样本中电影相似度前topk的电影
    topk=5
    #返回的推荐信息数据
    recom_info={}
     
    for user in user_click:
        #获取每个用户的点击序列
        click_list=user_click[user]
        recom_info.setdefault(user,{})
        #选取用户的前三个点击电影,找寻它们的相似电影进行推荐
        for itemid in click_list[:recent_click_num]:
            if itemid not in sim_info:
                continue
            #找寻与item相似度前topk的电影
            for itemsimzuhe in sim_info[itemid][:topk]:
                itemsimid=itemsimzuhe[0]
                itemsimscore=itemsimzuhe[1]
                recom_info[user][itemsimid]=itemsimscore
    
    return recom_info

(5)debug_recom_result方法:输出设定id的推荐信息:

def debug_recom_result(recom_result,item_info,user_id):
    #判断id是否存在
    if user_id not in recom_result:
        print("invalid result")
        return
    #对排序的推荐信息进行遍历
    for zuhe in sorted(recom_result[user_id].items(),key=operator.itemgetter(1),reverse=True):
        itemid,score=zuhe
        if itemid not in itemid:
            continue
        #输出推荐结果
        print(",".join(item_info[itemid])+"\t"+str(score))

(6)运行itemCF.py:

if __name__=="__main__":
    main_flow()

      结果:输出了与该用户选取的3个电影相似度较高的15(5X3)个电影。

三、参考资料

1、https://www.imooc.com/learn/1029

2、https://www.imooc.com/learn/990

3、https://study.163.com/course/introduction/1004092024.htm

4、https://blog.csdn.net/yimingsilence/article/details/54934302

5、https://blog.csdn.net/xiaokang123456kao/article/details/74735992

6、项亮. 推荐系统实践[M]. 人民邮电出版社, 2012.

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值