推模式也有如下的不足:
- 大量的写操作:每一个粉丝都要写一次。
- 大量的冗余存储:每一条内容都要存储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处理数据,生成训练样本,监控产品指标。其中比较重要的是模型的参数更新,即训练模型。