从零开始写一个推荐系统第二篇,推荐电影

1. 准备数据

上一篇文章讲解了两种计算相似度的方法,理论部分就已经准备完毕了。实践部分,考验的是你的工程能力。首先,我们需要准备一份数据,这份数据我们没办法自己去造,自己造的数据不真实,没有实践的意义。可供与实战的数据有很多,很多研究机构和大学都会公开实验的数据,本文选择使用ml-100k数据集,它是一份电影评分的数据集,我这里提供一份百度网盘的下载地址: https://pan.baidu.com/s/1KWe7gmsFoEHvDd0W762fcA 提取码:6skh

下载后解压,找到u.data文件,这个文件里的数据有四列

196	242	3	881250949
186	302	3	891717742
22	377	1	878887116

依次是用户id,电影id,评分,时间,我们只用前3列数据。

2. 读取u.data

需要编写一个函数打开u.data文件读取里面的数据,数据保存为字典,格式如下所示

{
    '196': {
        '242': 3,
        '323': 4
    },
    '166': {
        '242': 4,
        '213': 5
    }
}

首先用user_id做key,value为这个用户对电影的评分信息,value中以moive_id做key,评分做score, 这样存储,是为了后续计算的方便,函数实现如下

def get_user_score_info():
    """
    读取u.data 文件,获得用户对电影的打分信息,文件里有4列,我们只要前3列
    用户id  电影id   评分
    :return:
    """
    user_score_info = {}
    with open('./u.data', 'r')as f:
        for line in f:
            array = line.split()
            user_id = array[0]
            movie_id = array[1]
            score = int(array[2])
            user_score_info.setdefault(user_id, {})
            user_score_info[user_id][movie_id] = score

    return user_score_info

3. 计算一个人和其他人的相似度

选定一个人,作为base,计算他与其他人的相似度,如果俩俩之间计算相似度,计算量太大了,我最终想要完成的函数,一次只为一个人推荐电影。

在根据电影评分计算两个人的相似度时,我设置了一个前提条件,这两个人打过分的电影,至少有5个相同的电影,不然可能会出现这样的情形,两个人打分电影里只有一个是相同的,而且分数一致,不论采用哪种算法,他们的相似度都很高,可这样的相似度不具备参考价值。

实现函数can_calc_similarity, 计算两个人的评分数据是否可以计算相似度。

def get_same_movie(score_dict_1, score_dict_2):
    """
    获取两人看过的相同的电影id
    :param score_dict_1:
    :param score_dict_2:
    :return:
    """
    set_1 = set([movie for movie in score_dict_1])
    set_2 = set([movie for movie in score_dict_2])

    intersection_set = set_1.intersection(set_2)
    return intersection_set


def can_calc_similarity(score_dict_1, score_dict_2, same_movie_count=5):
    """
    判断两个人是否有必要计算相似度,如果两人评价的电影里,相同的电影数量超过same_movie_count 才进行计算
    :param score_dict_1:
    :param score_dict_2:
    :param same_movie_count:
    :return:
    """
    intersection_set = get_same_movie(score_dict_1, score_dict_2)
    return len(intersection_set) >= same_movie_count

接下来实现函数calc_similarity,它接收一个user_id参数,计算这个用户与其他用户的相似度

import math
import copy
import numpy as np
from rich.console import Console
from rich.table import Table

def calc_person_distance(score_dict_1, score_dict_2):
    """
    计算两人对电影评价的相似度
    :param score_dict_1:
    :param score_dict_2:
    :return:
    """
    intersection_set = get_same_movie(score_dict_1, score_dict_2)
    score_lst_1 = [score_dict_1[movie_id] for movie_id in intersection_set]
    score_lst_2 = [score_dict_2[movie_id] for movie_id in intersection_set]
    return sim_distance(score_lst_1, score_lst_2)


def calc_similarity(base_user_id):
    user_score_info = get_user_score_info()
    base_score_dict = user_score_info[base_user_id]

    similarity_lst = []
    for user_id, score_dict in user_score_info.items():
        if user_id == base_user_id:
            continue
        if not can_calc_similarity(base_score_dict, score_dict):
            continue
        similarity = calc_person_distance(base_score_dict, score_dict)
        similarity_lst.append((user_id, similarity))

    similarity_lst.sort(key=lambda x: x[1], reverse=True)
    head_5 = similarity_lst[:5]
    show_detail(base_user_id, user_score_info, head_5)
    
