设计 owllook 网络小说推荐系统

本文记录了我对owllook.net这一网络小说搜索引擎的推荐系统的分析与设计过程。这个项目是开源项目,地址在owllook repo,目前已经积累了一定的数据量,需要一个靠谱的推荐系统。注意我用到的数据是内部数据,并没有开源。

本文使用推荐算法包括:基于用户的协同过滤(UserCF)、基于物品的协同过滤(ItemCF)、基于流行度的推荐、随机推荐、基于用户标签相似度的推荐等算法。通过对比发现基于用户标签相似度的推荐算法表现效果最佳,目前已经将该算法在线上使用。

owllook

数据

本文的数据主要是用户收藏的小说、小说的类型两大类。其中用户收藏的小说这个数据比较有说服力,相对于搜索和点击记录,收藏数据更能显现出用户对这个小说的喜爱。

在我使用的这批数据中,共有用户2420个,共有书籍1596本,所有用户共收藏书籍4780本。为了验证推荐系统的有效性,我们使用每个用户的70%的数据当做训练集,把剩下的30%当做测试集。也就是说基本上每个用户都会在训练集和测试集同时出现,但是这个用户读过的书不会同时出现。我们的目的就是通过分析这个用户读过的书(训练集),去给这个用户推荐他可能感兴趣的书籍。如果我们推荐的书籍中包括了用户测试集的书,说明我们这个推荐命中了。这里的推荐没有考虑时间因素。

每本书都会有至少一个标签,比如《文化入侵异世界》这本书的标签是“科幻、时空穿梭、二次元”,这个标签是起点中文网给打的,可以认为比较准确。

下面是对用户收藏的书籍的个数统计,横坐标代表收藏个数,纵坐标代表该收藏个数有多少人。可以看出大部分人都只收藏了很少的书籍,符合长尾效应。

下面是对每本书被收藏次数统计,横坐标代表被收藏次数,纵坐标代表该被收藏次数有多少本。可以看出大部分书都只被收藏了很少次,只有少部分书被大量收藏,符合长尾效应。

推荐算法

下面开始分析推荐算法。

协同过滤

一提到推荐算法,首先反应过来的就是协同过滤。协同过滤分为基于用户的协同过滤和基于物品的协同过滤。

  • 基于用户的协同过滤算法(UserBasedCF) 这种算法给用户推荐和他兴趣相似的其他用户喜欢的物品。
  • 基于物品的协同过滤算法(ItemBasedCF) 这种算法给用户推荐和他之前喜欢的物品相似的物品。

这两种推荐算法只需要使用用户的历史收藏数据,所以算法比较简单,也有较好的可解释性。但是问题也很明显:如果用户的历史收藏数据太少,那么这两种算法就无效了。在我们的这个小说推荐系统中,数据只有几千条,并不算很多,不能保证协同过滤算法有太大的威力。

更多关于协同过滤的内容,可以看我这篇博客动手实现基于协同过滤的电影推荐系统

基于流行度的推荐

在用户收藏书籍个数统计和书籍被收藏个数统计的分析图中我们可以看到,事实上只有活跃的用户占少数、也只有少量的书籍比较热门。热门书籍大概可以从owllook.net这个搜索框下面的搜索排行中有所反映。

搜索排行

这个暗示着我们,或许可以使用基于流行度的推荐算法。即给用户直接推荐比较流行的书籍即可,很可能这个用户就喜欢。虽然这个方法很粗暴,但是对于整体效果来说,未必就差。

基于用户标签相似度的推荐

这个想法来自基于用户的协同过滤算法,本质上是个基于内容的推荐算法。所谓基于内容的推荐算法,就是把物品做映射,使用更低维的数据表示该物品,在本项目中就是用户标签。在UserCF中,我们使用两个用户的收藏历史来做相似性分析,但是对于owllook来说,收藏历史不算多,那么就会造成两个用户的共同的收藏书籍很少,那么就无法有效地求用户余弦相似度。所以,这就引导了我们做一个书籍到标签的映射,计算两个用户的相似度不是使用两个用户的共同书籍,而是使用两个用户的共同标签。标签的个数肯定是很少的,所以就解决了无法求两个用户相似度的问题。

