中华万年历推荐系统实践

前言

万年历的首屏包含日历、运势、星座等功能外,也一直提供着大量资讯类内容。这部分内容早期是由运营同学人工编辑筛选并干预展示规则。但纯粹以人工生产内容的方式效率较低,并且以人工干预展示的方式经常出现流量分配不合理的情况:大量展示机会往往落在少数内容。而内容的好坏直接由人的感觉衡量也极不客观。

为了解决信息生产效率受限的状况,后续逐步引入了外部信息抓取的方案。海纳百川,内容渐渐丰富了起来,但抓取的信息量极大提高后,仍然通过人工审核就常常使运营同学们措手不及。每篇内容的审核、编排时间被压缩,纯人工筛选和干预的效果也逐步下降。而这些机械枯燥的工作内容也并不能为我们的运营同学带来任何快乐和成长。

为了解决上述问题,我们开始了万年历推荐系统的设计和实现。

对于推荐系统,必要的是一套大数据平台。同时以我们逐步形成推荐系统的经验来看,如果在功能没有完全稳定成型的情况下,起初可以不要一次做出太复杂的推荐逻辑:越复杂的算法适用面越窄,当面对功能较大变更时很可能出现问题,而复杂的算法也较难排查和调试。

下面基于万年历推荐系统逐步形成的过程,分享一些我们的经验。

冷启动

我们在用户冷启动、item冷启动及系统冷启动上做了一些尝试。

用户冷启动一般可以结合一些注册信息或主动让用户描述兴趣等。比如参考用户注册时填写的年龄、性别等信息;进入app后请求用户勾选一些兴趣爱好。但在实践当中注意到采用注册信息作为依据并不靠谱:在万年历中这类信息支持度偏低,置信度比较难直接衡量。

item冷启动可以采用基于文本的挖掘方式做标注,但这种方式开销比较高。我们曾经尝试使用tensorflow构建卷积神经网络对文本做标签标注,并使用搜狗公开的文本分类语料库进行试验。在权衡计算开销和准确度的情况下并没有得到满意的结果(三层卷积神经网络网络,准确率89%)。最终采用直接由媒体合作方的自身属性和内容抓取频道直接做item的相关标记。

对于系统冷启动可以采取先不做个性化推荐的实现方案。可以制作热榜数据:计算最近一段时间(区分业务,设置不同的时间窗口)item热门分数关于点击率c和上线距今时间差t的函数(公式1):

gs1

其中△uptime为item已上线时间,T为选取的滑动窗口内item上线时间戳集合。α、β、θ为调优参数。

分数随t增大而衰减,点击率越高衰减越慢。hacker news和reddit等也有分享过它们使用的热榜模型可以作为参考。具体模型和参数,各支业务不同没有统一的套用方案,参数可以通过AB测试来确定。从经验上来看,除非有极其丰富的数据源,否则尽量不要使用时间的指数函数来做衰减,否则热榜很容易退化成item上线时间的倒序排序序列。

冷启动的改进

单单制作几个热门榜其实已经比随机选取内容展示要好很多了。但是对于用户个性化的反馈还没有。此时比较简单的一个方法就是实时生成用户个性化过滤信息做”负反馈“。对每个用户的浏览、点击等行为做记录,作为热门榜返回给到不同用户的内容做过滤依据。如果是针对纯时效性的内容,这部分过滤信息可以设置失效时间。对于用户量较大的场景,并不严格的要求过滤信息与用户行为完全一致,可以结合hyperloglog或bloomfilter的数据结构来做存储的进一步优化。比如我们希望item最多展示K次(K值为较小的正整数)或最多被点击1次后不再展示,则可以通过如下方案实现:

glq

对每一个item构建K个用户集合,当请求展示时,向该item最低次不含有该用户的集合中插入该用户,当第K个集合已经包含该用户时,该用户的候选资讯集合会将此item过滤。上传点击日志会将此item的所有次数的访问用户集合加入该用户。资讯存在时效性,从app下架时则可以同时释放item对应的访问用户集合存储资源。

策略改进实验