def sim_distance(lst1, lst2):
    """
    计算欧几里得距离
    :param lst1:
    :param lst2:
    :return:
    """
    sum_value = 0
    for x1, x2 in zip(lst1, lst2):
        sum_value += pow(x1 - x2, 2)
    return 1/(1+math.sqrt(sum_value))

if __name__ == '__main__':
    calc_similarity('196')

计算好相似度后,按照从大到小进行排序,取出相似度最高的前5名用户,作为电影推荐的参考用户,这一步,我为了查看具体信息,编写了函数show_detail,展示这5个用户与base用户对电影的评价情况,测试时,选择用户196, 实验效果如图
在这里插入图片描述

def show_detail(user_id, user_score_info, similarity_lst):
    base_score_dict = user_score_info[user_id]
    all_moive_set = set()
    for item in similarity_lst:
        intersection_set = get_same_movie(base_score_dict, user_score_info[item[0]])
        all_moive_set = all_moive_set.union(intersection_set)

    console = Console()
    table = Table(show_header=True, header_style="bold magenta", show_lines=True)
    table.add_column('user_id')
    table.add_column('相似度')
    for moive_id in all_moive_set:
        table.add_column(moive_id)

    similarity_lst_coyp = copy.deepcopy(similarity_lst)
    similarity_lst_coyp.insert(0, (user_id, ''))
    for id, similarity in similarity_lst_coyp:
        value_lst = [id, str(similarity)]
        for moive_id in all_moive_set:
            value_lst.append(str(user_score_info[id].get(moive_id, '')))

        table.add_row(*value_lst)

    console.print(table)

4. 实现推荐

已经从成千上万的用户中找到了电影品味和id为196的用户十分接近的5个用户,接下来,要根据这5位用户对电影的评分来为196推荐电影。直接从这5个用户中选择一个为196做推荐是不可行的,我们需要总和这5个人的评分情况来做推荐,具体算法如下。

  1. 对于一部电影,用相似度乘评分,得到的是一个加权的评分,这个评分更能反映出这个评论者对电影的评价推196的影响。
  2. 将这5个人对于一部电影加权后的评分累加在一起,得到的结果为socre_sum
  3. 计算这5个人与196的相似度之和,得到的结果为similarity_sum
  4. 计算socre_sum/similarity_sum , 这个结果作为这这一部影的推荐值,它考虑到了每一个人对电影的评分,也考虑到了每一个人和196的品味相似程度,更加客观,如果一部电影只有4个人评价,计算方法不变,缺失的评分视为0

算法实现如下

def calc_similarity(base_user_id):
    user_score_info = get_user_score_info()
    base_score_dict = user_score_info[base_user_id]

    similarity_lst = []
    for user_id, score_dict in user_score_info.items():
        if user_id == base_user_id:
            continue
        if not can_calc_similarity(base_score_dict, score_dict):
            continue
        similarity = calc_person_distance(base_score_dict, score_dict)
        similarity_lst.append((user_id, similarity))

    similarity_lst.sort(key=lambda x: x[1], reverse=True)
    head_5 = similarity_lst[:5]
    show_detail(base_user_id, user_score_info, head_5)
    recommend(base_user_id, user_score_info, head_5)


def recommend(base_user_id, user_score_info, head_5):
    diff_moive_set = set()      # 所有base_user_id 没看过的电影
    for user_id, similarity in head_5:
        diff_moive = get_diff_moive(user_score_info[base_user_id], user_score_info[user_id])
        diff_moive_set = diff_moive_set.union(diff_moive)

    recommend_lst = []
    for moive_id in diff_moive_set:
        moive_socre_sum = 0
        similarity_sum = 0
        user_score_count = 0
        for user_id, similarity in head_5:
            if moive_id in user_score_info[user_id]:
                user_score_count += 1
                moive_socre_sum += user_score_info[user_id][moive_id]*similarity
                similarity_sum += similarity

        if user_score_count < 3:
            continue
        recommend_lst.append((moive_id, moive_socre_sum/similarity_sum))

    recommend_lst.sort(key=lambda x: x[1], reverse=True)
    recommend_lst = recommend_lst[:5]
    print(recommend_lst)
    show_recommend_detail(user_score_info, recommend_lst, head_5)

