基于内容(content_based)的电影推荐系统实战

基于内容(content_based)的电影推荐系统实战

本文思想和代码基于https://coding.m.imooc.com/classindex.html?cid=297 该课程写成

个人代码参考: https://github.com/yuanzhiqj/recom

介绍

基于内容的推荐系统是根据用户的之前的偏好,计算推荐项目中与用户偏好相似度最高的项目,推荐给用户。

下面通过一个具体的电影推荐系统的示例来初步了解它

输入数据

给定两个输入文件,rating.txt和movies。分别代表用户的评分文件和电影的列表。截取部分数据解释如下:

rating.txt

userIdmovieIdratingtimestamp
1312.51260759144

userid: 用户编号

movid: 电影编号

rating:评分

timestamp:时间戳,越大表示越近时间评分

movie.txt

movietitlegenres
1Toy Story (1995)AAdventure|Animation|Children|Comedy|Fantasy

move:电影编号

title:电影名称

genres:电影分类,以’|'号分隔

处理数据

处理数据是比较麻烦的一部分,我们把它分为几步

  • 获得电影的平均得分
  • 获得一部电影的种类
  • 得到一个种类下的电影排名,评分从高到低
  • 获得最近的时间戳

获得平均得分

通过输入ratings.txt文件,返回一个包含所有电影平均分的字典score

record: 临时存数电影得分和评分次数的字典。

def get_avg(input_file):
    """
    获取平均分
        args: 
            input_file:输入文件
        return:
            一个字典,key:itemid value:avg
    """
    record = {}
    score = {}
    if not os.path.exists(input_file):
        print("no path")
        return {}
    linenum = 0
    fp = open(input_file)
    for line in fp:
        if linenum == 0:
            linenum += 1
            continue
        item = line.strip().split(",")
        if len(item) < 4:
            continue
        userid, itemid, rating = item[0],item[1],float(item[2])
        if itemid not in record:
            record[itemid] = [0,0]
        record[itemid][0] += rating
        record[itemid][1] += 1
    fp.close()
    for itemid in record:
        score[itemid] = round(record[itemid][0]/record[itemid][1],3)
    return score

获得电影的种类

假如一部电影有多个分类,比如同时属于冒险,动作。那么它们分别占0.5的比例。

即分别拥有1/n的权重,n为电影所属的种类数。

用字典item_cata存储

获得一个种类下排名前100的电影列表

在完成上两步的操作后,我们得到了两个字典:

  • 存储电影平均分的字典score
  • 存储电影种类的字典item_cast

我们先创建一个临时字典record,该字典存储着如下信息:

value:电影种类

key:a dict{value: 电影名称, key: 平均分} #即一个嵌套字典。

对该临时字典排序,得到一个新的有序字典cate_item_sort

for cate in record:
	if cate not in cate_item_sort:
    	cate_item_sort[cate] = []
    for combo in sorted(record[cate].items(),key=operator.itemgetter(1),reverse=True)[:topk]:
    	cate_item_sort[cate].append(combo[0]+"_"+str(combo[1]))

它的键值对为:value: 电影种类, key: 电影名称。按照评分从高到底排序

def get_item_cate(input_file, avg_score):
    """
    得到不同种类的电影的评分排序 和 同一电影的种类
    """
    record = {}
    item_cate = {}
    cate_item_sort = {}
    linenum = 0
    topk = 100
    if not os.path.exists(input_file):
        return {},{}
    fp = open(input_file)
    for line in fp:
        if linenum == 0:
            linenum += 1
            continue
        item = line.strip().split(",")
        if(len(item) < 3):
            continue
        itemid = item[0]
        cate_str = item[-1]
        cate_list = cate_str.strip().split("|")
        ratio = round(1/len(cate_list),3)
        if itemid not in item_cate:
            item_cate[itemid] = {}
        for fix_cate in cate_list:
            item_cate[itemid][fix_cate] = ratio
    fp.close()

    for itemid in item_cate:
        for cate in item_cate[itemid]:
            if cate not in record:
                record[cate] = {}
            itemid_rating_score = avg_score.get(itemid,0)
            record[cate][itemid] = itemid_rating_score
    for cate in record:
        if cate not in cate_item_sort:
            cate_item_sort[cate] = []
        for combo in sorted(record[cate].items(),key=operator.itemgetter(1),reverse=True)[:topk]:
            cate_item_sort[cate].append(combo[0]+"_"+str(combo[1]))
    return item_cate, cate_item_sort

获得最近的时间戳

这一步的目的是获得值最大的时间戳,方便我们与其它的时间戳进行比较。得到用户最近时间内的评分。在该文件中,最大时间戳为1476086345。

def get_latest_timestamp(input_file):
    """
    Args:
        input_file:user rating file
    only need run once
    """

    if not os.path.exists(input_file):
        return
    linenum = 0
    latest = 0
    fp = open(input_file)
    for line in fp:
        if linenum == 0:
            linenum += 1
            continue
        item = line.strip().split(",")
        if len(item) < 4:
            continue
        timestamp = int(item[3])
        if timestamp > latest:
            latest = timestamp
    fp.close()
    print(latest)

获得用户偏好

数据处理完后,在对用户推荐之前,我们需要获得用户之前的偏好信息。步骤如下:

  1. 得到每个用户评分4.0以上电影的信息。
  2. 获得这些电影的种类。
  3. 判定用户对这些种类的喜好程度,进行打分,并累加。

分值=用户评分 * 时间权重 * 种类占比。

比如用户给a电影打了4分,时间权重为0.1,该电影动作类占比0.5,则对动作类的喜好程度上升0.2。

  1. 得到用户最喜欢的前两个种类。
def get_up(item_cate, input_file):
    linenum = 0
    if not os.path.exists(input_file):
        return {}
    record = {}
    up = {}
    score_thr = 4.0
    topk = 2
    fp = open(input_file)
    for line in fp:
        if linenum == 0:
            linenum += 1
            continue
        item = line.strip().split(",")
        if(len(item) < 4):
            continue
        userid,itemid,rating,time = item[0],item[1],float(item[2]),int(item[3])
        if rating < score_thr:
            continue
        if itemid not in item_cate:
            continue
        time_score = get_time_score(time)
        if userid not in record:
            record[userid] = {}
        for fix_cate in item_cate[itemid]:
            if fix_cate not in record[userid]:
                record[userid][fix_cate] = 0
            record[userid][fix_cate] += rating * time_score * item_cate[itemid][fix_cate]
    fp.close()
    for userid in record:
        if userid not in up:
            up[userid]=[]
        total = 0
        for combo in sorted(record[userid].items(),key=operator.itemgetter(1),reverse=True)[:topk]:
            up[userid].append((combo[0],combo[1]))
            total += combo[1]
        for index in range(len(up[userid])):
            up[userid][index] = (up[userid][index][0],round(up[userid][index][1]/total,3))
    return up

最后一步:进行推荐

获得用户id后,我们只需查询用户偏好的种类,按照此种类查找cate_item_sort字典获得排名靠前的电影即可。

def recom(cate_item_sort,up,userid,topk=10):   
    if userid not in up:
        return {}
    recom_result = {}
    if userid not in recom_result:
        recom_result[userid] = []
    for zuhe in up[userid]:
        cate = zuhe[0]
        ratio = zuhe[1]
        num = int(topk*ratio) + 1
        if cate not in cate_item_sort:
            continue
        recom_list = cate_item_sort[cate][:num]
        recom_result[userid] += recom_list
    return  recom_result
  • 8
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值