虽然在比较严格的算法改进和迭代周期中一般希望先完成离线实验,再做用户调查,最后再完成线上实验和算法的全流量覆盖。但一般完成所有的流程,对于体量较小的公司周期太长。 下表是几种试验方法的优缺点比较:






实验方法优点 缺点
离线实验可短期完成多个算法、策略的性能比对一般无法准确测算商业化指标
用户调查比较真实地反馈用户感受,风险低成本高,选取用户分布要求较高,易偏置
在线实验可以比较真实反馈所有待检验性能指标周期长,有一定风险

我们实际采用流量从小到大逐步覆盖到所有用户的方法,如果新策略效果不佳则可以在实验中途下线。流量划分可以初期采用各个实验流量互斥的方式,后续对互不影响的实验做流量分层。流量划分可以采用移动设备标识的MD5值。android可以采用imei+mac,ios可以采用idfa。但要注意,在实践当中,发现android有部分手机在重启时mac会发生变化。而山寨机往往imei为一串0,所以建议提前做用户日志的统计来确定使用何种标识口径。

推荐系统的进一步改进

最常用的就是协同过滤。可以优先采取modelbased(比如als)和itembased两种方法。用户量极其庞大或用户行为比较稀疏的情况下尽量不要使用userbased。als我们使用spark构建离线推荐集合,itembased我们实现了两种计算策略:

  1. 标签及来源是否相同(公式2):

    gs2

  2. 基于用户的行为,计算关联关系(公式3):

    gs3

    其中N(j)表示消费了资讯j的用户集合

    如果用户活跃度差异较大还可以对用户活跃度较高的用户做降权惩罚(公式4):

    gs4

    其中u为同时消费过j和k的用户,active表示用户u的活跃度。

itembased方法可以用于实时个性化推荐。一个场景是详情页的“相关阅读”。用公式3离线计算关联关系。如新加入的item取不到离线相关数据则可以降级使用公式2。另一个场景是实时增加用户偏爱(点击或停留时间长)的item展示,策略如下:

sstj

实时上传的用户日志构建最近消费的item集合,资讯爬虫抓取进来的新item使用公式2构建出同tag下的item关联度索引。通过数据仓库完成ALS算法给出的用户离线推荐数据集。通过最近消费的item记录和关联关系(优先取离线关联关系,当取不到时取实时关联关系)与前文叙述的item过滤器过滤后得到实时item推荐候选集合。

对于中长期的兴趣或偏好,包括周期间隔较长的行为,可以沉淀出用户、item和标签的图结构(兴趣画像)。针对这一部分,就需要对item爬取时做标签标记。当用户消费item时,用户也即被关联了相应的标签,考虑标签权重,使用行为计数和时间间隔做加权。我们在数据仓库中采用hive拉链记历史的方式存储。离线天增量更新到redis来用于线上数据消费。

算法融合及补充

目前我们采用的方法是针对不同的场景,制定不同的多个独立子策略的融合方案。或在通用的场景下根据不同子策略的性能做优先级排序,优先或多从性能较优的子策略拉取数据。针对不同的业务场景,最后制定一些补丁策略来弥补独立算法融合的一些缺陷。后续我们也计划采用训练模型(如Logistic Regression)的方式对非定投内容进行重排序优化。

使用的组件

我们采用flume,kafka,storm及spark streaming完成用户数据的实时收集、解析、初步清洗和实时统计的工作。redis,hbase,hive完成不同时效响应要求的数据存储,使用hive和spark完成数据仓库建模及计算复杂度较高的模型。azkaban完成离线任务、准实时任务的调度和任务执行出错报警功能。

小结

本文介绍了中华万年历推荐系统从无到有构建过程中的一些经验和积累。目前还有很多不完善和需要改进的地方。但在这里希望分享出来一些我们的经验,能够抛砖引玉,给大家提供到一些参考,也欢迎随时和我们一同讨论!

作者介绍

韩冰,资深数据开发工程师。作为核心人员参与了随身云大数据平台、推荐系统的设计与开发等工作。

随身云技术团队

随身云技术团队,承担了中华万年历、微历、生活日历等产品的研发和运维工作,支撑起了高达2.5亿用户量的产品体验。

北京,朝阳区,望京 http://suishen.mobi