def show_recommend_detail(user_score_info, recommend_lst, similarity_lst):
    console = Console()
    table = Table(show_header=True, header_style="bold magenta", show_lines=True)
    table.add_column('user_id')
    table.add_column('相似度')
    for moive_id, _ in recommend_lst:
        table.add_column(moive_id)

    for user_id, similarity in similarity_lst:
        value_lst = [user_id, str(similarity)]
        for moive_id, _ in recommend_lst:
            value_lst.append(str(user_score_info[user_id].get(moive_id, '')))

        table.add_row(*value_lst)

    console.print(table)

def get_diff_moive(base_score, score_dict_2):
    set_1 = set([movie for movie in base_score])
    set_2 = set([movie for movie in score_dict_2])

    return set_2.difference(set_1)

这部分代码并不复杂,get_diff_moive函数返回两个人点评分的差异化部分,我们需要找到196用户没有评价过的电影,然后计算这些电影的推荐分数,在计算电影推荐分数时,只计算那些至少有3个人评价过的电影,这3个人已经是品味和196很相似了,他们又都看过这部电影,才有推荐价值。如果一部电影只有5个人中的一个人看过,说明这部电影不是很受这5个人喜欢,5个人超过半数的人都喜欢的电影才有推荐意义。

程序最终的输出如下图
在这里插入图片描述

最终,本系统为196用户推荐了5部电影。这是一个非常简单的推荐系统,它的算法核心是基于用户的,是基于用户的协同过滤推荐,当用户数量并不大时,它可以胜任推荐任务,但当用户数量非常大时,就会遇到性能问题。当你想为196推荐电影时,你不得不计算他和所有用户的相似度,如果所有用户的总量为一千万,其计算量会非常的大。

想要解决海量用户下的推荐,可以使用基于物品的协同过滤算法,关于这个算法,我将在下一篇文章继续和大家分享。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 构建企业级推荐系统需要考虑很多方面,包括数据收集、存储和处理、特征工程、模型选择和训练、实时推荐等。以下是一些基本步骤: 1. 数据收集和存储:收集用户行为数据,如用户点击、购买、评分等,存储在大数据平台中,如Hadoop、Spark等。 2. 特征工程:从原始数据中提取有用的特征,如用户行为、用户信息、商品信息等,为后续的模型训练做准备。 3. 模型选择和训练:选择适合自己场景的推荐算法,如协同过滤、内容推荐、混合推荐等,利用训练数据进行模型训练。 4. 实时推荐:将训练好的模型署到实时推荐系统中,为用户提供个性化推荐服务。 在实际构建过程中,还需要考虑数据安全、性能优化、AB测试等问题。同时,还需要不断优化算法,提高推荐质量和效果。 ### 回答2: 从零开始构建企业级推荐系统一个庞大且复杂的过程,成功地完成这项任务需要经过以下几个步骤。 首先,需要进行需求分析和问题定义。明确企业对推荐系统的需求,例如是要为用户提供个性化的产品推荐,还是为客户提供相关的广告推荐等。同时,也需要了解业务流程和数据特点,以便为推荐系统的设计和开发做好准备。 其次,要进行数据采集和处理。将企业的所有数据汇总,包括用户信息、商品信息、交互行为等,并进行清洗和预处理。这些数据将作为推荐系统的基础。 然后,需要选择和构建推荐算法。推荐算法是推荐系统的核心,可以选择常见的协同过滤、内容过滤、基于图的推荐等算法。根据实际需求和数据特点,可以选择单一算法或多种算法的组合进行推荐。 接下来,进行模型训练和优化。使用历史数据训练推荐模型,并通过实验和评估来不断优化模型的性能。可使用交叉验证、A/B测试等方法来评估模型的准确性和效果。 最后,将推荐系统上线和署。将训练好的模型署到生产环境中,并进行系统集成和性能测试。还需要与其他业务系统进行对接,确保推荐系统的稳定性和实时性。 总结来说,要从零开始构建企业级推荐系统,需要明确需求、进行数据处理、选择合适的算法、训练和优化模型,最后将系统上线。当然,实际的建设过程中还会遇到各种挑战和问题,需要不断学习和改进来提高推荐系统的准确性和用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值