电影推荐系统

什么是推荐系统

推荐系统是在面对用户信息过载的情况下,或者在用户没有明确购买信息的情况下,通过程序或工具自动化实现物品或商品的自动推荐过程。(而不依赖外部专家)
为了能够给用户推荐感兴趣的新奇,我们首先分析该用户的兴趣,从海量信息中选择出用户兴趣相似的信息,并讲这些信息推荐给用户。
推荐系统的任务就是能够连接信息与用户,帮助用户找到其感兴趣的信息,同时让一些有价值的信息能够到达潜在的用户中。

推荐系统一般由哪几部分?

推荐系统一般有三个部分组成,前端的交互界面,日志系统以及推荐算法系统。

推荐系统的应用场景特点是什么?

(1)、存在信息过载的情况,用户不能很容易从所有物品中找到喜欢的物品。
(2)、用户大部分时候没有特别明确的需求

推荐系统的应用举例?

(1)、电子商务:亚马逊,京东,淘宝,天猫…
(2)、电影视频网站:Netflix, YouTube…
(3)、个性化音乐网络电台:豆瓣电台,喜马拉雅,网易音乐…
(4)、个性化广告:百度搜索引擎…

用到的推荐算法

一、简述或举例子说明基于内容的推荐算法
基于内容的推荐算法,原理使用户喜欢和自己关注item在内容上类似item,比如你看了哈利波特,基于内容的推荐算法发现哈利波特ll-VI,与你之前观看的在内容上面(共有很多关键词)有很大关联性,就把后者推荐给你。

二、简述或举例说明基于模型的推荐算法
该类型的推荐算法是通过预先设定的计算模型来实现推荐,常常用于实时推荐,相比其他离线推荐算法可以缩短推荐实现,本项目中实时推荐算法通过根据具体业务构建了想要的推荐模型来实现推荐。

三、简述或举例说明基于用户的协同过滤算法
比如你想看一个电影,但是不知道具体看哪一部,你会怎么做?有两种办法,一种是问问周围兴趣爱好相似的朋友,另外看看电影的相似程度。
协同过滤算法就是基于上面的思想,主要包含基于用户的协同过滤推荐算法以及基于物品的协同过滤推荐算法。

四、一般协同过滤算法,需要几个步骤?
(1)、收集用户偏好
(2)、找到相似的用户或者物品
(3)、计算推荐

五、基于用户的CF伪代码实现
电影推荐系统
六、基于物品的CF伪代码实现
(1)、先计算物品-物品的相似矩阵
电影推荐系统
(2)、再给用户推荐
电影推荐系统

推荐系统架构

一、简述或者手写系统的架构图,并叙述各部分的功能
电影推荐系统

  • 用户可视化:主要负责实现和用户的交互以及业务数据的展示,主体采用AngularJS2进行实现,部署在Apache服务上。代码实现为website模块
  • 综合业务服务:主要实现JavaEE层面整体的业务逻辑,通过Spring进行构建,对接业务需求。部署在Tomcat上。代码实现为businessServer模块
  • 【数据存储部分】代码实现为recommend.dataloader模块
    • 业务数据库:项目采用广泛应用的文档数据库MongDB作为主数据库,主要负责平台业务逻辑数据的存储。
    • 搜索服务器:项目采用ElasticSearch作为模糊检索服务器,通过利用ES强大的匹配查询能力实现基于内容的推荐服务。
    • 缓存数据库:项目采用Redis作为缓存数据库,主要用来支撑实时推荐系统部分对于数据的高速获取需求。
  • 【离线推荐部分】
    • 离线统计服务:批处理统计性业务采用Spark Core + Spark SQL进行实现,实现对指标类数据的统计任务。代码实现为recommend.staticsrecommend模块
    • 离线推荐服务:离线推荐业务采用Spark Core + Spark MLlib进行实现,采用ALS算法进行实现。代码实现为recommend.offlinerecommend模块
    • 工作调度服务:对于离线推荐部分需要以一定的时间频率对算法进行调度,采用Azkaban进行任务的调度。
  • 【实时推荐部分】代码实现为recommend.streamrecommend+recommend.kafkastream模块
    • 日志采集服务:通过利用Flume-ng对业务平台中用户对于电影的一次评分行为进行采集,实时发送到Kafka集群。
    • 消息缓冲服务:项目采用Kafka作为流式数据的缓存组件,接受来自Flume的数据采集请求。并将数据推送到项目的实时推荐系统部分。
    • 实时推荐服务:项目采用Spark Streaming作为实时推荐系统,通过接收Kafka中缓存的数据,通过设计的推荐算法实现对实时推荐的数据处理,并将结构合并更新到MongoDB数据库。

