流动的推荐系统——兴趣Feed技术架构与实现



推模式也有如下的不足:

  • 大量的写操作:每一个粉丝都要写一次。
  • 大量的冗余存储:每一条内容都要存储N份(受众数量)。
  • 非实时:一条内容产生后,有一定的延迟才会到达受众Feed中。


既然两者各有优劣,那么将两者结合起来呢?一种简单的结合方案是全局的:

  • 对于活跃度高的用户,使用推模式,每次他们刷新时不用等待太久,而且内容页相对多一些。
  • 对于活跃度没有那么高的用户,使用拉模式,当他们登录时才拉取最新的内容。
  • 对于热门的内容生产者,缓存其最新的N条内容,用于不同场景下拉取。


还有一种结合方案是分用户的,这是Etsy的设计方案:

  • 如果受众用户与内容产生用户之间的亲密度高,则优先推送,因为这个内容更可能被这个受众所感兴趣。
  • 如果受众用户与内容产生用户之间的亲密度低,则推迟推送或者不推送。
  • 也不是完全遵循亲密度顺序,而是采用与之相关的概率。


在中小型的社交网络上,采用纯推模式就够用了,结合的方案可以等业务发展到一定规模后再考虑。

一个推模式的Feed发布实现很简单:

  • 一个集中存储所有动态内容的数据库,一般是MySQL。
  • 为每个用户保存各自排序后的Feed,一般是Key-Value数据库,如Redis或者HBase。
  • 一个类似Pinlater的分布式异步任务队列,Celery是一个不错的选择。


按兴趣排序

兴趣Feed的排序,要避免陷入两个误区:

  • 没有目标的排序。设计排序算法之前,一定要先弄清楚:为什么要对时间序重排?希望达到什么目标?目标用哪些指标量化?只有先确定目标,才能检验和优化算法。
  • 人工量化排序因素。我们经常见到的产品或者运营的同学要求对某个因素加权、降权。这样做很不明智,因为人的知识利于做启发,不利于做量化。人可以告诉算法很多可能有用的排序因素,缩短效果提升的路径,但是人直接指定参数的权重,对效果提升来说基本上有百害而无一利。


我们从机器学习的思路来简单设计一个提升互动率的兴趣Feed。首先,定义好互动行为包括哪些,比如点赞、转发、评论和查看详情等。其次,区分好正向互动和负向互动,比如隐藏某条内容、点击不感兴趣等是负向的互动。

这是一个典型的二分类监督学习问题,将正向的互动视为同一类。一条动态产生之后,展示给用户之前,用机器学习来预测用户对产生正向互动的概率,预测的概率就可以作为兴趣排序分数输出。

能产生概率输出的二分类算法都可以用在这里,包括贝叶斯、最大熵和逻辑回归等。互联网常用的是逻辑回归,它有很多好处:

  • 线性模型,足够简单。
  • 产生0-1之间的输出,互相可以比较。
  • 开源实现多,初始技术成本小。
  • 工业界已经反复验证过。


用机器学习来为兴趣Feed排序,最重要的是将<动态,受众>这个数据对表示成特征向量。特征向量就是排序因素的向量化表述。在算法选定后,人工可以花很多力气在寻找影响排序的因素上,这就是传说中的“特征工程”。特征工程还包括对已有的特征进行选择,选择的目的是:机器学习模型完成后,以RPC的方式提供服务,供Feed系统中新动态内容发布时调用。

关于RPC框架,选用Apache Thrift即可。机器学习模型训练框架有很多,我们可以选Vowpal Wabbit,它是一个分布式机器学习框架,可以和Hadoop轻松结合。

数据和效果追踪

我们既要通过历史数据来寻找算法的最优参数,又要通过新的数据验证排序效果,所以我们要关注数据的存储和使用。

与兴趣Feed相关的数据有:

  • 目标有关的互动行为数据(记录每一个用户在Feed上的操作行为)。
  • 曝光给用户的内容(一条曝光要有唯一的ID,曝光的内容仅记录ID即可)。
  • 互动行为与曝光的映射关系(每条互动数据要对应到一条曝光数据)。
  • 用户profile(提供用户特征,来自离线挖掘和数据同步)。
  • Feed内容分析数据(提供内容特征)。


日志的收集和存储,一般选用Kafka和Hadoop即可,用Hive处理数据,生成训练样本,监控产品指标。其中比较重要的是模型的参数更新,即训练模型。