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使用Srapy框架爬虫模拟登陆并抓取知乎内容
Jul 02 Python
浅谈Python2、Python3相对路径、绝对路径导入方法
Jun 22 Python
pytorch训练imagenet分类的方法
Jul 27 Python
transform python环境快速配置方法
Sep 27 Python
对Python3+gdal 读取tiff格式数据的实例讲解
Dec 04 Python
Python Pandas数据结构简单介绍
Jul 03 Python
python日志模块logbook使用方法
Sep 19 Python
python使用socket 先读取长度,在读取报文内容示例
Sep 26 Python
python selenium实现发送带附件的邮件代码实例
Dec 10 Python
python+selenium+PhantomJS抓取网页动态加载内容
Feb 25 Python
python 使用xlsxwriter循环向excel中插入数据和图片的操作
Jan 01 Python
浅谈Python从全局与局部变量到装饰器的相关知识
Jun 21 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
smarty基础之拼接字符串的详解
2013/06/18 PHP
php获取中文拼音首字母类和函数分享
2014/04/24 PHP
js中有关IE版本检测
2012/01/04 Javascript
addEventListener()第三个参数useCapture (Boolean)详细解析
2013/11/07 Javascript
把jQuery的类、插件封装成seajs的模块的方法
2014/03/12 Javascript
jquery实现滑动特效代码
2015/08/10 Javascript
深入剖析JavaScript中的函数currying柯里化
2016/04/29 Javascript
原生JS实现图片左右轮播
2016/12/30 Javascript
Angular+Bootstrap+Spring Boot实现分页功能实例代码
2017/07/21 Javascript
node实现分片下载的示例代码
2018/10/17 Javascript
vue 中基于html5 drag drap的拖放效果案例分析
2018/11/01 Javascript
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
2018/11/05 Javascript
新手入门带你学习JavaScript引擎运行原理
2019/06/24 Javascript
Python自动化测试工具Splinter简介和使用实例
2014/05/13 Python
Python+Pika+RabbitMQ环境部署及实现工作队列的实例教程
2016/06/29 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
2018/07/25 Python
Django中使用Celery的教程详解
2018/08/24 Python
Python拼接字符串的7种方法总结
2018/11/01 Python
Python面向对象总结及类与正则表达式详解
2019/04/18 Python
Python调用scp向服务器上传文件示例
2019/12/22 Python
浅谈PyTorch的可重复性问题(如何使实验结果可复现)
2020/02/20 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
Python通过递归函数输出嵌套列表元素
2020/10/15 Python
方太官方网上商城:销售方太抽油烟机、燃气灶、消毒柜等
2017/01/17 全球购物
Kathmandu澳洲户外商店:新西兰户外运动品牌
2017/11/12 全球购物
美国婚礼装饰和活动用品批发供应商:Event Decor Direct
2018/10/12 全球购物
新西兰优惠网站:Treat Me
2019/07/04 全球购物
老教师工作总结的自我评价
2013/09/27 职场文书
数据管理员的自我评价分享
2013/11/15 职场文书
党员学习十八大感想
2014/01/17 职场文书
致裁判员加油稿
2014/02/08 职场文书
辩论赛主持词
2014/03/18 职场文书
开工仪式主持词
2014/03/20 职场文书
共产党员公开承诺书
2014/03/25 职场文书
离婚协议书格式
2015/01/26 职场文书
财务会计个人原因辞职信
2019/06/21 职场文书