记--结合业务从零实现一个推荐系统

前言:本文章主要讲述本人在工作期间做的推荐系统工作,主要内容是从零开始构建一个可用的推荐系统,其中包括实时,离线两部分。笔者其实是第一次从零实现一个全面的推荐系统。本文章会记录下在开发过程中的技术细节,以及对推荐系统的一些看法。在笔者开发的过程中,很多的技术点其实都是现学现用的,难免会有不足和错误,希望读者在阅读过程中,如有发现,希望指出。

1. 推荐系统背景

推荐系统从互联网时代开始便有相关技术,并且在演进过程中诞生了不少的经典算法,比如协同过滤CF,隐语义矩阵分解,personal rank等等。当然,这些算法随着大数据时代的到来变得更为出名,并且,随着深度学习时代的到来(大概是2012到2014年)的到来,基于神经网络的推荐系统也进一步发掘出来,产业化也逐渐出现,在这方面最出名的研究应当是Google的Deep Neural Networks for YouTube Recommendation(2016年论文),大概在这个时候,深度学习便在推荐系统中大行其道。而这几年,又出现了强化学习应用到推荐系统的相关研究。

2.推荐系统场景

笔者开发的推荐系统是面向短视频推荐的,类似B站,抖音这种。通过记录用户的观看点击等记录,利用算法理解用户的偏好,并进行推荐。

3.推荐系统架构

笔者在开发推荐系统时,参考了推荐系统方面最经典的架构,lambda架构,将整个推荐分为两个部分,离线部分以及实时部分。离线部分主要处理全量数据集,该部分的算法更关注推荐的精准性或其他相关指标,且算法运行时间较长。实时算法处理的是最近产生的一批数据,也可以说是数据流,该部分更关注运行的速度以及健壮性(其实就是时间复杂度和空间复杂度都不能高),该部分产生的推荐结果会直接推送给用户。
架构如下:
记--结合业务从零实现一个推荐系统_第1张图片
首先是业务端,需要接受推荐结果是业务端,产生用户操作记录的也是业务端,因此,在推荐的第一步,就是将用户操作数据收集下来,存在某个分布式数据库中,笔者用的是mongodb,接下来的离线推荐将基于该数据库中的数据。在上面的架构图中,笔者流出了几类常见的推荐算法,这些算法也是笔者致力于应用在推荐系统中的。在实时部分中,所操作的主要数据库不再是mongodb,而是redis,这是为了能够提升操作速度。流程如下:
记--结合业务从零实现一个推荐系统_第2张图片
离线部分以及实时部分将会在下文详细介绍。

4.离线推荐部分

离线推荐部分比较简单,由于不需要考虑算法的时间复杂度(一天跑一次或两次),因此只需要将注意力放在如何提高算法的某些推荐评测指标,比如RMSE/MAE, 召回率/精确率。
下面说一下笔者对上面架构图算法的一些理解,可供读者参考。

  • 协同过滤算法Item-Based/User-Based:笔者在选择算法的时候,第一个就是它,不仅因为其实现方便,而且一直以来,这类算法的效果都比较好,是非常经典的算法。值得注意的是,如果用户数目远大于物品,建议Item-Based,如果倒过来,建议User-Based,笔者在运行的时候,两种算法都用上了,但其实效果都算不错,**在一开始的时候,算法的RMSE是1.0以上,大概过了两个月,变为0.6x了。**这证明是算法是靠谱的,且数据量越大,效果越好。
  • GBDT+LR:这种算法也相当出名,在很多大企业里,涉及到广告CTR预估的,我了解到这种算法几乎都会被考虑,而且效果不错。网上也有很多例子,读者可以自己去挖掘。笔者理解中,该算法的应用主要难点是构建特征稍为麻烦,不像协同过滤这么直接(构建打分矩阵就可以了),笔者在这方面没有太多的经验,而且时间也不允许(工期赶),因此将这种算法的优先级放在很后面,没有去实现。
  • **隐语义:**这种算法笔者有应用在推荐系统中,但是调用了spark的ml包。从结果上来说,其RMSE和协同过滤其实差不多,但由于结合了spark,运算速度更快。而且隐语义技术有个非常好的地方,那就是其可以计算出用户以及物品的“隐特征”,往后可能利用这个特征向量描述该用户或物品,可用在各种各样的相似度比较中,很灵活。

