Python基于机器学习方法实现的电影推荐系统实例详解


Posted in Python onJune 25, 2019

推荐算法在互联网行业的应用非常广泛,今日头条、美团点评等都有个性化推荐,推荐算法抽象来讲,是一种对于内容满意度的拟合函数,涉及到用户特征和内容特征,作为模型训练所需维度的两大来源,而点击率,页面停留时间,评论或下单等都可以作为一个量化的 Y 值,这样就可以进行特征工程,构建出一个数据集,然后选择一个合适的监督学习算法进行训练,得到模型后,为客户推荐偏好的内容,如头条的话,就是咨询和文章,美团的就是生活服务内容。

可选择的模型很多,如协同过滤,逻辑斯蒂回归,基于DNN的模型,FM等。我们使用的方式是,基于内容相似度计算进行召回,之后通过FM模型和逻辑斯蒂回归模型进行精排推荐,下面就分别说一下,我们做这个电影推荐系统过程中,从数据准备,特征工程,到模型训练和应用的整个过程。

我们实现的这个电影推荐系统,爬取的数据实际上维度是相对少的,特别是用户这一侧的维度,正常推荐系统涉及的维度,诸如页面停留时间,点击频次,收藏等这些维度都是没有的,以及用户本身的维度也相对要少,没有地址、年龄、性别等这些基本的维度,这样我们爬取的数据只有打分和评论这些信息,所以之后我们又从这些信息里再拿出一些统计维度来用。我们爬取的电影数据(除电影详情和图片信息外)是如下这样的形式:

Python基于机器学习方法实现的电影推荐系统实例详解

这里的数据是有冗余的,又通过如下的代码,对数据进行按维度合并,去除冗余数据条目:

# 处理主函数,负责将多个冗余数据合并为一条电影数据,将地区,导演,主演,类型,特色等维度数据合并
def mainfunc():
 try:
  unable_list = []
  with connection.cursor() as cursor:
   sql='select id,name from movie'
   cout=cursor.execute(sql)
   print("数量: "+str(cout))

   for row in cursor.fetchall():
    #print(row[1])
    movieinfo = df[df['电影名'] == row[1]]
    if movieinfo.shape[0] == 0:
     disable_movie(row[0])
     print('disable movie ' + str(row[1]))
    else:
     g = lambda x:movieinfo[x].iloc[0]
     types = movieinfo['类型'].tolist()
     types = reduce(lambda x,y:x+'|'+y,list(set(types)))
     traits = movieinfo['特色'].tolist()
     traits = reduce(lambda x,y:x+'|'+y,list(set(traits)))
     update_one_movie_info(type_=types, actors=g('主演'), region=g('地区'), director=g('导演'), trait=traits, rat=g('评分'), id_=row[0])
       
  connection.commit()
 finally:
  connection.close()

之后开始准备用户数据,我们从用户打分的数据中,统计出每一个用户的打分的最大值,最小值,中位数值和平均值等,从而作为用户的一个附加属性,存储于userproex表中:

'insert into userproex(userid, rmax, rmin, ravg, rcount, rsum, rmedian) values(\'%s\', %s, %s, %s, %s, %s, %s)' % (userid, rmax, rmin, ravg, rcount, rsum, rmedium)
'update userproex set rmax=%s, rmin=%s, ravg=%s, rmedian=%s, rcount=%s, rsum=%s where userid=\'%s\'' % (rmax, rmin, ravg, rmedium, rcount, rsum, userid)

以上两个SQL是最终插入表的时候用到的,代表准备用户数据的最终步骤,其余细节可以参考文末的github仓库,不在此赘述,数据处理还用到了一些SQL,以及其他处理细节。

系统上线运行时,第一次是全量的数据处理,之后会是增量处理过程,这个后面还会提到。

我们目前把用户数据和电影的数据的原始数据算是准备好了,下一步开始特征工程。做特征工程的思路是,对type, actors, director, trait四个类型数据分别构建一个频度统计字典,用于之后的one-hot编码,代码如下:

def get_dim_dict(df, dim_name):
 type_list = list(map(lambda x:x.split('|') ,df[dim_name]))
 type_list = [x for l in type_list for x in l]
 def reduce_func(x, y):
 for i in x:
  if i[0] == y[0][0]:
  x.remove(i)
  x.append(((i[0],i[1] + 1)))
  return x
 x.append(y[0])
 return x
 l = filter(lambda x:x != None, map(lambda x:[(x, 1)], type_list))
 type_zip = reduce(reduce_func, list(l))
 type_dict = {}
 for i in type_zip:
 type_dict[i[0]] = i[1]
 return type_dict

涉及到的冗余数据也要删除

df_ = df.drop(['ADD_TIME', 'enable', 'rat', 'id', 'name'], axis=1)

将电影数据转换为字典列表,由于演员和导演均过万维,实际计算时过于稀疏,当演员或导演只出现一次时,标记为冷门演员或导演