推荐系统数据模型及数据流

一、推荐系统的数据集数据模型有哪些,具体以什么格式存储的

  • Movie【电影数据表】
    Movie数据集有10个字段,原始数据每个字段之间通过“^”符号进行分割。
    字段名 字段类型 字段描述 字段备注
    mid Int 电影的ID
    name String 电影的名称
    descri String 电影的描述
    timelong String 电影的时长
    shoot String 电影拍摄时间
    issue String 电影发布时间
    language Array[String] 电影语言 每一项用“|”分割
    genres Array[String] 电影所属类别 每一项用“|”分割
    director Array[String] 电影的导演 每一项用“|”分割
    actors Array[String] 电影的演员 每一项用“|”分割
  • Rating【用户评分表】
    Rating数据集有4个字段,原始数据每个字段之间通过“,”分割
    字段名 字段类型 字段描述 字段备注
    uid Int 用户的ID
    mid Int 电影的ID
    score Double 电影的分值
    timestamp Long 评分的时间
  • Tag【电影标签表】
    数据集有4个字段,原始数据每个字段之间通过“,”分割
    字段名 字段类型 字段描述 字段备注
    uid Int 用户的ID
    mid Int 电影的ID
    tag String 电影的标签
    timestamp Long 评分的时间
  • User【用户表】
    字段名 字段类型 字段描述 字段备注
    uid Int 用户的ID
    username String 用户名
    password String 用户密码
    first boolean 用于是否第一次登录
    genres List 用户偏爱的电影类型
    timestamp Long 用户创建的时间
  • RateMoreMoviesRecently【最近电影评分个数统计表】
    字段名 字段类型 字段描述 字段备注
    mid Int 电影的ID
    count Int 电影的评分数
    yeahmonth String 评分的时段 201507
  • RateMoreMovies【电影评分个数统计表】
    字段名 字段类型 字段描述 字段备注
    mid Int 电影的ID
    count Int 电影的评分数
  • AverageMoviesScore【电影平均评分表】
    字段名 字段类型 字段描述 字段备注
    mid Int 电影的ID
    avg Double 电影的平均评分
  • MovieRecs【电影相似性矩阵】
    字段名 字段类型 字段描述 字段备注
    mid Int 电影的ID
    recs Array[(mid:Int,score:Double)] 该电影最相似的电影集合
  • UserRecs【用户电影推荐矩阵】
    字段名 字段类型 字段描述 字段备注
    uid Int 用户的ID
    recs Array[(mid:Int,score:Double)] 推荐给该用户的电影集合
  • StreamRecs【用户实时电影推荐矩阵】
    字段名 字段类型 字段描述 字段备注
    uid Int 用户的ID
    recs Array[(mid:Int,score:Double)] 实时推荐给该用户的电影集合
  • GenresTopMovies【电影类别TOP10】
    字段名 字段类型 字段描述 字段备注
    genres String 电影类型
    recs Array[(mid:Int,score:Double)] TOP10电影

二、电影推荐项目数据流是怎样的?
电影推荐系统
【系统初始化部分】

  1. 通过Spark SQL将系统初始化数据加载到MongoDB和ElasticSearch中。