其实仔细想想还是很有道理的:只有看的书一样的两个用户才比较相似吗?其实并不是。直观上,对于小说来说,如果两个用户的口味比较一致就行。比如两个人都喜欢科幻,那么这两个人就比较相似。所以可以把其中一个人喜欢的,另一个人没看过的《三体》推荐过去。

总之,我们可以把每个用户收藏的书转化成标签之后,然后利用每个人的标签,求相似用户,然后根据相似用户做书籍推荐。

具体做法是:

  1. 求出每个用户的所有书籍累积的标签计数,比如用户A : {"科幻":3, "时空穿梭":2, "二次元":4}
  2. 求该用户和其他用户的余弦相似度,比如用户A : {"用户B" : 0.97, "用户C" : 0.86, "用户D" : 0.72};
  3. 遍历每个相似用户的书籍,累加用户相似度得到书籍平分,比如用户B看过《三体》、《球形闪电》,用户C看过《球形闪电》,用户D看过《流浪地球》,那么给用户A推荐的书籍的打分为:用户A : {"《三体》" : 0.97,"《球形闪电》" : 1.83,"《流浪地球》" : 0.72
  4. 对推荐的书籍按照打分排序,并把得分最高的TopK推荐给用户A.

评价指标

本文采用的评价指标和《推荐系统实践》一致,分为精确率,召回率,覆盖率和新颖度。

  • 精确率描述最终的推荐列表中有多少比例是发生过的用户—物品评分记录;

  • 召回率描述有多少比例的用户—物品评分记录包含在最终的推荐列表中;

  • 覆盖率反映了推荐算法发掘长尾的能力,覆盖率越高,说明推荐算法越能够将长尾中的物品推荐给用户;

  • 新颖度反映了推荐列表中物品的平均流行度。如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖。

算法对比

在上面的几节内容中,我们已经分析了本文的主要几个算法的工作原理,推荐系统的评价指标。真正地推荐过程分为4步:

  1. 构造训练集和预测集
  2. 训练推荐系统模型
  3. 给出推荐结果
  4. 评价推荐结果

所以我一共实现了5种推荐算法:

  • UserCF,基于用户的协同过滤算法
  • ItemCF,基于物品的协同过滤算法
  • Random,随机推荐
  • Most Popular,推荐最流行物品
  • Most Similar,根据用户标签相似度推荐

推荐10本书籍

为了对比,我们一致使用推荐10本书籍,比如下面的含义就是用户h****的所有书籍标签、所有收藏的书籍、给他推荐的书籍:

User: h****
User tags: {'上古先秦', '东方玄幻', '异术超能', '都市', '架空历史', '玄幻', '二次元', '灵异', '恐怖惊悚', '幻想修仙', '武侠仙侠', '仙侠', '历史'}
User books: ['剑来', '圣墟', '修真聊天群', '牧神记', '汉乡', '逍遥游', '深夜书屋', '秦吏', '三寸人间']
recommend for userid = h****:
['大王饶命', '飞剑问道', '修真聊天群', '赘婿', '太初', '帝霸', '一念永恒', '诡秘之主', '天道图书馆', '永夜君王']

下面是当给每个用户推荐10本书的时候,各个推荐算法的结果对比:

推荐算法\评测指标PrecisionRecallCoveragePopularity
UserCF0.49%4.93%45.69%1.1230
ItemCF0.08%0.08%52.77%0.6141
Random0.04%0.34%99.83%0.8827
Most Popular0.74%7.45%0.90%3.5024
Most Similar1.43%14.08%7.96%2.2894

在上面的结果中,我们可以看到,对于协同过滤来说,UserCF比ItemCF的准确和召回率多高出了不少,说明在我们的实验中,求相似用户然后做推荐的方式是有效果的。ItemCF表现差是因为书籍很多、被收藏的书主要是同样的那几个,导致我们不能很好的求不同书籍的相似情况。另外,我们看到ItemCF比UserCF的覆盖率更广、流行度更低,这说明ItemCF能更好的挖掘出书籍的长尾效应,能够把更冷门的书籍推荐出来。

随机推荐推荐准确率和召回率都比较低,说明随机猜的推荐效果很差。其覆盖率极高,流行度很低,这说明随机猜地推荐算法最能把所有的书籍都推荐出去了,但是我们都知道这种推荐做法不可取。

基于流行度的推荐算法,准确率和召回率比UserCF还要高。这说明我们在上面的分析是对的!由于owllook首页给出了排行榜,另外也可能由于比较火的书确实有很多人喜欢看,所以我们只要把最火的10本书无脑推荐给所有人都能得到很高的准确和召回。其实这引发了我们思考:推荐算法越复杂越好吗?未必见得。基于流行度的推荐的覆盖率很低,这是显然地,毕竟只推荐了最火的10本。流行度是所有推荐算法中最高的,这个指标说明了我们推荐的确实都是最流行的书,并没有很好地把所有书籍都推荐出去的能力。

最后,我们设计的基于用户标签相似度的算法表现最好,准确率和召回率都是最高的,而且领先了一大截。这说明用户对于小说的追求确实是和小说的类型有关,给用户推荐口味相投的用户收藏的书籍确实是是个明智之举。值得注意的是这个算法的召回率,因为召回率反映了推荐算法能不能把用户想要的东西都推荐给他,14%的召回非常高了。这个算法的覆盖率8%左右,相对于协同过滤和随机猜测都很小,说明这个算法并不能太好的挖掘书籍的长尾效应;但是这个覆盖率也远高于基于流行度的推荐,说明这个算法还是推荐了不少的新鲜书籍给用户。最后,这个算法的流行度小于基于流行度的推荐算法,大于UserCF,这个说明了该算法也推荐了不少流行书籍。但上文已经解释过了,推荐流行书籍并不代表是错的,因为流行书籍确实受欢迎。

上面是推荐10本书的情况下,不同推荐算法的对比,我们可以得出结论:本文设计的基于用户标签相似度的推荐算法表现非常好,给用户推荐的书籍受到了用户喜爱,同时也有一定地挖掘长尾效应的能力。

推荐20本书籍

为了做进一步地对比,我将推荐书籍的个数扩大到20本,比如下面的含义就是用户y***的所有书籍标签、所有收藏的书籍、给他推荐的书籍:

User: y***
User tags: {'武侠仙侠', '玄幻', '都市', '武侠幻想', '架空历史', '都市生活', '两晋隋唐', '虚拟网游', '未来世界', '东方玄幻', '武侠', '科幻', '二次元', '游戏', '历史'}
User books: ['尘劫录', '大王饶命', '凡人修仙传仙界篇', '天骄战纪', '霸汉', '修炼狂潮', '惊悚乐园', '江山美色', '死人经', '孺子帝', '多宝浮屠', '赘婿', '枭臣', '长宁帝军', '天下第九', '三寸人间', '牧神记', '剑来', '行镖', '最强反派系统', '星际游轮']
recommend for userid = y***:
['圣墟', '太初', '剑来', '赘婿', '帝霸', '飞剑问道', '修真聊天群', '诡秘之主', '天道图书馆', '汉乡', '永夜君王', '大道朝天', '三寸人间', '武炼巅峰', '元尊', '一念永恒', '明朝败家子', '斗战狂潮', '斗破苍穹', '将夜']

下面是当给每个用户推荐20本书的时候,各个推荐算法的结果对比:

推荐算法\评测指标PrecisionRecallCoveragePopularity
UserCF0.28%5.50%59.08%0.8878
ItemCF0.08%1.48%62.53%0.5854
Random0.02%0.36%100.00%0.8739
Most Popular0.65%12.65%1.57%3.2299
Most Similar0.87%17.52%15.33%2.1372

从上面的结果中,我们可以看出,我们设计的基于用户标签相似度的推荐算法准确率、召回率依然最高,覆盖率和流行率处于中等水平,仍然是效果最好的算法。总体来看,当推荐书的个数变多时,所有的算法准确率降低了、召回率提高了。很显然,当推荐数目变多时,推荐的书籍就更不准了,但推荐的书籍就更了。这告诉我们应该根据项目需求,合理选择推荐系统的优化目标。另外,我们注意到Most Similar算法与Most Similar算法的准确率和召回率更加接近了,这是否说明Most Similar已经退化成Most Popular了呢?这么说有一定道理,但并不完全是。有道理是因为比较流行的书籍本来就受欢迎,Most Similar也做推荐是理所当然的;不完全对的地方在于Most Similar的覆盖率还是非常大的,说明对长尾的书籍也做了推荐;流行度指标较低,说明推荐了一些冷门的书籍。

推荐解释

这里说的推荐解释,是给我们一个直观上的感受:我们的推荐结果是有说服力的。对于我们的基于用户标签相似度的推荐算法,可解释性非常强,因为我们只要证明推荐的结果书籍的标签是用户喜欢的标签即可。我们把推荐书籍的个数设为10,推荐书籍的同时打印出这个书籍的标签。

对于用户s********,他喜欢的标签是幻想修仙仙侠,推荐的这10本书基本比较符合,同时也推荐了两本玄幻的书籍,相当于去探索用户的其他兴趣。

User: s********
User tags: {'幻想修仙', '仙侠'}
User books: ['系统的超级宗门', '明朝败家子', '兔子必须死', '我真是医二代', '万界之最强大', '听说我死后超凶', '神豪的悠闲人生', '创业吧学霸大人', '这个末世有点槽', '逆天邪神']
recommend for userid = s********:
一念永恒 ['仙侠', '幻想修仙']
飞剑问道 ['仙侠', '古典仙侠']
圣墟 ['玄幻', '东方玄幻']
牧神记 ['玄幻', '东方玄幻']
道君 ['仙侠', '幻想修仙']
凡人修仙传 ['仙侠', '幻想修仙']
剑来 ['武侠仙侠']
斗战狂潮 ['仙侠', '修真文明']
蛊真人 ['仙侠', '幻想修仙']
大王饶命 ['都市', '都市生活', '二次元']
['一念永恒', '飞剑问道', '圣墟', '牧神记', '道君', '凡人修仙传', '剑来', '斗战狂潮', '蛊真人', '大王饶命']

对于用户陈**,他喜欢的书籍标签是科幻二次元,看出推荐的书籍都是比较接近他的口味的。

User: 陈**
User tags: {'未来世界', '史诗奇幻', '奇幻', '科幻', '时空穿梭', '二次元'}
User books: ['放开那个女巫', '两界搬运工', '修真四万年']
recommend for userid = 陈**:
大王饶命 ['都市', '都市生活', '二次元']
诡秘之主 ['玄幻', '异世大陆', '二次元']
修真聊天群 ['都市', '异术超能', '二次元']
修真四万年 ['科幻', '未来世界']
赘婿 ['历史', '架空历史']
学霸的黑科技系统 ['科幻', '超级科技']
牧神记 ['玄幻', '东方玄幻']
异常生物见闻录 ['科幻', '时空穿梭', '二次元']
天道图书馆 ['玄幻', '异世大陆']
['大王饶命', '诡秘之主', '修真聊天群', '修真四万年', '赘婿', '学霸的黑科技系统', '牧神记', '异常生物见闻录', '大医凌然', '天道图书馆']

对于用户风**,他喜欢的标签是历史武侠,这个推荐的书籍非常符合他的口味。

User: 风**
User tags: {'清史民国', '传统武侠', '两宋元明', '历史', '历史军事', '架空历史', '武侠'}
User books: ['刺明', '明扬天下', '草清', '三国之兵临天下', '顺明', '指南录', '纸花船', '明贼', '乱世扬明', '明末传奇', '大明1617', '大明最后一个太子', '汉儿不为奴']
recommend for userid = 风**:
赘婿 ['历史', '架空历史']
汉乡 ['历史', '架空历史']
唐砖 ['历史', '两晋隋唐']
带着仓库到大明 ['历史', '两宋元明']
剑来 ['武侠仙侠']
秦吏 ['历史', '上古先秦']
锦衣夜行 ['历史', '两宋元明']
圣墟 ['玄幻', '东方玄幻']
晚明 ['历史', '两宋元明']
['赘婿', '汉乡', '唐砖', '带着仓库到大明', '剑来', '秦吏', '锦衣夜行', '明朝败家子', '圣墟', '晚明']

当然,也存在一些口味比较复杂的用户,收藏了很多种类的书籍,推荐的结果也应该包括了对应的种类。比如对于用户青**,推荐的结果留给读者自行判断吧。

User: 青**
User tags: {'游戏异界', '二次元', '恐怖惊悚', '史诗奇幻', '灵异', '都市', '古典仙侠', '东方玄幻', '进化变异', '修真文明', '异世大陆', '都市生活', '灵异鬼怪', '幻想修仙', '黑暗幻想', '奇幻', '科幻', '游戏', '玄幻', '仙侠'}
User books: ['怪谈研究会', '就是个道士', '最初的寻道者', '恶神当道', '我是仙凡', '道辟九霄', '当个法师闹革命', '炼道长生', '一世之尊', '问镜', '原始战记', '极度尸寒', '恐怖邮差', '玄门封神', '封仙', '蛊真人', '这世界的土著好凶猛', '诸天纪', '仙路云霄', '旧日篇章', '深夜书屋', '修真新时代', '白中仙的修道生涯', '侠道行', '道门振兴系统', '难道我是神', '我假装会异能', '我当道士那些年', '绿洲中的领主', '与妖怪的二三事', '道吟', '氪金魔主', '一品修仙', '全球高武', '黑夜玩家', '民国谍影']
recommend for userid = 青**:
圣墟 ['玄幻', '东方玄幻']
飞剑问道 ['仙侠', '古典仙侠']
牧神记 ['玄幻', '东方玄幻']
一念永恒 ['仙侠', '幻想修仙']
大王饶命 ['都市', '都市生活', '二次元']
剑来 ['武侠仙侠']
斗战狂潮 ['仙侠', '修真文明']
太初 ['玄幻', '东方玄幻']
逆天邪神 ['仙侠', '幻想修仙']
道君 ['仙侠', '幻想修仙']
['圣墟', '飞剑问道', '牧神记', '一念永恒', '大王饶命', '剑来', '斗战狂潮', '太初', '逆天邪神', '道君']

探讨

本文设计的基于用户标签相似度的推荐算法有没有什么缺点呢?我认为也是有的。

首先,这个算法仍然是基于相似度的算法,那么就不可避免地涉及到推荐系统冷启动问题。当一个新用户加入的时候,他的喜好是未知的,那么该算法也就失效了。

其次,在实验过程中发现,并不是每一本书都会有对应的标签,那么用户如果看了几本没有标签的书,那么就没法给他做推荐了。另外,如果我们的推荐算法有效的前提是书籍的标签是准确的,这个并不是那么容易确定的。

再者,本文对每个要推荐的小说累加了用户的相似度,然后根据小说得分的TopK做的推荐。那么不同的相似度计算方式会影响推荐结果。这个需要在实验中去验证选择。

对于前两个问题,可以考虑使用基于流行度的推荐算法作为补充。即,我们遇到无法推荐的情况时,直接把流行的书籍推荐给他。上文的推荐算法对比已经说明了,流行书籍推荐是有效的。

结论

本文设计的基于用户标签相似度的网络小说推荐系统,在推荐系统的各个评测指标上,表现都很出色。目前已经投入线上使用当中。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值