movie_dict_list = []
for i in df_.index:
 movie_dict = {}
 #type
 for s_type in df_.iloc[i]['type'].split('|'):
 movie_dict[s_type] = 1
 #actors
 for s_actor in df_.iloc[i]['actors'].split('|'):
 if actors_dict[s_actor] < 2:
  movie_dict['other_actor'] = 1
 else:
  movie_dict[s_actor] = 1
 #regios
 movie_dict[df_.iloc[i]['region']] = 1
 #director
 for s_director in df_.iloc[i]['director'].split('|'):
 if director_dict[s_director] < 2:
  movie_dict['other_director'] = 1
 else:
  movie_dict[s_director] = 1
 #trait
 for s_trait in df_.iloc[i]['trait'].split('|'):
 movie_dict[s_trait] = 1
 movie_dict_list.append(movie_dict)

使用DictVectorizer进行向量化,做One-hot编码

v = DictVectorizer()
X = v.fit_transform(movie_dict_list)

这样的数据,下面做余弦相似度已经可以了,这是特征工程的基本的一个处理,模型所使用的数据,需要将电影,评分,用户做一个数据拼接,构建训练样本,并保存CSV,注意这个CSV不用每次全量构建,而是除第一次外都是增量构建,通过mqlog中类型为'c'的消息,增量构建以comment(评分)为主的训练样本,拼接之后的形式如下:

USERID cf2349f9c01f9a5cd4050aebd30ab74f
movieid 10533913
type 剧情|奇幻|冒险|喜剧
actors 艾米·波勒|菲利丝·史密斯|理查德·坎德|比尔·哈德尔|刘易斯·布莱克
region 美国
director 彼特·道格特|罗纳尔多·德尔·卡门
trait 感人|经典|励志
rat 8.7
rmax 5
rmin 2
ravg 3.85714
rcount 7
rmedian 4
TIME_DIS 15

这个数据的actors等字段和上面的处理是一样的,为了之后libfm的使用,在这里需要转换为libsvm的数据格式

dump_svmlight_file(train_X_scaling, train_y_, train_file)

模型使用上遵循先召回,后精排的策略,先通过余弦相似度计算一个相似度矩阵,然后根据这个矩阵,为用户推荐相似的M个电影,在通过训练好的FM,LR模型,对这个M个电影做偏好预估,FM会预估一个用户打分,LR会预估一个点击概率,综合结果推送给用户作为推荐电影。

模块列表

  • recsys_ui: 前端技术(html5+JavaScript+jquery+ajax)
  • recsys_web: 后端技术(Java+SpringBoot+mysql)
  • recsys_spider: 网络爬虫(python+BeautifulSoup)
  • recsys_sql: 使用SQL数据处理
  • recsys_model: pandas, libFM, sklearn. pandas数据分析和数据清洗,使用libFM,sklearn对模型初步搭建
  • recsys_core: 使用pandas, libFM, sklearn完整的数据处理和模型构建、训练、预测、更新的程序
  • recsys_etl:ETL 处理爬虫增量数据时使用kettle ETL便捷处理数据

为了能够输出一个可感受的系统,我们采购了阿里云服务器作为数据库服务器和应用服务器,在线上搭建了电影推荐系统的第一版,地址是:

www.technologyx.cn

可以注册,也可以使用已有用户:

用户名 密码
gavin 123
gavin2 123
wuenda 123

欢迎登录使用感受一下。

Python基于机器学习方法实现的电影推荐系统实例详解

设计思路

Python基于机器学习方法实现的电影推荐系统实例详解

用简单地方式表述一下设计思路,

1.后端服务recsys_web依赖于系统数据库的推荐表‘recmovie'展示给用户推荐内容

2.用户对电影打分后(暂时没有对点击动作进行响应),后台应用会向mqlog表插入一条数据(消息)。

3.新用户注册,系统会插入mqlog中一条新用户注册消息

4.新电影添加,系统会插入mqlog中一条新电影添加消息

5.推荐模块recsys_core会拉取用户的打分消息,并且并行的做以下操作:

a.增量的更新训练样本
b.快速(因服务器比较卡,目前设定了延时)对用户行为进行基于内容推荐的召回
c.训练样本更新模型
d.使用FM,LR模型对Item based所召回的数据进行精排
e.处理新用户注册消息,监听到用户注册消息后,对该用户的属性初始化(统计值)。
f.处理新电影添加消息,更新基于内容相似度而生成的相似度矩阵

注:

由于线上资源匮乏,也不想使系统增加复杂度,所以没有直接使用MQ组件,而是以数据库表作为代替。
项目源码地址: https://github.com/GavinHacker/recsys_core

模型相关的模块介绍

增量的处理用户comment,即增量处理评分模块

这个模块负责监听来自mqlog的消息,如果消息类型是用户的新的comment,则对消息进行拉取,并相应的把新的comment合并到总的训练样本集合,并保存到一个临时目录

然后更新数据库的config表,把最新的样本集合(csv格式)的路径更新上去

运行截图

Python基于机器学习方法实现的电影推荐系统实例详解

消息队列的截图

Python基于机器学习方法实现的电影推荐系统实例详解

把csv处理为libsvm数据