【离线推荐部分】

  1. 通过Azkaban实现对于离线统计服务以离线推荐服务的调度,通过设定的运行时间完成对任务的触发执行。测试环境中直接执行即可
  2. 离线统计服务从MongoDB中加载数据,将【电影平均评分统计】、【电影评分个数统计】、【最近电影评分个数统计】三个统计算法进行运行实现,并将计算结果回写到MongoDB中;离线推荐服务从MongoDB中加载数据,通过ALS算法分别将【用户推荐结果矩阵】、【影片相似度矩阵】回写到MongoDB中。

【实时推荐部分】

  1. Flume从综合业务服务的运行日志中读取日志更新,并将更新的日志实时推送到Kafka中;Kafka在收到这些日志之后,通过kafkaStream程序对获取的日志信息进行过滤处理,获取用户评分数据流【UID|MID|SCORE|TIMESTAMP】,并发送到另外一个Kafka队列;Spark Streaming监听Kafka队列,实时获取Kafka过滤出来的用户评分数据流,融合存储在Redis中的用户最近评分队列数据,提交给实时推荐算法,完成对用户新的推荐结果计算;计算完成之后,将新的推荐结构和MongDB数据库中的推荐结果进行合并。

【业务系统部分】

  1. 推荐结果展示部分,从MongoDB、ElasticSearch中将离线推荐结果、实时推荐结果、内容推荐结果进行混合,综合给出相对应的数据。
  2. 电影信息查询服务通过对接MongoDB实现对电影信息的查询操作。
  3. 电影评分部分,获取用户通过UI给出的评分动作,后台服务进行数据库记录后,一方面将数据推动到Redis群中,另一方面,通过预设的日志框架输出到Tomcat中的日志中。
  4. 项目通过ElasticSearch实现对电影的模糊检索。
  5. 电影标签部分,项目提供用户对电影打标签服务。

推荐系统计算部分

一、数据加载时怎样进行的,核心方法是哪一个?
MongoDB数据加载部分时,首先读取cvs格式数据为RDD,将其转换为DF,然后写入到MongeDB对应数据表中,核心方法为storeDataInMongo

ES数据加载时,会将之前加载的Movie和Tag数据集合并,产生新的电影数据集,这是考虑到用户可以使用标签字段来检索数据。核心方法为storeMoiveDataInES

二、离线统计部分统计那些数据,数据是怎么处理的?

  1. 统计所有历史数据中电影的评分个数
    1. 通过Rating数据集,用mid进行group by操作,count计算总数,写入到RateMoreMovies表中
  2. 统计以月为单位的电影的评分个数
    1. 需要注册一个UDF函数,用于将Timestamp这种格式的数据转换成 yyyyMM 这个格式,simpleDateFormat
    2. 需要将RatingDF装换成新的RatingOfMouthDF【只有日期数据发生了转换】
    3. 通过group by 年月,mid 来完成统计
    4. 将数据写入到RateMoreMoviesRecently表中
  3. 统计每个电影评分平均得分
    1. 通过Rating数据集,用户mid进行group by操作,avg计算评分
    2. 将数据写入到AverageMoviesScore
  4. 统计每种类别中评分最高的10个电影
    1. 需要通过JOIN操作将电影的平均评分数据和Movie数据集进行合并,产生MovieWithScore数据集
    2. 需要将电影的类别数据转换成RDD,GenresRDD
    3. 将GenresRDD和MovieWithScore数据集进行笛卡尔积,产生一个 N * M行的数据集。
    4. 通过过滤操作,过滤掉电影的真是类别和GenresRDD中的类别不匹配的电影
    5. 通过Genres作为Key,进行groupByKey操作,将相同电影类别的电影进行聚集。
    6. 通过排序和提取,获取评分最高的10个电影
    7. 将结果输出到MongoDB中GenresTopMovies表中
      2 3 4 过程可以通过spark的函数 explode 来替代完成
      电影推荐系统
      三、离线CF推荐部分推荐那些数据,具体步骤是是什么?
  • 计算用户推荐矩阵
  • 计算电影相似度矩阵
    具体流程:
  1. 训练ALS推荐模型
    1. 需要构建RDD[Rating]类型的训练数据
    2. 直接通过ALS.train方法来进行模型训练
  2. 计算用户推荐矩阵
    通过ALS训练出来的Model来计算所有当前用户电影的推荐矩阵,主要思路如下:
    1. UserId和MovieID做笛卡尔积,产生(uid,mid)的元组
    2. 通过模型预测(uid,mid)的元组。
    3. 将预测结果通过预测分值进行排序。
    4. 返回分值最大的K个电影,作为当前用户的推荐。
      最后生成的数据结构如下:将数据保存到MongoDB的UserRecs表中
  3. 计算电影相似度矩阵
    1. 获取电影的特征矩阵,转换成DoubleMatrix
    2. 电影的特征矩阵之间做笛卡尔积,通过余弦相似度计算两个电影的相似度
    3. 将数据通过GroupBy处理后,输出到MovieRec中
  4. ALS模型的参数选择
    1. 通过计算ALS的均方根误差来判断参数的优劣程度

