本文使用推荐算法包括:基于用户的协同过滤(UserCF)、基于物品的协同过滤(ItemCF)、基于流行度的推荐、随机推荐、基于用户标签相似度的推荐等算法。通过对比发现基于用户标签相似度的推荐算法表现效果最佳,目前已经将该算法在线上使用。
本文的数据主要是用户收藏的小说、小说的类型两大类。其中用户收藏的小说这个数据比较有说服力,相对于搜索和点击记录,收藏数据更能显现出用户对这个小说的喜爱。
每本书都会有至少一个标签,比如《文化入侵异世界》这本书的标签是“科幻、时空穿梭、二次元”,这个标签是起点中文网给打的,可以认为比较准确。
下面是对用户收藏的书籍的个数统计,横坐标代表收藏个数,纵坐标代表该收藏个数有多少人。可以看出大部分人都只收藏了很少的书籍,符合长尾效应。
下面是对每本书被收藏次数统计,横坐标代表被收藏次数,纵坐标代表该被收藏次数有多少本。可以看出大部分书都只被收藏了很少次,只有少部分书被大量收藏,符合长尾效应。
下面开始分析推荐算法。
一提到推荐算法,首先反应过来的就是协同过滤。协同过滤分为基于用户的协同过滤和基于物品的协同过滤。
这两种推荐算法只需要使用用户的历史收藏数据,所以算法比较简单,也有较好的可解释性。但是问题也很明显:如果用户的历史收藏数据太少,那么这两种算法就无效了。在我们的这个小说推荐系统中,数据只有几千条,并不算很多,不能保证协同过滤算法有太大的威力。
这个暗示着我们,或许可以使用基于流行度的推荐算法。即给用户直接推荐比较流行的书籍即可,很可能这个用户就喜欢。虽然这个方法很粗暴,但是对于整体效果来说,未必就差。
这个想法来自基于用户的协同过滤算法,本质上是个基于内容的推荐算法。所谓基于内容的推荐算法,就是把物品做映射,使用更低维的数据表示该物品,在本项目中就是用户标签。在UserCF中,我们使用两个用户的收藏历史来做相似性分析,但是对于owllook来说,收藏历史不算多,那么就会造成两个用户的共同的收藏书籍很少,那么就无法有效地求用户余弦相似度。所以,这就引导了我们做一个书籍到标签的映射,计算两个用户的相似度不是使用两个用户的共同书籍,而是使用两个用户的共同标签。标签的个数肯定是很少的,所以就解决了无法求两个用户相似度的问题。
其实仔细想想还是很有道理的:只有看的书一样的两个用户才比较相似吗?其实并不是。直观上,对于小说来说,如果两个用户的口味比较一致就行。比如两个人都喜欢科幻,那么这两个人就比较相似。所以可以把其中一个人喜欢的,另一个人没看过的《三体》推荐过去。
总之,我们可以把每个用户收藏的书转化成标签之后,然后利用每个人的标签,求相似用户,然后根据相似用户做书籍推荐。
具体做法是:
本文采用的评价指标和《推荐系统实践》一致,分为精确率,召回率,覆盖率和新颖度。
在上面的几节内容中,我们已经分析了本文的主要几个算法的工作原理,推荐系统的评价指标。真正地推荐过程分为4步:
所以我一共实现了5种推荐算法:
为了对比,我们一致使用推荐10本书籍,比如下面的含义就是用户h****的所有书籍标签、所有收藏的书籍、给他推荐的书籍:
User:h****Usertags:{'上古先秦','东方玄幻','异术超能','都市','架空历史','玄幻','二次元','灵异','恐怖惊悚','幻想修仙','武侠仙侠','仙侠','历史'}Userbooks:['剑来','圣墟','修真聊天群','牧神记','汉乡','逍遥游','深夜书屋','秦吏','三寸人间']recommendforuserid=h****:['大王饶命','飞剑问道','修真聊天群','赘婿','太初','帝霸','一念永恒','诡秘之主','天道图书馆','永夜君王']下面是当给每个用户推荐10本书的时候,各个推荐算法的结果对比:
在上面的结果中,我们可以看到,对于协同过滤来说,UserCF比ItemCF的准确和召回率多高出了不少,说明在我们的实验中,求相似用户然后做推荐的方式是有效果的。ItemCF表现差是因为书籍很多、被收藏的书主要是同样的那几个,导致我们不能很好的求不同书籍的相似情况。另外,我们看到ItemCF比UserCF的覆盖率更广、流行度更低,这说明ItemCF能更好的挖掘出书籍的长尾效应,能够把更冷门的书籍推荐出来。
随机推荐推荐准确率和召回率都比较低,说明随机猜的推荐效果很差。其覆盖率极高,流行度很低,这说明随机猜地推荐算法最能把所有的书籍都推荐出去了,但是我们都知道这种推荐做法不可取。
基于流行度的推荐算法,准确率和召回率比UserCF还要高。这说明我们在上面的分析是对的!由于owllook首页给出了排行榜,另外也可能由于比较火的书确实有很多人喜欢看,所以我们只要把最火的10本书无脑推荐给所有人都能得到很高的准确和召回。其实这引发了我们思考:推荐算法越复杂越好吗?未必见得。基于流行度的推荐的覆盖率很低,这是显然地,毕竟只推荐了最火的10本。流行度是所有推荐算法中最高的,这个指标说明了我们推荐的确实都是最流行的书,并没有很好地把所有书籍都推荐出去的能力。
最后,我们设计的基于用户标签相似度的算法表现最好,准确率和召回率都是最高的,而且领先了一大截。这说明用户对于小说的追求确实是和小说的类型有关,给用户推荐口味相投的用户收藏的书籍确实是是个明智之举。值得注意的是这个算法的召回率,因为召回率反映了推荐算法能不能把用户想要的东西都推荐给他,14%的召回非常高了。这个算法的覆盖率8%左右,相对于协同过滤和随机猜测都很小,说明这个算法并不能太好的挖掘书籍的长尾效应;但是这个覆盖率也远高于基于流行度的推荐,说明这个算法还是推荐了不少的新鲜书籍给用户。最后,这个算法的流行度小于基于流行度的推荐算法,大于UserCF,这个说明了该算法也推荐了不少流行书籍。但上文已经解释过了,推荐流行书籍并不代表是错的,因为流行书籍确实受欢迎。
上面是推荐10本书的情况下,不同推荐算法的对比,我们可以得出结论:本文设计的基于用户标签相似度的推荐算法表现非常好,给用户推荐的书籍受到了用户喜爱,同时也有一定地挖掘长尾效应的能力。
为了做进一步地对比,我将推荐书籍的个数扩大到20本,比如下面的含义就是用户y***的所有书籍标签、所有收藏的书籍、给他推荐的书籍:
User:y***Usertags:{'武侠仙侠','玄幻','都市','武侠幻想','架空历史','都市生活','两晋隋唐','虚拟网游','未来世界','东方玄幻','武侠','科幻','二次元','游戏','历史'}Userbooks:['尘劫录','大王饶命','凡人修仙传仙界篇','天骄战纪','霸汉','修炼狂潮','惊悚乐园','江山美色','死人经','孺子帝','多宝浮屠','赘婿','枭臣','长宁帝军','天下第九','三寸人间','牧神记','剑来','行镖','最强反派系统','星际游轮']recommendforuserid=y***:['圣墟','太初','剑来','赘婿','帝霸','飞剑问道','修真聊天群','诡秘之主','天道图书馆','汉乡','永夜君王','大道朝天','三寸人间','武炼巅峰','元尊','一念永恒','明朝败家子','斗战狂潮','斗破苍穹','将夜']下面是当给每个用户推荐20本书的时候,各个推荐算法的结果对比:
从上面的结果中,我们可以看出,我们设计的基于用户标签相似度的推荐算法准确率、召回率依然最高,覆盖率和流行率处于中等水平,仍然是效果最好的算法。总体来看,当推荐书的个数变多时,所有的算法准确率降低了、召回率提高了。很显然,当推荐数目变多时,推荐的书籍就更不准了,但推荐的书籍就更全了。这告诉我们应该根据项目需求,合理选择推荐系统的优化目标。另外,我们注意到MostSimilar算法与MostPopular算法的准确率和召回率更加接近了,这是否说明MostSimilar已经退化成MostPopular了呢?这么说有一定道理,但并不完全是。有道理是因为比较流行的书籍本来就受欢迎,MostSimilar也做推荐是理所当然的;不完全对的地方在于MostSimilar的覆盖率还是非常大的,说明对长尾的书籍也做了推荐;流行度指标较低,说明推荐了一些冷门的书籍。
这里说的推荐解释,是给我们一个直观上的感受:我们的推荐结果是有说服力的。对于我们的基于用户标签相似度的推荐算法,可解释性非常强,因为我们只要证明推荐的结果书籍的标签是用户喜欢的标签即可。我们把推荐书籍的个数设为10,推荐书籍的同时打印出这个书籍的标签。
对于用户s********,他喜欢的标签是幻想修仙和仙侠,推荐的这10本书基本比较符合,同时也推荐了两本玄幻的书籍,相当于去探索用户的其他兴趣。
User:s********Usertags:{'幻想修仙','仙侠'}Userbooks:['系统的超级宗门','明朝败家子','兔子必须死','我真是医二代','万界之最强大','听说我死后超凶','神豪的悠闲人生','创业吧学霸大人','这个末世有点槽','逆天邪神']recommendforuserid=s********:一念永恒['仙侠','幻想修仙']飞剑问道['仙侠','古典仙侠']圣墟['玄幻','东方玄幻']牧神记['玄幻','东方玄幻']道君['仙侠','幻想修仙']凡人修仙传['仙侠','幻想修仙']剑来['武侠仙侠']斗战狂潮['仙侠','修真文明']蛊真人['仙侠','幻想修仙']大王饶命['都市','都市生活','二次元']['一念永恒','飞剑问道','圣墟','牧神记','道君','凡人修仙传','剑来','斗战狂潮','蛊真人','大王饶命']对于用户陈**,他喜欢的书籍标签是科幻和二次元,看出推荐的书籍都是比较接近他的口味的。
User:陈**Usertags:{'未来世界','史诗奇幻','奇幻','科幻','时空穿梭','二次元'}Userbooks:['放开那个女巫','两界搬运工','修真四万年']recommendforuserid=陈**:大王饶命['都市','都市生活','二次元']诡秘之主['玄幻','异世大陆','二次元']修真聊天群['都市','异术超能','二次元']修真四万年['科幻','未来世界']赘婿['历史','架空历史']学霸的黑科技系统['科幻','超级科技']牧神记['玄幻','东方玄幻']异常生物见闻录['科幻','时空穿梭','二次元']天道图书馆['玄幻','异世大陆']['大王饶命','诡秘之主','修真聊天群','修真四万年','赘婿','学霸的黑科技系统','牧神记','异常生物见闻录','大医凌然','天道图书馆']对于用户风**,他喜欢的标签是历史和武侠,这个推荐的书籍非常符合他的口味。
User:风**Usertags:{'清史民国','传统武侠','两宋元明','历史','历史军事','架空历史','武侠'}Userbooks:['刺明','明扬天下','草清','三国之兵临天下','顺明','指南录','纸花船','明贼','乱世扬明','明末传奇','大明1617','大明最后一个太子','汉儿不为奴']recommendforuserid=风**:赘婿['历史','架空历史']汉乡['历史','架空历史']唐砖['历史','两晋隋唐']带着仓库到大明['历史','两宋元明']剑来['武侠仙侠']秦吏['历史','上古先秦']锦衣夜行['历史','两宋元明']圣墟['玄幻','东方玄幻']晚明['历史','两宋元明']['赘婿','汉乡','唐砖','带着仓库到大明','剑来','秦吏','锦衣夜行','明朝败家子','圣墟','晚明']当然,也存在一些口味比较复杂的用户,收藏了很多种类的书籍,推荐的结果也应该包括了对应的种类。比如对于用户青**,推荐的结果留给读者自行判断吧。
首先,这个算法仍然是基于相似度的算法,那么就不可避免地涉及到推荐系统冷启动问题。当一个新用户加入的时候,他的喜好是未知的,那么该算法也就失效了。
其次,在实验过程中发现,并不是每一本书都会有对应的标签,那么用户如果看了几本没有标签的书,那么就没法给他做推荐了。另外,如果我们的推荐算法有效的前提是书籍的标签是准确的,这个并不是那么容易确定的。
再者,本文对每个要推荐的小说累加了用户的相似度,然后根据小说得分的TopK做的推荐。那么不同的相似度计算方式会影响推荐结果。这个需要在实验中去验证选择。
对于前两个问题,可以考虑使用基于流行度的推荐算法作为补充。即,我们遇到无法推荐的情况时,直接把流行的书籍推荐给他。上文的推荐算法对比已经说明了,流行书籍推荐是有效的。
本文设计的基于用户标签相似度的网络小说推荐系统,在推荐系统的各个评测指标上,表现都很出色。目前已经投入线上使用当中。