这个模块负责把最新的csv文件,异步的处理成libSVM格式的数据,以供libFM和LR模型使用,根据系统的性能确定任务的间隔时间

运行截图

Python基于机器学习方法实现的电影推荐系统实例详解

基于内容相似度推荐

当监听到用户有新的comment时,该模块将进行基于内容相似度的推荐,并按照电影评分推荐

运行截图

Python基于机器学习方法实现的电影推荐系统实例详解

libFM预测

http://www.libfm.org/

对已有的基于内容推荐召回的电影进行模型预测打分,呈现时按照打分排序

如下图为打分更新

Python基于机器学习方法实现的电影推荐系统实例详解

逻辑回归预测

对样本集中的打分做0,1处理,根据正负样本平衡,> 3分为喜欢 即1, <=3 为0 即不喜欢,这样使用逻辑回归做是否喜欢的点击概率预估,根据概率排序

Python基于机器学习方法实现的电影推荐系统实例详解

项目源码地址: https://github.com/GavinHacker/recsys_core

Python 相关文章推荐
Python中基本的日期时间处理的学习教程
Oct 16 Python
Python使用matplotlib和pandas实现的画图操作【经典示例】
Jun 13 Python
pandas的唯一值、值计数以及成员资格的示例
Jul 25 Python
Django 路由控制的实现代码
Nov 08 Python
dataframe 按条件替换某一列中的值方法
Jan 29 Python
Python如何实现转换URL详解
Jul 02 Python
python3字符串操作总结
Jul 24 Python
pytorch torchvision.ImageFolder的用法介绍
Feb 20 Python
Python打印特殊符号及对应编码解析
May 07 Python
python根据用户需求输入想爬取的内容及页数爬取图片方法详解
Aug 03 Python
python exit出错原因整理
Aug 31 Python
用 python 进行微信好友信息分析
Nov 28 Python
Python 中的参数传递、返回值、浅拷贝、深拷贝
Jun 25 #Python
pyqt5 删除layout中的所有widget方法
Jun 25 #Python
在Python中表示一个对象的方法
Jun 25 #Python
Python设置matplotlib.plot的坐标轴刻度间隔以及刻度范围
Jun 25 #Python
浅谈PyQt5 的帮助文档查找方法,可以查看每个类的方法
Jun 25 #Python
PyQt5根据控件Id获取控件对象的方法
Jun 25 #Python
PyQt5组件读取参数的实例
Jun 25 #Python
You might like
PHP代码加密的方法总结
2020/03/13 PHP
js刷新框架子页面的七种方法代码
2008/11/20 Javascript
jQuery 树形结构的选择器
2010/02/15 Javascript
JQuery开发的数独游戏代码
2010/10/29 Javascript
SOSO地图API使用(一)在地图上画圆实现思路与代码
2013/01/15 Javascript
Js日期选择器并自动加入到输入框中示例代码
2013/08/02 Javascript
jQuery遍历对象、数组、集合实例
2014/11/08 Javascript
JavaScript中对象介绍
2014/12/31 Javascript
jqGrid翻页时数据选中丢失问题的解决办法
2017/02/13 Javascript
Vue 2.0+Vue-router构建一个简单的单页应用(附源码)
2017/03/14 Javascript
react.js 获取真实的DOM节点实例(必看)
2017/04/17 Javascript
JavaScript中一些特殊的字符运算
2017/08/17 Javascript
vue登录路由验证的实现
2017/12/13 Javascript
Angular6封装http请求的步骤详解
2018/08/13 Javascript
JavaScript读写二进制数据的方法详解
2018/09/09 Javascript
vue cli使用融云实现聊天功能的实例代码
2019/04/19 Javascript
微信小程序使用GoEasy实现websocket实时通讯
2020/05/19 Javascript
js模拟实现百度搜索
2020/06/28 Javascript
使用Python快速制作可视化报表的方法
2019/02/03 Python
如何用C代码给Python写扩展库(Cython)
2019/05/17 Python
python requests指定出口ip的例子
2019/07/25 Python
Python 实现平台类游戏添加跳跃功能
2020/03/27 Python
Python函数参数分类原理详解
2020/05/28 Python
施华洛世奇日本官网:SWAROVSKI日本
2018/05/04 全球购物
俄罗斯最大的灯具网站:Fandeco
2020/03/14 全球购物
ECCO俄罗斯官网:北欧丹麦鞋履及皮具品牌
2020/06/26 全球购物
秋季运动会加油稿200字
2014/01/11 职场文书
创先争优制度
2014/01/21 职场文书
社区食品安全实施方案
2014/03/28 职场文书
环境工程专业毕业生求职信
2014/09/30 职场文书
2014乡镇领导班子四风对照检查材料思想汇报
2014/10/05 职场文书
老公保证书怎么写
2015/02/26 职场文书
同乡会致辞
2015/07/30 职场文书
企业愿景口号
2015/12/25 职场文书
Python卷积神经网络图片分类框架详解分析
2021/11/07 Python
Python使用pandas导入xlsx格式的excel文件内容操作代码
2022/12/24 Python