在笔者实现的推荐系统中,离线算法是被动吊起的,笔者基于crontab控制离线算法3小时跑一遍。
另外,为了解决稀疏性问题,笔者对原生的协同过滤算法进行了改动,加入了标签的考虑(用户是可获得用户标签的,物品也是),有兴趣的读者可私我了解。

5.实时推荐部分

首先看图:
记--结合业务从零实现一个推荐系统_第3张图片
实时部分解释起来相对麻烦。因为笔者将很多涉及到算法效率,健壮性的东西都放在实时里了,内容有点大。下面会逐一解释。

笔者在这里抓住一个东西:标签。实时算法将基于标签展开。

首先,视频是有标签的,这可以通过外包团队去打。一旦打好,就会存储在数据库中。但除此之外,用户也是有标签的——用户的标签通过其操作记录获得。比如,小明看了A,B,C三个视频,每个视频都会自己的标签,那小明的标签就是这些标签的加权组合(比如 A,B视频都有某个标签,那该标签在小明身上会占更多比重)。

  • 用户长期标签:基于 用户比较长一段时间内观看过的视频,加权组合而成的标签。
  • 用户短期标签: 类似长期标签,但是基于用户很短一个时间看的视频,比如5分钟。

上面两种标签都可以加入一些限制,比如对标签出现次数加入下限的限制。为什么这么设计呢?举个例子,小明看了若干视频,每个视频都带着自己的标签,比如A视频标签[a,b,c,d,e],B视频标签[b,c,e,f],那很明显地,对于小明来说,a,d,f出现了一次,b,c,e出现了两次,那么可以设置一个限制,出现过两次及以上的标签才纳入用户标签中,那小明的用户标签就是[b,c,e],这样做可以避免很多"噪音",“噪音”的意思是,只出现过一次或少数次的标签其实不能反馈用户的真实爱好,必须得多次。另外,去掉这些"噪音"还可以降低计算复杂度,毕竟向量更短。

标签可以理解为一个特征向量,每个用户,每个物品都带着这种特征向量。那如何利用这种特征向量呢?通过比较——然后就可以得出两个东西的相似程度了。这种比较不止可以用在用户-用户,物品-物品上,用户-物品也是可以的。 这就是这个实时算法妙的地方。

但这种比较不是余弦相似度,欧式距离这种比较,因为标签是中文因此这里的比较必须基于NLP。实际上,笔者利用了了word2cvec的训练结果,直接获取两个中文词汇之间的相似程度,比如:similarity{爱好,兴趣} = 0.9。(word2vec是同事帮我训练的,但不难,只是采用的语料库很大,用了搜狗的开源语料库)。

笔者就是利用了用户-物品的比较方式。实现如下:

  1. 求出用户的长期标签,时间长度3个月。并基于此向量跟数据库中的物品标签向量做对比,得出最为相似的200个视频。 -------该步计算量巨大,因为涉及到数据库中的物品标签全量遍历。因为不能实时,但也不能离线,因此笔者采用异步操作
  2. 求出用户的短期标签,长度为5分钟。将其与第1步得到的视频进行相似度求解,得出最终的20个视频,推荐给用户。 --------该步计算量小,可以实时。

读者还看到,上面实时的图还包含了离线算法召回200个视频这个分支,这是笔者后面实现算法的时候加上去的。离线算法的推荐视频挺靠谱的,因此笔者将其前200个也拿出来作为召回集合,当然,这种计算也是异步的。这是一些小trick,在业务中会用的挺多这种手段,虽然操作不复杂,但能比较好的提高效果。

6.总结

上面比较粗糙的讲述了笔者的推荐系统的工作。个人觉得,推荐系统其实是蛮有套路的,但在实践的时候,难度还挺大的,尤其是实时部分,涉及到健壮性,还有算法效率的考量,这个挺令我头痛的。

下面总结一下这些模块用到的技术:

笔者实现的推荐系统是基于python写的,即使是涉及到spark的部分,用了pyspark。

  • 离线部分: 纯粹的python实现协同过滤算法。pyspark+隐语义ALS算法
  • 实时部分: 异步框架celery, 实时算法的单独实现(这个花了笔者很长时间)
  • 跟业务端对接:flask模块+celery模块
  • 评判指标: 离线部分–RMSE,召回率/精确率,实时部分–转化率(视频被看次数/视频被推荐次数)

大致的工作就这些,有兴趣的研究的更深的可以私聊我微信:jiedemaikena


参考:https://blog.csdn.net/weixin_41697507/article/details/89386899

你可能感兴趣的:(机器学习,python学习,大数据,数据库,nlp)