四、实时推荐主要解决的需求是什么?
对于实时推荐算法,主要有两点需求:

(1)用户本次评分后、或最近几个评分后系统可以明显的更新推荐结果;

(2)计算量不大,满足响应时间上的实时或者准实时要求;

五、实时推荐算法的前提或者数据集来自哪里?

  1. 在Redis集群中存储了每一个用户最近对电影的K次评分。实时算法可以快速获取。
  2. 离线推荐算法已经将电影相似度矩阵提前计算到了MongoDB中。
  3. Kafka已经获取到了用户实时的评分数据。

六、实时推荐计算过程是怎样的?
电影推荐系统
算法过程如下:

实时推荐算法输入为一个评分<userId, movieId, rate, timestamp>,而执行的核心内容包括:获取userId 最近K 次评分、获取movieId 最相似K 个电影、计算候选电影的推荐优先级、更新对userId 的实时推荐结果。

七、什么是冷启动问题,该项目如何解决的?
整个推荐系统更多的是依赖于用于的偏好信息进行电影的推荐,那么就会存在一个问题,对于新注册的用户是没有任何偏好信息记录的,那这个时候推荐就会出现问题,导致没有任何推荐的项目出现。这就是冷启动问题。

处理这个问题一般是通过当用户首次登陆时,为用户提供交互式的窗口来获取用户对于物品的偏好。该项目同样使用这种方法,获取用户偏好并写入数据库。

推荐项目后台业务&前端

一、推荐系统的后台架构是怎样的?
电影推荐系统
后台服务通过Spring框架进行创建,主要负责后台数据和前端业务的交互。项目主要分为REST接口服务层、业务服务层、业务模型以及工具组件等。

REST接口服务层,主要通过Spring MVC为UI提供了通讯接口,主要包括用户接口、推荐接口、评分接口、查询接口、标签接口以及统计接口。

服务层主要实现了整体系统的业务逻辑,提供了包含电影相对应操作的服务、评分层面的服务、推荐层面的服务、标签层面的服务以及用户层面的服务。

业务模型方面,将推荐、业务请求以及具体业务数据进行模型创建。

工具组件层面,提供了对Redis、ES、MongoDB的客户端以及项目常量定义。

二、几个REST接口层的http请求

  • http://ip:port/rest/users/register?username=username&password=password
  • http://ip:port//rest/movie/guass?username-username&num-num

三、前后端是如何数据交互的?
采用前后端分离的架构,前端通过向后端发起http的get请求,后台通过REST接口接收请求,调用service层的方法实现业务逻辑以及获取数据,最终返回到前端页面,前端页面获取数据之后,对数据进行展示。最终要获取及展示的数据是电影数据,同时会访问图片服务器获取电影对应的图片进行展示。