基于协同过滤算法的电影资源个性化推荐系统
摘要
在互联网时代下,信息量呈指数级增长。海量数据以 0、1 方式编码存放在磁盘等设备中非常方便,但是对于人类来说,我们的脑容量有限,在海量数据中挑选出适合自己的信息变得非常困难。简直如大海捞针,即使使用了分类方法和搜索引擎,我们还是要花大量时间去挑选适合自己的信息,并且经常选取了错误的信息。在这样的情况下,个性化推荐系统应运而生。从起初的基于内容的推荐,到现在最常用的协同过滤推荐等推荐算法,我们从繁琐庞大的信息中解脱出来,可以接受大量系统推荐给我们的信息。 在大多数电子商务网站上,都运行着个性化推荐系统,不仅提高了用户购买率,还提高了用户体验。其系统内部实现由于基础数据不同也各有不同,而协同过滤算法被广泛应用到这些系统中,发挥着巨大的作用。
本文首先详细介绍了协同过滤算法与推荐系统概要,分析了各种协同过滤算法的优缺点。然后设计了一个简易的电影资源推荐系统,并使用 python、微信公众号等技术实现了这个推荐系统,最后对这个推荐系统做了简单的测试和结果分析。
关键词 :个性化推荐系统;协同过滤;机器学习;数据挖掘;数据分析;python
一、绪论
电子商务网站中,推荐系统广泛存在。典型的,亚马逊商城出现的“您可能需要购买”或者“购买过此类商品的人还购买过”等字样的推荐商品,打开淘宝商城直接呈现在你眼前的商品,都是推荐系统针对用户个人产生的推荐。包括非电商网站也会有非常多的推荐广告,如百度搜索引擎侧边的广告,网上博客侧边的广告。这些广告经常会引起我们的注意,因为它们也产生于推荐系统,是推荐系统为用户“量身制定”的广告。如果没有这些推荐,或者推荐出来的信息都是随便推荐的,那可能起不到广告作用,反而会引起用户的反感,造成用户流失。
1.1 课题背景及意义
世纪九十年代以来,电子商务在国内外蓬勃发展,网上浏览信息已经成为人们主要获取信息的途径,网上购物也成为人们的主要消费行为。而网上信息的爆炸式增长,海量数据呈现在用户面前,使用户无所适从,用户如何方便快捷地找到自己需要的商品变得尤为重要[1]。在这种情况下,依赖用户反馈信息的推荐系统应运而生,而且得到了越来越广泛的应用。
首先,推荐系统解决了“信息过载”的额问题,网上信息数量繁多,重复率也极高,如何快速找到适合自己的信息,成为互联网的一大关注点。推荐系统利用用户反馈信息,预测用户的喜好,实时向用户推荐适合的信息,从而提高网络服务的质量,增加企业网站竞争力。
其次,推荐系统可以大规模产生推荐,当今用户对产品和服务的品质要求越来越高,每个用户都希望能够找到符合自己期望的个性化和多样化服务,这时候靠人工实现推荐显然效率低下,而推荐系统能够实现高效、实时、大规模的推荐。
再者,推荐系统可以评估推荐效果,收集用户反馈信息,通过知识挖掘技术,能够获取相应的知识,在客户管理、市场分析等领域非常有用。
总体来说,互联网上的大多数网站为推荐系统的应用提供了平台,而推荐系统的推
荐效果也在快速提升。一方面,互联网的发展,特别是电子商务领域,在金融的推动下,电子商务为推荐系统的应用发展提供了广阔的平台,推动推荐算法的发展。另一方面,推荐系统节约了用户浏览查找适合信息的时间,提高了用户的体验。
二、协同过滤与相关技术
2.1 推荐算法介绍
2.1.1 基于内容的推荐算法
基于内容的推荐一种传统的推荐方法,它依靠内容的特征将内容分类,并推荐给可能感兴趣的用户。这种方法实现简单,在系统的整个生命周期中都能使用。在后面的章节中将使用此类方法弥补协同过滤算法的数据稀疏问题。
基于内容的推荐是在信息检索领域提出的,该算法可以使用的场景非常多,其基本思想是利用用户喜欢的对象之间的相关性而做出推荐。推荐时首先为每一个推荐对象抽取出一些特征来表示此对象,例如抽取文章的关键词,然后利用一个用户过去的喜好记录,及喜不喜欢这个关键词相关的内容,然后给出该用户的推荐内容。
例如信息分类的电影网站,如果你喜欢看喜剧,则基于内容的推荐算法的推荐结果就是喜剧电影;如果你喜欢看某个明星主演的电影,则它的推荐结果就是这个明星主演的其他电影。
基于内容推荐有以下几个优点:
- 推荐容易理解:只考虑单个用户的喜好和内容之间的匹配程度。推荐的内容属于用户关注的范畴。
- 用户具有独立性:用户与用户之间的数据不存在关联,用户只和数据相关。
- 无新推荐对象冷启动问题:当增加新的内容时,可以迅速提取出该内容的特征,而不会出现数据稀少使算法无法做出推荐。
该算法也存在一些缺点:
- 推荐对象的特征提取能力有限:文字内容非常容易提取出关键词,但是如果是影视频或图片则无法提取内容特征。
- 无法发现用户的潜在兴趣:该算法只依赖用户过去的兴趣产生推荐,而某些特征的内容用户现在不感兴趣是因为不知道它的存在,基于内容的推荐不会产生此类推荐结果。
- 推荐内容重复性较高:提取的特征不能完全代表主体内容,无法识别两个内容的相似程度。
2.1.2 协同过滤算法
协同过滤最早的应用是 1992 年 Xerox 公司在 Palo Alto 的研究中心解决资讯过载的问题。它比基于内容的推荐算法更有效。
传统的基于内容推荐,例如把最近被搜索频率较高的电影推荐给每一个用户;或者从用户浏览的文章中提取出关键词,根据关键词推荐;或者用户最近观看科幻片比较多,便把评分较高的科幻片推荐给用户。这些方法在特定的场景也有显著的效果,但是这些算法都简单的根据单个用户的信息作出预测,而没有考虑到用户与用户之间的相关关系。而协同过滤算法则“考虑的比较周全”。将这部分信息也当做预测的原数据。根据历史数据来建立模型,预测出用户喜欢却还没有得到的信息,推荐给用户,产生价值。现在协同过滤算法应用广泛,特别是电子商务领域的商品推荐,最著名的电子商务推荐系统应属亚马逊商城的图书推荐系统,用户查看自己感兴趣的书籍,马上会在底下看到 “买了这本书籍的用户还购买了”的字样。亚马逊是在“对同样一本书有兴趣的读者们兴趣在某种程度上相近”的假设前提下提供这样的推荐,此举也成为亚马逊网络书店为人所津津乐道的一项服务,各网络书店也跟进做这样的推荐服务。
协同过滤的出发点是:兴趣相近的人可能会对同样的东西感兴趣。只要根据用户历史喜好数据,分析出相似爱好的用户,然后就能根据相似用户的意见或者评价进行推荐。
2.2 相关技术介绍
在本文的推荐系统搭建在安装 Linux 操作系统的云服务器(Elastic Compute Service 简称 ECS)上。除了核心的推荐算法,还需要以下技术支持:
- python :一门被广泛应用的编程语言,有很多不同功能的第三方库可以使用。调用这些第三方库,简单的 python 代码可以实现强大的功能:网页爬虫、神经网络、、图像识别、文章分词及关键字提取、网站后台等等。本系统中使用 python 作为主要开发语言,使用 python 爬取源数据,并使用 python 编写后台逻辑、与数据库交互等。
- MySQL :一个免费的关系数据库软件,当拥有大量记录,并且记录与记录之间有复杂的联系时,把这些信息直接存到文件系统是不适合的,存到非关系数据库又体现不出记录之间的复杂关系,比较好的选择就是使用 MySQL, MySQL 是关系数据库中比较流行的一个。在本系统中使用 MySQL 数据库存储电影资源的信息、用户的信息、用户的行为信息。
- 微信公众号 :作为一个 C/S 架构的服务,开发一个客户端软件所需要的时间太长,使用 HTML 开发一个网站前端也需要写大量代码,由于本系统对前端界面需求不大,为了节省时间,本文使用腾讯公司的微信公众号作为后台接入点,将微信聊天界面当成用户界面使用,这样避免了编写前端代码,用户也可以使用微信轻松的访问系统。
- Web.py :一个使用 python 语言的轻量级网站后台框架,使用这个框架可以简单的实现网站后台,与微信公众号对接,作为公众号的后台,提供用户数据采集功能和推荐计算服务。
除了上述的主要技术,在系统实现阶段还会用到一些其他技术,例如:使用 pythond 的第三方库 requests 下载 HTML 页面内容、使用 Xpath 语法解析 HTML 页面内容、使用 pymysql 库操作 MySQL 等等。
三、系统分析与设计
3.1 需求分析
本文将利用上一章中介绍的技术搭建一个电影资源个性化推荐系统。本系统对用户提供一个微信公众号,微信用户对公众号添加关注后就能享受到电影个性化推荐服务。
在用户使用推荐系统时,会用到几个功能:
- 个性化推荐功能:这是用户所需要的服务,也是推荐系统的必要功能。
- 评价功能:用户可以对看过的电影进行一个评价,这份数据将作为推荐算法做抉择的依据。
- 搜索功能:一个基本的功能,能让用户准确搜索出电影的详细信息。
除了上述的必要功能,用户可能还会用到一些其他的扩展功能:
- 说明书:怎么与公众号进行交互,让系统进行相关的操作,把这个写成说明书给用户看。
- 收藏夹:用户对于喜欢的电影可以添加到收藏夹,以免忘记。
在使用该系统时,用户可以主动向公众号发出一些命令,然后公众号完成相应的功能,可能还需要返回结果内容。
-
推荐功能可以将内容主动推荐给用户,也可以在用户使用系统时显示出推荐结果,也可以让用户主动寻求推荐。由于本系统使用微信公众号,功能有限,所以使用的方法是用户主动寻求推荐,当用户发送“推荐”二字给系统时,系统做出推荐结果返回给用户。
-
评价功能可以在用户购买商品后进行评价,或者在观看电影后对电影进行评价,但是由于本系统没有购物系统,只是简单的推荐系统,所以这个功能也是用户主动评价,用户可以对电影进行一个 0-10 分的评价。系统将记录这个评分,用来作为个性推荐的依据。
-
搜索功能则比较简单,用户直接发送电影名,系统将电影详细信息返回给用户。
其他功能对推荐系统的作用不是很大,本文没有做出具体实现。
3.2 系统设计
首先需要准备工作环境,工作环境非常简单,一台电脑作为后台,一个微信公众号作为前端。电脑需要拥有公网 IP,这样微信公众号才能接入到自己编写的后台程序,最好的选择就是购买一个云服务器,配置不需要高。在本系统中使用安装了 CentOS 操作系统的云服务器,微信公众号使用微信申请个人微信公众号。整体结构如下图所示:
图 3-1 系统结构
在实现推荐系统时,我们需要两个重要的数据资源。一是用户需要的信息资源,在本系统中就是电影资源;二是用户信息资源,用户的信息和用户的历史评价记录。将这些数据存放在 MySQL 数据库里面供推荐程序读取和写入。有了这些基本的数据,我们的算法才能跑起来,它会根据用户的历史评价记录,作出新的推荐。由于算法根据历史数据作出推荐,所以数据的可靠性和数据量能够极大的影响推荐效果。
3.2.1 微信公众号
图 3-2 微信公众号申请
在微信公众号官网上,可以注册以下几种账号,如图 3-2。而这里只需要简单
的注册订阅号即可。个人订阅号虽然功能权限较少,但是只需要提供个人信息就能申请注册。个人订阅号能够每天群发一条消息到用户,能够在用户主动发送文字、图片或语音时回应消息[5]。在该系统中,这些权限已经足够。
根据注册流程,很容易就能得到一个个人订阅号,每一个微信用户扫描此订阅号的
二维码或者直接搜索订阅号名称就能关注此订阅号,但此时的公众号没有任何功能。
3.2.2 接口定义
当订阅号申请完成之后,用任意微信就能搜索关注,如图 3-3。
图 3-3 微信订阅号信息
有了微信公众号后,就相当于有了用户的交互界面。当用户发送文字请求时,微信公众号回复消息。现在给微信公众号定义以下操作接口:命令 参数 1 参数 2 …
用户只要打开微信公众号发送这样格式的文字,微信公众号就给出相应回复。
定义以下几种操作:
- 搜索电影信息:
搜索 电影名
- 评价电影:
评价 电影名 分数
- 请求个性化推荐:
推荐
- 简单搜索:
电影名
当微信公众号服务器收到用户发来的请求时,会转发到我们的后台,然后在我们的后台处理后返回结果,最终结果返回到用户界面。
图 3-4 用户交互界面
用户操作如图 3-4 所示,由于现在后端还没有开发,所以现在用户发送请求时得不到返回结果,微信会提示服务出现故障。这些功能的实现将在下一节内说明。现在只说明这些接口的作用,最简单的,用户可能会主动了解一些电影的信息,为此,提供两个命令:
“搜索 电影名”或者直接发送“电影名”。然后公众号返回改电影的详情信息。还要获取用户的喜好信息,所以提供了“评价 电影名 评分”命令,在用户评价电影时将会在后台采集该信息作为协同过滤算法的数据依据。最后提供“推荐”命令让用户获取个性化推荐的结果。由于个人订阅号的权限有限,只能被动响应用户的请求,所以在本系统中所有的功能都靠用户主动发送命令触发。而发送命令只需要打开微信公众号输入文字即可。
3.2.3 前后端交互接口
在微信公众号平台上开启开发者模式,并设置一个令牌,然后在开发者设置的基本设置中,指定一台服务器,并启用服务器配置,如图 3-5 所示,这样设置之后,当用户发送消息时,微信公众号服务器将收到消息并封装成 XML 格式的数据转发到指定的后台服务器的 80 端口,然后等待此服务器回复后转发到用户微信。本节对微信公众号与后台服务的交互接口进行说明。
图 3-5 配置公众号接入后台服务器
3.2.3 文本请求
当用户发送“推荐”两个字到公众号时,公众号服务器会以 POST 的方式发出如下 XML 格式的文本信息给我们指定的服务器的 80 端口。内容如图 3-6 所示,内容包含了公众号 ID、用户微信 ID、发送时间、消息类型、消息内容以及一个消息 ID。
图 3-6 微信请求格式
当后台服务接收到该信息之后,我们只需要将发送方名字和接收方名字的位置交换,然后将 Content 内容替换成后台程序处理的结果,发送到微信公众号服务器,经过公众号服务器的处理和转发,用户便能收到回复。
在本系统中,只处理用户发送的文字请求,不处理图片和语音。
3.2.3 事件处理
除了文本请求,还有一类通知叫做事件,当有新用户关注公众号或者老用户取消关注时,公众号服务器会封装相应的 XML 内容发送给后台服务器。例如,图 3-7 为新用户关注公众号时,公众号服务器发送给后台服务器的内容。
图 3-7 关注事件
当收到新关注时,系统将发送系统的使用方法给新用户,并记录用户信息;当收到取消关注通知时,系统将对用户的信息做一些清理工作。
3.2.3 Web 服务器
微信公众号开发者配置成功之后,搭建后台服务器。数据库和推荐程序都放在同一台机器上,服务监听 80 端口等待微信公众号服务器发来 POST 请求,然后操作数据库做相应操作后返回结果。
如本章图 3-1 所示架构,我们只需要搭建一个 Web 服务器,监听 80 端口,当有用户关注微信公众号、发送文字,发送图片等操作时,微信公众号服务器就会将这些操作封装成 XML 格式的数据发送到我们自己搭建的 Web 服务器,Web 服务器处理后返回一段 XML 格式的内容给微信公众号服务器,微信公众号服务器再将此回复转成相应的文字或者图片等内容发送给用户。在这个过程中,发送请求的是用户,处理请求的是 Web 服务器,微信公众号服务器只是做了一个转发的功能。而数据库和协同过滤算法就放在 Web 后台服务器上面,当用户发来请求时作相应的处理后返回信息给用户。
web.py 是一个开源的轻量级 python Web 框架[6]。使用 python 就能进行简单的开发,完成这个系统中需要的功能,监听 80 端口,与微信公众号服务器进行简单的交互,及收发 XML 数据。 安装 web.py:
- web.py 官网(http://webpy.org/)下载压缩包到本地解压。
- 进入解压后的文件夹执行 python setup.py install 安装。
安装完成后,在 python 脚本中使用 import Web 语句就能引入 web.py 框架。web.py 是一个框架,可以将它看成一个 Web 服务器,在引入它之后,只需要专心编写与微信公众号服务器交互的代码,不需要关心消息如何经过 TCP/IP 协议栈、如何到达对端。
只需要简单的几行代码就能实现一个 Web 服务器,如图 3-8,在 urls 中指定 Web 的默认访问路径为根目录,指定自定义类型 Main 来处理 HTTP 请求。在 Main 中定义两种请求方式的回调函数以及返回内容。最后运行 web.application()和 run()方法。
图 3-8 使用 web.py
编写完成后保存为 main.py 文件,增加可执行权限。并在命令行运行:
c++
./main.py 80
因为微信公众号服务器只会连接 Web 服务器的 80 端口,所以必须监听 80 端口微信公众号服务器才能连接成功,另外 80 端口需要拥有操作系统管理员权限才能监听。
然后在浏览器输入主机 IP 地址或者域名地址,便能得到 Web 后台返回的信息,如图 3-9 的例子,Web 后台主机 IP 地址为 211.159.176.139。
图 3-9 浏览器访问 Web 后台
相应地,在 Web 后端也能看到请求处理日志,如图 3-10。
图 3-10 Web 后端日志
3.3 数据库设计
3.3.1 电影信息表
图 3-11 电影信息表
图 3-11 是一张存放电影信息的 MySQL 表格,每一条记录是一场电影的信息,每一场电影有一个唯一的 ID 字段用来在系统中标识唯一一场电影,因为电影名相同的电影可能不是同一场电影,如图 3-12。
图 3-12 重名电影
《西游记》就有很多个版本。为了区分开这些重名电影,只能使用电影详情页 URL
来作为关键字,但是 URL 太长了,不方便程序使用,所以使用 ID 值标识每一部电影。
3.3.2 其他表结构
具体的操作还需要依靠数据库来达到大量数据的长期存储。数据库中,除了电影信息表,还需要增加关于用户的表格。当新的用户添加关注时,需要将用户的信息存入数据库的用户表;当用户评价电影时,需要更新该用户的喜好信息。
针对用户的“搜索”、“评价”、“推荐”三个接口,新增加三个表:用户信息表、用户搜索记录表、用户评分表。在收到新用户关注事件,将用户的信息保存到用户信息表,并赋唯一 ID 标识每个用户,如图 3-13。当用户搜索电影时,保存用户的搜索记录。当用户评价电影时,保存用户的评价记录。保存这些记录都是为“推荐”功能提供数据支持。
三个新表的结构非常简单,如图 3-13,每个表有 3 个字段。
图 3-13 用户相关表
3.4 推荐算法设计
如果有如图 3-14 中的用户评分数据,针对每一个用户的历史数据,可以为每一个用户绘制一条二维曲线,横坐标为不同的电影,纵坐标为每一场电影的评分,如图 3-15。
图 3-14 用户历史评分
从图中可以看出,如果是喜好相似的两个用户,他们的曲线起伏波动会是相近的,甚至可能是重叠的。仔细看这分数据能够看出,用户 1 和用户 5 的曲线是最相近的,所以可以将他们标记为有着共同爱好的用户,也就是互为邻居,当我们为一个用户找到一个或者多个邻居之后,就可以将这个用户的邻居喜欢的内容推荐给该用户。
图 3-15 用户喜好曲线
本系统中的协同过滤算法使用这样的原理,不断为每一个用户找到喜好邻居,然后将邻居的喜好推荐给该用户。对于两条曲线,它们之间所围成的面积越小则越相近,也表示这两个用户有共同的兴趣爱好。
上述算法非常简单实用,但是数据库中的用户喜好数据往往是稀疏的,对于同一场电影不可能每个人都对它进行过评分。所以这个算法在满数据的情况下非常实用,但是在数据稀疏的情况下则不是那么有效。当然现实场景中也不需要数据全满,如果数据全满则意味着两个用户看过的电影是相同的,则无法推荐一些用户没有看过的电影给用户。
四、系统实现
4.1 数据采集
豆瓣网上有大量的电影信息,每次听到电影名时,我总是会上豆瓣网查看电影的评分,然后决定自己要不要去观看。本文的数据来源也豆瓣电影。
在该推荐系统中,要做的就是将用户可能喜欢的电影信息反馈给用户,但是不包含电影本身。提供给用户的信息只能保证用户依靠该信息能够找到相应的电影资源,而系统也不提供下载资源的方法,只提供电影资源的名称和详情 URL 等。
4.1.1 爬虫概要
豆瓣电影的网址为:“https://movie.douban.com/tag/ 爱 情?start=0&type=T”
我们只需要将以上 URL 中的“爱情”分别替换成“科幻”、“喜剧”、“励志”、“文艺”等词就能看到分好类的电影信息的第一页内容。
图 4-1 目录页首页 URL
例如,图 4-1 中 url_1 的内容放在浏览器中就能访问到图 4-2 的页面。
图4-2 目录页内容
而点击目录页首页页面最底下的页码就能翻页浏览该类型所有电影信息。图 4-2 滑到页面底部就是图 4-3 的内容。
图4-3 目录页页底的页码
爬虫需要做的工作就是代替我们人工去翻页浏览所有电影信息,并将这些信息保存
到数据库。我们的爬虫程序会依次访问每一个类型的电影列表,并从第一页翻到最后一页,把每页展示的电影信息存入数据库的表中。
这里我使用 python 的以下第三方库如图 4-4:urllib、requests、lxml、pymysql。
图 4-4 python 爬虫使用的库
使用 time 和 random 库只是为了在爬取的每两个页面之间插入一个随机时间间隔。
控制访问豆瓣服务器的速度,以防影响到豆瓣服务器正常运行被禁止访问。
核心的函数有以下几个:
- 使用 page = requests.get(URL)方法能够获取到 URL 指定的 HTML 网页文件存入 page 中;
- 使用 selector = lxml.etree.HTML(page)方法能够将名为 page 的 HTML 页面转换成一棵标签树存入 selector 中。
-
使用 text = selector.xpath(“xpath”)函数能够从标签树中提取出自己想得到的 HTML 标签内容(及电影的信息)到 text 中,xpath 为 xpath 表达式,类似于正则表达式,只是 xpath 更适用于提取标签文本(如 XML,HTML)中的内容;
-
pymysql 库则用来操作 MySQL 数据库,建表,插入数据等操作;
- urllib 用来格式化 URL。
有了以上这些方法可用,我们只需要编写代码使程序生成豆瓣电影的所有 URL,并依次下载这些页面,分析提取出其中我们需要的信息,就能获得所有的电影信息。图 4-5 的代码生成了所有类型的电影列表页首页 URL,并使用 xpath 语法提取出了每种电影类型的页码数。之后我们只需要依次去遍历这些 URL 并提取信息存入数据库就完成了电影数据的制备。
图 4-5 电影列表 URL 的生成
4.1.1 数据库结构
在 MySQL 中,需要建立一个数据库,然后在库中建表,分别用来存放我们的电影信息、用户信息、用户评价信息、用户搜索记录等等。目前我们只需要先建立一个存放电影信息的表,其他表在之后使用到时再建立。将每一条电影信息存为电影信息表中的一条记录。我创建了一个简单的电影信息表,一共有 6 个字段,分别存每场电影的名字、豆瓣评分、评价人数、电影详情页 URL、首映时间、演员表。
图 4-6 电影信息表
注意,在我们爬取电影信息时是不进入详情页的,只在列表页就能采到我们所需要的全部信息。这样可以减少我们访问的 URL 数量。并且为了简单,电影信息表的 6 个字段都是使用 varchar 类型存储,这样爬下来的页面经过简单的提取就能放到数据库中,我们也不用在此做太多复杂的操作,以免一条数据出错导致整个爬虫停止工作。
4.1.1 爬虫主要逻辑
前面的准备工作做完了,我们就可以开始写爬虫了,首先我们遍历所有的 URL,在 4.1.1 中我们生成了各类电影的列表页首页 URL,以及每类电影的总页数。有了这些信息我们就可以推出所有列表页 URL。从浏览器中很容易看出每个列表页有 20 场电影信息(除每种类型最后一个列表页不足 20 页)。而且每两个相邻页的 URL 存在一个关系。例如爱情类电影的首个列表页 URL 是: https://movie.douban.com/tag/%E7%88%B1%E6%83%85?start=0&type=T
第二页 URL 是: https://movie.douban.com/tag/%E7%88%B1%E6%83%85?start=20&type=T
很明显的规律,每一页的 start 值会比上一页增加 20,不断增加 start 的值直到最后一页。 根据这个规律我们可以生成所有列表页的 URL,如图 4-7,获取所有类型的列表页 URL,并遍历所有页码。然后依次下载每个 URL 对应的 HTML 页面,存入 s 变量中。将 s 放入 run.search()方法中,在该方法中解析页面内容并存入数据库,该方法的内部实现将在后文中介绍。
图 4-7 爬虫主逻辑
在每次下载一个页面并分析完成入库之后,程序会睡眠一个随机时间。然后再循环下载下一个 URL 对应的页面。睡眠随机时间是为了控制程序访问豆瓣服务器的次数,以免给服务器造成负担。
4.1.1 页面分析
在 4.1.3 中遍历了所有列表页的 URL,并且使用 requests.get()方法获取到了每个页面的内容。在 run.search()中提取出我们所需要的 6 个字段,并存入数据库表中。在构造函数中连接数据库,如图 4-8。
图 4-8 连接数据库
在 search 函数中,使用 xpath 选择器从页面中提取出我们需要的 6 个字段,并将这条电影记录插入到数据库。如图 4-9 所示。
图 4-9 提取电影信息入库
4.1.1 数据处理
爬虫运行后,数据库中的数据就会不断增加。在数据库中能够查看到新插入的数据,如图 4-10 所示。
图 4-10 爬虫爬取的数据
表中的 6 个字段全是简单的字符串类型,后续程序使用的表和这个表之间还有一些差异,所以还要做数据去重、类型转换、去除两端空格等处理,比较麻烦,为了降低程序的复杂性,这些工作将在程序之前完成。
这个步骤做法很多,可以编写一个简单的脚本进行处理。将这个表的电影信息导入到上一章设计的电影信息表中。
4.2 基础功能实现
4.2.1 后端主要逻辑
当后端服务收到公众号服务器以 POST 方式发来的请求时,判断消息的类型,调用相应的函数作相应的处理,如果出现异常,则返回 success 给公众号服务器,这样用户就不会显示服务器出故障的字样。如图 4-11,调用相应的函数处理相应类型的消息。 当回复时,只需要将发送方和接收方的 name 交换位置即可,如图 4-11 中的 222 和
行。
图 4-11 POST 处理逻辑
在图 4-11 中,receive.parse_xml()方法负责解析收到的 XML 内容,reply.TextMsg() 负责组装要响应的 XML 内容并又 send()方法返回最终 XML 内容。具体实现分别放在
receive.py 和 reply.py 文件中。只需要 import receive 和 import reply 就能引用发到这几个函数。具体封装如图 4-12 和图 4-13,图 4-12 中,调用 xml.etree.ElementTree.fromstring() 解析 XML,然后使用 find()函数提取关键字段。
图 4-12 receive.py 解析 XML
图 4-13 中的组装模块,目前只用回复文字给用户,所以暂时只用得到 TextMsg 类型。
图 4-13 reply.py 组装 XML
图 4-14 中,分别编写了三个处理函数,对于图片消息,回复“暂时不支持”字样;
对于事件消息,只关心新用户添加关注事件;对于文字消息,将它当命令解析。如果文字不是“评价”、“搜索”、“推荐”三个命令中的一个,那直接将所有文字当成电影名去搜索数据库,然后返回搜索结果。
图 4-14 后台处理函数
当用户添加关注时,服务需要在用户表中检查是否有该有用户的记录,没有则添加为新用户,为其分配 ID。然后需要将操作说明发送给用户,让新用户知道如何跟系统交互。测试结果如图 4-15,符合预期的回复。
图 4-15 关注提示测试
然后查看数据库中的用户表,发现新用户添加成功。如图 4-16。
图 4-16 新用户信息
当用户取消关注时,暂时不删除用户的信息,以免该用户再次关注时数据被清空。后期可以修改为保存一段时间后删除,例如用户已经取消关注三年则删除该用户的信息和记录。目前用户量较少则取消关注时不清楚记录。当用户再次关注时检查数据库,发现已经拥有信息则不会重复添加。
4.2.2 搜索功能
接下来编写系统的处理逻辑。当收到公众号服务器以 POST 发来的请求时,依次判断消息类型、消息内容,如果是“搜索”或者是“电影名”,则调用搜索电影功能。如图 4-11 中的 on_search()、on_browse()实现搜索功能,其实 on_search()内部只是简单调用 on_browse()函数,所以搜索功能其实是靠 on_browse()函数实现。如图 4-17。
图 4-17 精准搜索
在查询数据库后,判断是否找到用户搜索的电影,如果搜索记录不为 0 条,则将电影信息返回给用户,并在搜索记录表内增加搜索记录。如果搜索结果为 0 条记录,则可能是用户输入有误,使用 MySQL 的 like 语句模糊查找,并返回查找结果,如果还没有找到,则返回“该电影不存在”字样,如图 4-18。
图 4-18 模糊搜索
当用户发出搜索请求时,相应的,服务会精准查找数据库中的电影,如果查找到相关电影,则更新查找记录表。如果没有找到,则使用模糊查找并返回结果,不更新查找记录表。如果都没找到,则返回“没有该电影”字样。测试结果如图 4-19。
图 4-19 搜索功能测试
图中发现精准搜索时出现了查询到多个结果的情况,查看数据库发现名字叫做《天空之城》的电影有两部,搜索记录也增加了两条。而用户可能想要的只是其中一部。这种情况是无法避免的,因为用户的输入只是一个简单的电影名,如果想更惊喜的搜索,可以让用户输入更多关于电影的信息,以筛选出用户想要的,但是这样使用户输入的复杂度增加,而得到的效果却很小,所以暂时不处理这个问题。
4.2.3 评价功能
为了获取用户对电影的喜好,本系统设置有评分功能,用户对于观看过的电影可以进行一个评价,对电影打一个 0-10 之间的评分。当用户发来评分命令时,系统解析命令后调用 evaluate 方法,记录用户对电影的评分,实现代码如图 4-20。
图 4-20 评价功能
用户评价电影时,输入评价电影名和分数,对电影评价,这样重名的电影就会被同时评分,但是这可能不是用户想要的结果,用户可能只针对其中的一部电影评分。这种情况我们是有必要避免的,但是代价仍然是增加复杂度,在用户评价时,告诉用户该电影名对应多部电影,然后让用户再在其中选择一次,这样的复杂度是可以接受的,后续可以增加此处理逻辑,但目前如果用户对一个电影名评分,则相同电影名的电影都会被一同评分。如图 4-21,评价电影时,用户输入的电影名不对不会进行模糊查找,只有在数据库中精准找到的电影名才会评价成功。
图 4-21 评价功能测试
图中将两部名叫《天空之城》的电影都评 8.5 分,用户可能只想评论其中一部。这里后续可以进行改进完善。
4.3 协同过滤推荐实现
有了第二章的数据和交互系统,以及第三章对于协同过滤算法的研究。本章将在交互系统中实现推荐功能。由于微信订阅号没有主动发消息的接口,所以使用被动推送,当用户发送“推荐”命令时,系统返回个性化推荐结果。
当收到用户的“推荐”命令时,web.py 后台服务将调用相应的 recommend 函数,该函数会完成整个结果计算,最后返回推荐的内容。
4.3.1 读取用户曲线
根据上一章讨论的协同过滤算法,系统使用基于用户的协同过滤算法,首先要做的就是绘制如图 3-15 中的用户评分曲线。根据用户名,可以从数据库中找到该用户 ID,再根据用户 ID 找到该用户评价过的所有电影,有了该用户对的评分数据,则可以绘制用户评分曲线。如图 4-22,通过两次查找数据库,找到目标用户的评分数据,然后将评分数据存入一个字典中,字典的 key 为电影 ID,value 为该用户对该电影的评分。由于字典的 key 和 value 一一对应组成二维对应关系,可以将 key 看成用户评分曲线的横坐标,及自变量;将 value 看成纵坐标,及因变量,返回在 0 到 10 之内变动;将这个
字典看做目标用户评价曲线。
图 4-22 目标用户评分数据
以同样的方法,绘制其余每一个用户的评价曲线,并与目标用户对比计算出相似程度。如图 4-23,调用 compute 函数计算两个用户的相似程度,然后将结果全部存于字典 areas 中,areas 的 key 为其他用户的 ID,值为相似程度。
图 4-23 遍历所有用户评分数据
4.3.2 用户相似程度
compute 函数接受两个用户的评分数据,返回两个用户的相似程度。只有当两个用户都评论了同一场电影时,才能分析出两个用户的兴趣差值,如果两个用户评价的电影场次是完全不相同的,则认为这两个用户没有共同的兴趣爱好[7]。只有当两个用户评论过相同的电影时,才会计算两个用户的相似程度。同理,连个用户评价的电影场次相同的数量越多,则他们的关注点越相似,则认为他们的相似程度越大;而评论的电影场次差异越大,则关注点不一致,认为他们相似程度越小。
具体算法实现如图 4-24,area 表示两个用户的差异值,该值越大则说明用户差异越大,该值越小则说明用户相似程度越大。但是该值一定是一个非负数。只有当两个用户评价的电影场次完全没有交集时,认为这两个用户的差异无穷大,使用-1 表示无穷大的差异,这是由于数据稀疏造成的。
图 4-24 计算相似程度
对于两个用户评价了同一场电影,则计算他们对这场电影评分的差值的平方记为△。例如,A 用户评价了 10 场电影,B 用户评价了 15 场电影,这些评价中有 7 部电影场次是相同的。则计算这 7 部电影的评分差值的平方 △1、△2、△3、△4、△5、△6、△7、。然后取这些值的平均值 △arg。
计算出 △arg 之后,考虑到 A 和 B 用户一共评价了 7 部相同场次的电影,使 area = △ arg/7。通过这个方法来降低 △arg 的值,使系统体现出两个用户评价的电影相同场次越多,关注点越相近,相似度越高,差异值越小。
最后,考虑到用户 A 有 3 部电影 B 没有评价,用户 B 有 8 部电影 A 没有评价过。所以如果 A 是个性化推荐的目标用户,则 areaA = area 3;如果 B 是目标用户,则 areaB = area 8。通过这样的方法使差异值增大,使系统体现出两个用户评价过的不相同场次的电影数量越多,关注点越不同,相似度越低,差异值越大。通过对用户曲线差距的平均值 △arg 除以一个数再乘以一个数,达到适当调整的效果,解决由数据稀疏造成的问题。
最后整理后得出一个公式放到 compute 函数中作为核心算法。而对于目标用户 A,可推荐的电影就是 B 评价的而 A 未评价的 8 场电影中评分高的。对于 B 用户则相反。
4.3.3 获得最终结果
将目标用户与每个用户对比后,将从这些用户中找出差异值最小的用户,将他视为
目标用户的邻居。如果没有找到邻居,则告诉用户没有找到适合的推荐结果。如图 4-25。
图 4-25 寻找邻居用户
找到邻居用户的用户 ID 后,查询数据库得到能够作为推荐的电影 ID 集合。如图4-26。邻居用户评价过,自己却没有评价过的电影则认为是推荐电影集。
图 4-26 寻找推荐电影 ID 集合
最后,根据这些电影 ID,从中找出要推荐的电影信息。而本系统中没有挑选电影,而是将集合中所有电影推荐给用户,如图 4-27。
图 4-27 产生最终推荐内容
这里其实可以做进一步挑选。根据两个用户的相似度和可推荐的集合中电影数量,
如果用户差异小(可能差异值在 10 以内),说明有共同爱好,则可以挑选出邻居评分最高的几部电影推荐给目标用户。而如果用户差异值大(例如在 1000 以上),则说明两个用户关注点相同,但是评分相反,则可以将邻居的低评分电影推荐给目标用户。也可以采用其他的推荐方案。
五、实验结果分析
5.1 推荐功能测试
推荐功能开发完成后,重启后台服务。在微信客户端进行测试功能正确性。
5.1.1 协同过滤推荐
分别让用户 ID 为 3 和 4 两个用户对系统中的电影做评价,评价场次有相同的,也有不同的,评分根据用户对电影的评价填写。然后两个用户分别发送推荐命令,查看后台日志如图 5-1 所示。
图 5-1 后台计算邻居
图中显示两个用户的邻居 ID 分别为 4 和 3,也就是两个用户互为邻居,差距值分别为 6.2 和 8.3,可推荐电影数量分别为 8 和 6。另外对于用户 3,其还有一个邻居用户 ID 为 38,但是差距值大于用户 3,并且可推荐电影数为 0,说明用户 38 评价过的电影包含于用户 3 都评价过的电影。可以看到互为邻居的用户对于对方计算出来的差距值不相同,所以使用邻居一词并不准确,应该说是“粉丝”。A 用户对 B 用户的差距值越小,
用户就会越喜欢 B 用户评分高的电影。
由此,协同过滤算法生效,可以在两个用户的微信客户端看到协同过滤算法的推荐结果,如图 5-2,推荐结果符合预期。
图 5-2 互为邻居的用户推荐结果
5.1.2 基于内容推荐
对于一个没有找到邻居的用户,或者邻居并没有可推荐的电影场次的时候,认为这是冷启动导致的,所以基于内容的推荐算法将启动,并且由于算法是随机找出 100 部影片,并将其中豆瓣评分最高的一部返回给用户,所以用户每次收到的推荐结果都不一样
(也有很小的几率出现推荐结果相同)。
让两个没有对任何电影进行过评分的用户发送推荐命令,系统一定会启动基于内容的推荐算法,如图 5-3 所示结果。
图 5-3 冷启动时的推荐结果
系统已经拥有了“搜索”、“评价”、“推荐”三大功能。
对于搜索功能,如果用户直接发送的不是命令,就把输入内容当做电影名直接完成搜索功能返回结果。如果搜索的电影名不存在,则使用模糊搜索返回最多 5 条结果。
对于评价功能,用户必须保证电影名输入正确,否则评价失败。评分范围在 0-10 分,如果用户评分超出 10 分,则取 10 分,如果评分为负数,则取 0 分,评分可以接受小数点保留后一位。如果用户对一部电影多次评价,则取最后一次评分为准。
对于推荐功能,使用基于用户的协同过滤算法,推荐和目标用户有着共同兴趣爱好的邻居用户评价过的电影。如果由于冷启动或数据稀疏没有找到适合推荐,则启用基于内容的推荐算法弥补。
有了这些功能之后,系统大致完成,但是系统中仍有很多不足的地方。本章将对系
统中能够改进的点进行说明,并给出解决思路。
5.2 推荐功能优化
在推荐功能测试中发现推荐结果有缺点。当用户评价电影场数较多后,每次推荐结果都是相同的。或者当数据稀疏时,用户得到的推荐结果并不满意。推荐系统并不是完美无缺的,而是在使用过程中发现问题,并不断解决问题。
5.2.1 重复推荐
分析系统中的算法实现,如果系统中的评价数据不发生变化,发现系统为目标用户找到邻居总是同一个人,并且每次推荐的结果总是相同的电影场次。只有当用户的评价数据发生改变后,推荐结果才会发生改变。这个问题最本质的原因是系统没有记住推荐过什么内容给用户,导致用户下一次寻求推荐时,系统忘了已经推荐过的内容,发生重复推荐,直到用户将推荐给他的电影场次一一评价后,推荐内容才会发生改变。
为解决这个问题,系统可以新增一个数据库表格,记录为每个用户推荐过的电影,如果再次发生推荐时发现已经推荐过,则跳过此电影,往后寻找其他推荐。
在推荐电影之后,可以寻求用户对此推荐的满意程度,如果满意度低,则查看数据源进行分析,如果数据中没有恶意评价,说明推荐算法有改进的空间。
5.2.2 响应时间
在系统建立初期,用户量少,计算邻居用户所需要的时间非常短,但随着用户规模增加,每次寻找邻居都需要花费大量时间。为预防这个问题,系统可以新增一份记录数据,记录所有用户的邻居关系,并当评价数据发生改变时或者定期重新计算邻居关系,而不是用户请求时才发生计算。有了邻居数据记录后,每次用户请求推荐只需要简单查询而不需要临时计算,节省了计算时间。另外,如果查询数据库消耗时间占比较大, 可以引入缓存机制,将经常访问的内容复制到内存中,用户请求时可以直接从内存中查询并返回。
5.3 评价功能优化
评价功能非常重要,因为它是协同过滤推荐的基础。没有评分数据就没有协同过滤。
本系统中的评分系统依然存在一些问题。
5.3.1 影片名纠纷
在命令设计时,评分命令为“评价 电影名 分数”,这个命令非常简单,但依然存在一些细微问题。
当用户输入电影名错误时,系统找不到相应的电影,则评分失败。如果用户评价的不是电影名,则评分失败是正常的。而如果用户只是输入时输入有误,则评价失败是系统的失误,例如《你的名字。》电影经常被用户误输入为《你的名字》,少了一个句号导致评价失败,系统损失评分数据。这种情况是可以改进的,解决方法有很多。
解决以上问题,在用户输入评价命令后,搜索电影名,如果没有搜到该电影,则可以使用模糊搜索,并将模糊搜索出来的电影名和对应 ID 返回给用户,提示用户可以使用电影 ID 进行评分。而 ID 是数字,所以不容易出现输入有误。
对电影名重复的不同电影,用户评价时,也可以使用以上方法,但用户评价的电影名搜索出多部同名影片时,可以返回这些同名电影的详细信息以及对应 ID 号,提示用户电影名重复,可以使用电影 ID 号进行评分。
这样的改进可以使评价数据更齐全、更精准,相应的提高协同过滤算法的推荐质量。
5.3.2 鼓励评分
鉴于用户评分对协同过滤算法的重要性,可以做适当的提示和奖励,鼓励用户多进行评价,促进数据填充,使协同过滤算法有更多的推测依据。
5.4 其他辅助功能
搜索功能是一个辅助功能,协同过滤算法也可以基于用户的搜索记录。但是基于用户评分的协同过滤比基于搜索记录的协同过滤更严谨。
在系统中,模糊搜索只是简单地使用 MySQL 的 like 语句实现,这样模糊搜索的结果不会太好,而且用户只能搜索电影名,而不能使用导演、演员、上映地点、上映时间等信息进行搜索。关于这些,系统可以搭建一个搜索引擎,专门提供搜索功能,并记录用户搜索历史。
除了搜索功能,系统还能提供其他一些辅助功能提高系统的完整性。
5.4.1 功能说明书
在新用户对公众号添加关注时,系统会返回操作命令的说明。如果用户长时间没使用该系统,则可能忘记了操作方式,于是可以增加一个获取说明书的功能,例如,用户发送“怎么用”,则系统返回完整的公众号说明书给用户。如果微信公众号有添加菜单功能,也可以增加按钮实现功能。
5.4.2 收藏夹
既然有用户系统,则可以为每个用户提供一个收藏夹,让用户可以将电影添加到收藏夹、查看收藏的电影列表、删除收藏夹中的电影等等。收藏夹也可以作为协同过滤算法的元数据。系统实现起来也比较简单,只需要在数据库中增加相应的一张表记录,然后新增收藏夹相关命令。
六、结论
个性化推荐系统是在互联网、电子商务发展过程中应用知识发现技术、通过频繁的交互针对不同的用户做出个性化的推荐结果。协同过滤算法是目前应用最广泛的个性化推荐技术。
在本文主要研究工作中,基于 CentOS 操作系统、MySQL、微信公众平台、python、协同过滤算法,一步步实现了一个简单实时的电影资源推荐系统,该系统面向微信用户,可以同时为广大用户提供不间断的服务,拥有搜索、评价、个性化推荐等功能。在文章中间的章节对协同过滤算法作出分析评价,研究其实现方法,最后结合本系统环境实现一个基于协同过滤算法的推荐系统。虽然没有分析用户对推荐结果的反馈情况,但是文章最后也提出了很多改进方案。
参考文献
- 基于图数据库的电影推荐系统的设计与实现(云南大学·刘东华)
- 个性化电影推荐管理系统的设计与实现(吉林大学·蔡文琳)
- 基于协同过滤的推荐系统的设计与实现(中山大学·刘振兴)
- Research on Movie Recommendation Method Based on Convolution Neural Network and Long Short Term Memory Network(华中师范大学·唐忆缘)
- 深度协同过滤算法在推荐系统中的研究与应用(郑州大学·崔军磊)
- 基于内容与协同过滤算法的电影推荐系统研究(黑龙江大学·潘悦)
- 基于协同过滤的电影推荐系统的构建(西安电子科技大学·张海朋)
- 基于耦合相似度协同过滤算法的影视推荐系统设计与实现(哈尔滨工业大学·李思远)
- 基于协同过滤的电影推荐系统的构建(西安电子科技大学·张海朋)
- 基于协同过滤算法和强化学习的电影推荐系统(天津大学·张志军)
- 基于协同过滤的个性化电影推荐系统(山东大学·刘晓伟)
- Research on Movie Recommendation Method Based on Convolution Neural Network and Long Short Term Memory Network(华中师范大学·唐忆缘)
- 基于MovieLens数据集的协同过滤推荐系统研究(西安电子科技大学·李清)
- 电影推荐与点评系统的设计与实现(华中科技大学·连浩磊)
- Research on Movie Recommendation Method Based on Convolution Neural Network and Long Short Term Memory Network(华中师范大学·唐忆缘)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:源码客栈网 ,原文地址:https://m.bishedaima.com/yuanma/35821.html